[NOTEPAD] Encoding detection (#1852)
[reactos.git] / base / applications / notepad / dialog.c
1 /*
2 * Notepad (dialog.c)
3 *
4 * Copyright 1998,99 Marcel Baur <mbaur@g26.ethz.ch>
5 * Copyright 2002 Sylvain Petreolle <spetreolle@yahoo.fr>
6 * Copyright 2002 Andriy Palamarchuk
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include "notepad.h"
24
25 #include <assert.h>
26 #include <commctrl.h>
27 #include <strsafe.h>
28
29 LRESULT CALLBACK EDIT_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
30
31 static const TCHAR helpfile[] = _T("notepad.hlp");
32 static const TCHAR empty_str[] = _T("");
33 static const TCHAR szDefaultExt[] = _T("txt");
34 static const TCHAR txt_files[] = _T("*.txt");
35
36 static UINT_PTR CALLBACK DIALOG_PAGESETUP_Hook(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
37
38 VOID ShowLastError(VOID)
39 {
40 DWORD error = GetLastError();
41 if (error != NO_ERROR)
42 {
43 LPTSTR lpMsgBuf = NULL;
44 TCHAR szTitle[MAX_STRING_LEN];
45
46 LoadString(Globals.hInstance, STRING_ERROR, szTitle, ARRAY_SIZE(szTitle));
47
48 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
49 NULL,
50 error,
51 0,
52 (LPTSTR) &lpMsgBuf,
53 0,
54 NULL);
55
56 MessageBox(NULL, lpMsgBuf, szTitle, MB_OK | MB_ICONERROR);
57 LocalFree(lpMsgBuf);
58 }
59 }
60
61 /**
62 * Sets the caption of the main window according to Globals.szFileTitle:
63 * (untitled) - Notepad if no file is open
64 * [filename] - Notepad if a file is given
65 */
66 void UpdateWindowCaption(BOOL clearModifyAlert)
67 {
68 TCHAR szCaption[MAX_STRING_LEN];
69 TCHAR szNotepad[MAX_STRING_LEN];
70 TCHAR szFilename[MAX_STRING_LEN];
71
72 /* Load the name of the application */
73 LoadString(Globals.hInstance, STRING_NOTEPAD, szNotepad, ARRAY_SIZE(szNotepad));
74
75 /* Determine if the file has been saved or if this is a new file */
76 if (Globals.szFileTitle[0] != 0)
77 StringCchCopy(szFilename, ARRAY_SIZE(szFilename), Globals.szFileTitle);
78 else
79 LoadString(Globals.hInstance, STRING_UNTITLED, szFilename, ARRAY_SIZE(szFilename));
80
81 /* When a file is being opened or created, there is no need to have the edited flag shown
82 when the new or opened file has not been edited yet */
83 if (clearModifyAlert)
84 StringCbPrintf(szCaption, ARRAY_SIZE(szCaption), _T("%s - %s"), szFilename, szNotepad);
85 else
86 {
87 BOOL isModified = (SendMessage(Globals.hEdit, EM_GETMODIFY, 0, 0) ? TRUE : FALSE);
88
89 /* Update the caption based upon if the user has modified the contents of the file or not */
90 StringCbPrintf(szCaption, ARRAY_SIZE(szCaption), _T("%s%s - %s"),
91 (isModified ? _T("*") : _T("")), szFilename, szNotepad);
92 }
93
94 /* Update the window caption */
95 SetWindowText(Globals.hMainWnd, szCaption);
96 }
97
98 int DIALOG_StringMsgBox(HWND hParent, int formatId, LPCTSTR szString, DWORD dwFlags)
99 {
100 TCHAR szMessage[MAX_STRING_LEN];
101 TCHAR szResource[MAX_STRING_LEN];
102
103 /* Load and format szMessage */
104 LoadString(Globals.hInstance, formatId, szResource, ARRAY_SIZE(szResource));
105 _sntprintf(szMessage, ARRAY_SIZE(szMessage), szResource, szString);
106
107 /* Load szCaption */
108 if ((dwFlags & MB_ICONMASK) == MB_ICONEXCLAMATION)
109 LoadString(Globals.hInstance, STRING_ERROR, szResource, ARRAY_SIZE(szResource));
110 else
111 LoadString(Globals.hInstance, STRING_NOTEPAD, szResource, ARRAY_SIZE(szResource));
112
113 /* Display Modal Dialog */
114 // if (hParent == NULL)
115 // hParent = Globals.hMainWnd;
116 return MessageBox(hParent, szMessage, szResource, dwFlags);
117 }
118
119 static void AlertFileNotFound(LPCTSTR szFileName)
120 {
121 DIALOG_StringMsgBox(Globals.hMainWnd, STRING_NOTFOUND, szFileName, MB_ICONEXCLAMATION | MB_OK);
122 }
123
124 static int AlertFileNotSaved(LPCTSTR szFileName)
125 {
126 TCHAR szUntitled[MAX_STRING_LEN];
127
128 LoadString(Globals.hInstance, STRING_UNTITLED, szUntitled, ARRAY_SIZE(szUntitled));
129
130 return DIALOG_StringMsgBox(Globals.hMainWnd, STRING_NOTSAVED,
131 szFileName[0] ? szFileName : szUntitled,
132 MB_ICONQUESTION | MB_YESNOCANCEL);
133 }
134
135 static void AlertPrintError(void)
136 {
137 TCHAR szUntitled[MAX_STRING_LEN];
138
139 LoadString(Globals.hInstance, STRING_UNTITLED, szUntitled, ARRAY_SIZE(szUntitled));
140
141 DIALOG_StringMsgBox(Globals.hMainWnd, STRING_PRINTERROR,
142 Globals.szFileName[0] ? Globals.szFileName : szUntitled,
143 MB_ICONEXCLAMATION | MB_OK);
144 }
145
146 /**
147 * Returns:
148 * TRUE - if file exists
149 * FALSE - if file does not exist
150 */
151 BOOL FileExists(LPCTSTR szFilename)
152 {
153 WIN32_FIND_DATA entry;
154 HANDLE hFile;
155
156 hFile = FindFirstFile(szFilename, &entry);
157 FindClose(hFile);
158
159 return (hFile != INVALID_HANDLE_VALUE);
160 }
161
162 BOOL HasFileExtension(LPCTSTR szFilename)
163 {
164 LPCTSTR s;
165
166 s = _tcsrchr(szFilename, _T('\\'));
167 if (s)
168 szFilename = s;
169 return _tcsrchr(szFilename, _T('.')) != NULL;
170 }
171
172 int GetSelectionTextLength(HWND hWnd)
173 {
174 DWORD dwStart = 0;
175 DWORD dwEnd = 0;
176
177 SendMessage(hWnd, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);
178
179 return dwEnd - dwStart;
180 }
181
182 int GetSelectionText(HWND hWnd, LPTSTR lpString, int nMaxCount)
183 {
184 DWORD dwStart = 0;
185 DWORD dwEnd = 0;
186 DWORD dwSize;
187 HRESULT hResult;
188 LPTSTR lpTemp;
189
190 if (!lpString)
191 {
192 return 0;
193 }
194
195 SendMessage(hWnd, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);
196
197 if (dwStart == dwEnd)
198 {
199 return 0;
200 }
201
202 dwSize = GetWindowTextLength(hWnd) + 1;
203 lpTemp = HeapAlloc(GetProcessHeap(), 0, dwSize * sizeof(TCHAR));
204 if (!lpTemp)
205 {
206 return 0;
207 }
208
209 dwSize = GetWindowText(hWnd, lpTemp, dwSize);
210
211 if (!dwSize)
212 {
213 HeapFree(GetProcessHeap(), 0, lpTemp);
214 return 0;
215 }
216
217 hResult = StringCchCopyN(lpString, nMaxCount, lpTemp + dwStart, dwEnd - dwStart);
218 HeapFree(GetProcessHeap(), 0, lpTemp);
219
220 switch (hResult)
221 {
222 case S_OK:
223 {
224 return dwEnd - dwStart;
225 }
226
227 case STRSAFE_E_INSUFFICIENT_BUFFER:
228 {
229 return nMaxCount - 1;
230 }
231
232 default:
233 {
234 return 0;
235 }
236 }
237 }
238
239 static RECT
240 GetPrintingRect(HDC hdc, RECT margins)
241 {
242 int iLogPixelsX, iLogPixelsY;
243 int iHorzRes, iVertRes;
244 int iPhysPageX, iPhysPageY, iPhysPageW, iPhysPageH;
245 RECT rcPrintRect;
246
247 iPhysPageX = GetDeviceCaps(hdc, PHYSICALOFFSETX);
248 iPhysPageY = GetDeviceCaps(hdc, PHYSICALOFFSETY);
249 iPhysPageW = GetDeviceCaps(hdc, PHYSICALWIDTH);
250 iPhysPageH = GetDeviceCaps(hdc, PHYSICALHEIGHT);
251 iLogPixelsX = GetDeviceCaps(hdc, LOGPIXELSX);
252 iLogPixelsY = GetDeviceCaps(hdc, LOGPIXELSY);
253 iHorzRes = GetDeviceCaps(hdc, HORZRES);
254 iVertRes = GetDeviceCaps(hdc, VERTRES);
255
256 rcPrintRect.left = (margins.left * iLogPixelsX / 2540) - iPhysPageX;
257 rcPrintRect.top = (margins.top * iLogPixelsY / 2540) - iPhysPageY;
258 rcPrintRect.right = iHorzRes - (((margins.left * iLogPixelsX / 2540) - iPhysPageX) + ((margins.right * iLogPixelsX / 2540) - (iPhysPageW - iPhysPageX - iHorzRes)));
259 rcPrintRect.bottom = iVertRes - (((margins.top * iLogPixelsY / 2540) - iPhysPageY) + ((margins.bottom * iLogPixelsY / 2540) - (iPhysPageH - iPhysPageY - iVertRes)));
260
261 return rcPrintRect;
262 }
263
264 static BOOL DoSaveFile(VOID)
265 {
266 BOOL bRet = TRUE;
267 HANDLE hFile;
268 LPTSTR pTemp;
269 DWORD size;
270
271 hFile = CreateFile(Globals.szFileName, GENERIC_WRITE, FILE_SHARE_WRITE,
272 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
273 if(hFile == INVALID_HANDLE_VALUE)
274 {
275 ShowLastError();
276 return FALSE;
277 }
278
279 size = GetWindowTextLength(Globals.hEdit) + 1;
280 pTemp = HeapAlloc(GetProcessHeap(), 0, size * sizeof(*pTemp));
281 if (!pTemp)
282 {
283 CloseHandle(hFile);
284 ShowLastError();
285 return FALSE;
286 }
287 size = GetWindowText(Globals.hEdit, pTemp, size);
288
289 if (size)
290 {
291 if (!WriteText(hFile, (LPWSTR)pTemp, size, Globals.encFile, Globals.iEoln))
292 {
293 ShowLastError();
294 bRet = FALSE;
295 }
296 else
297 {
298 SendMessage(Globals.hEdit, EM_SETMODIFY, FALSE, 0);
299 bRet = TRUE;
300 }
301 }
302
303 CloseHandle(hFile);
304 HeapFree(GetProcessHeap(), 0, pTemp);
305 return bRet;
306 }
307
308 /**
309 * Returns:
310 * TRUE - User agreed to close (both save/don't save)
311 * FALSE - User cancelled close by selecting "Cancel"
312 */
313 BOOL DoCloseFile(VOID)
314 {
315 int nResult;
316
317 if (SendMessage(Globals.hEdit, EM_GETMODIFY, 0, 0))
318 {
319 /* prompt user to save changes */
320 nResult = AlertFileNotSaved(Globals.szFileName);
321 switch (nResult)
322 {
323 case IDYES:
324 if(!DIALOG_FileSave())
325 return FALSE;
326 break;
327
328 case IDNO:
329 break;
330
331 case IDCANCEL:
332 return FALSE;
333
334 default:
335 return FALSE;
336 }
337 }
338
339 SetFileName(empty_str);
340 UpdateWindowCaption(TRUE);
341
342 return TRUE;
343 }
344
345 VOID DoOpenFile(LPCTSTR szFileName)
346 {
347 static const TCHAR dotlog[] = _T(".LOG");
348 HANDLE hFile;
349 LPTSTR pszText = NULL;
350 DWORD dwTextLen;
351 TCHAR log[5];
352
353 /* Close any files and prompt to save changes */
354 if (!DoCloseFile())
355 return;
356
357 hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
358 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
359 if (hFile == INVALID_HANDLE_VALUE)
360 {
361 ShowLastError();
362 goto done;
363 }
364
365 if (!ReadText(hFile, (LPWSTR *)&pszText, &dwTextLen, &Globals.encFile, &Globals.iEoln))
366 {
367 ShowLastError();
368 goto done;
369 }
370 SetWindowText(Globals.hEdit, pszText);
371
372 SendMessage(Globals.hEdit, EM_SETMODIFY, FALSE, 0);
373 SendMessage(Globals.hEdit, EM_EMPTYUNDOBUFFER, 0, 0);
374 SetFocus(Globals.hEdit);
375
376 /* If the file starts with .LOG, add a time/date at the end and set cursor after
377 * See http://support.microsoft.com/?kbid=260563
378 */
379 if (GetWindowText(Globals.hEdit, log, ARRAY_SIZE(log)) && !_tcscmp(log, dotlog))
380 {
381 static const TCHAR lf[] = _T("\r\n");
382 SendMessage(Globals.hEdit, EM_SETSEL, GetWindowTextLength(Globals.hEdit), -1);
383 SendMessage(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)lf);
384 DIALOG_EditTimeDate();
385 SendMessage(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)lf);
386 }
387
388 SetFileName(szFileName);
389 UpdateWindowCaption(TRUE);
390 NOTEPAD_EnableSearchMenu();
391 done:
392 if (hFile != INVALID_HANDLE_VALUE)
393 CloseHandle(hFile);
394 if (pszText)
395 HeapFree(GetProcessHeap(), 0, pszText);
396 }
397
398 VOID DIALOG_FileNew(VOID)
399 {
400 /* Close any files and prompt to save changes */
401 if (DoCloseFile()) {
402 SetWindowText(Globals.hEdit, empty_str);
403 SendMessage(Globals.hEdit, EM_EMPTYUNDOBUFFER, 0, 0);
404 SetFocus(Globals.hEdit);
405 NOTEPAD_EnableSearchMenu();
406 }
407 }
408
409 VOID DIALOG_FileOpen(VOID)
410 {
411 OPENFILENAME openfilename;
412 TCHAR szPath[MAX_PATH];
413
414 ZeroMemory(&openfilename, sizeof(openfilename));
415
416 if (Globals.szFileName[0] == 0)
417 _tcscpy(szPath, txt_files);
418 else
419 _tcscpy(szPath, Globals.szFileName);
420
421 openfilename.lStructSize = sizeof(openfilename);
422 openfilename.hwndOwner = Globals.hMainWnd;
423 openfilename.hInstance = Globals.hInstance;
424 openfilename.lpstrFilter = Globals.szFilter;
425 openfilename.lpstrFile = szPath;
426 openfilename.nMaxFile = ARRAY_SIZE(szPath);
427 openfilename.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
428 openfilename.lpstrDefExt = szDefaultExt;
429
430 if (GetOpenFileName(&openfilename)) {
431 if (FileExists(openfilename.lpstrFile))
432 DoOpenFile(openfilename.lpstrFile);
433 else
434 AlertFileNotFound(openfilename.lpstrFile);
435 }
436 }
437
438 BOOL DIALOG_FileSave(VOID)
439 {
440 if (Globals.szFileName[0] == 0)
441 {
442 return DIALOG_FileSaveAs();
443 }
444 else if (DoSaveFile())
445 {
446 UpdateWindowCaption(TRUE);
447 return TRUE;
448 }
449 return FALSE;
450 }
451
452 static UINT_PTR
453 CALLBACK
454 DIALOG_FileSaveAs_Hook(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
455 {
456 TCHAR szText[128];
457 HWND hCombo;
458
459 UNREFERENCED_PARAMETER(wParam);
460
461 switch(msg)
462 {
463 case WM_INITDIALOG:
464 hCombo = GetDlgItem(hDlg, ID_ENCODING);
465
466 LoadString(Globals.hInstance, STRING_ANSI, szText, ARRAY_SIZE(szText));
467 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
468
469 LoadString(Globals.hInstance, STRING_UNICODE, szText, ARRAY_SIZE(szText));
470 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
471
472 LoadString(Globals.hInstance, STRING_UNICODE_BE, szText, ARRAY_SIZE(szText));
473 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
474
475 LoadString(Globals.hInstance, STRING_UTF8, szText, ARRAY_SIZE(szText));
476 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
477
478 SendMessage(hCombo, CB_SETCURSEL, Globals.encFile, 0);
479
480 hCombo = GetDlgItem(hDlg, ID_EOLN);
481
482 LoadString(Globals.hInstance, STRING_CRLF, szText, ARRAY_SIZE(szText));
483 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
484
485 LoadString(Globals.hInstance, STRING_LF, szText, ARRAY_SIZE(szText));
486 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
487
488 LoadString(Globals.hInstance, STRING_CR, szText, ARRAY_SIZE(szText));
489 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
490
491 SendMessage(hCombo, CB_SETCURSEL, Globals.iEoln, 0);
492 break;
493
494 case WM_NOTIFY:
495 if (((NMHDR *) lParam)->code == CDN_FILEOK)
496 {
497 hCombo = GetDlgItem(hDlg, ID_ENCODING);
498 if (hCombo)
499 Globals.encFile = (ENCODING) SendMessage(hCombo, CB_GETCURSEL, 0, 0);
500
501 hCombo = GetDlgItem(hDlg, ID_EOLN);
502 if (hCombo)
503 Globals.iEoln = (int) SendMessage(hCombo, CB_GETCURSEL, 0, 0);
504 }
505 break;
506 }
507 return 0;
508 }
509
510 BOOL DIALOG_FileSaveAs(VOID)
511 {
512 OPENFILENAME saveas;
513 TCHAR szPath[MAX_PATH];
514
515 ZeroMemory(&saveas, sizeof(saveas));
516
517 if (Globals.szFileName[0] == 0)
518 _tcscpy(szPath, txt_files);
519 else
520 _tcscpy(szPath, Globals.szFileName);
521
522 saveas.lStructSize = sizeof(OPENFILENAME);
523 saveas.hwndOwner = Globals.hMainWnd;
524 saveas.hInstance = Globals.hInstance;
525 saveas.lpstrFilter = Globals.szFilter;
526 saveas.lpstrFile = szPath;
527 saveas.nMaxFile = ARRAY_SIZE(szPath);
528 saveas.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY |
529 OFN_EXPLORER | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK;
530 saveas.lpstrDefExt = szDefaultExt;
531 saveas.lpTemplateName = MAKEINTRESOURCE(DIALOG_ENCODING);
532 saveas.lpfnHook = DIALOG_FileSaveAs_Hook;
533
534 if (GetSaveFileName(&saveas))
535 {
536 /* HACK: Because in ROS, Save-As boxes don't check the validity
537 * of file names and thus, here, szPath can be invalid !! We only
538 * see its validity when we call DoSaveFile()... */
539 SetFileName(szPath);
540 if (DoSaveFile())
541 {
542 UpdateWindowCaption(TRUE);
543 return TRUE;
544 }
545 else
546 {
547 SetFileName(_T(""));
548 return FALSE;
549 }
550 }
551 else
552 {
553 return FALSE;
554 }
555 }
556
557 VOID DIALOG_FilePrint(VOID)
558 {
559 DOCINFO di;
560 TEXTMETRIC tm;
561 PRINTDLG printer;
562 SIZE szMetric;
563 int border;
564 int xLeft, yTop, pagecount, dopage, copycount;
565 unsigned int i;
566 LOGFONT hdrFont;
567 HFONT font, old_font=0;
568 DWORD size;
569 LPTSTR pTemp;
570 static const TCHAR times_new_roman[] = _T("Times New Roman");
571 RECT rcPrintRect;
572
573 /* Get a small font and print some header info on each page */
574 ZeroMemory(&hdrFont, sizeof(hdrFont));
575 hdrFont.lfHeight = 100;
576 hdrFont.lfWeight = FW_BOLD;
577 hdrFont.lfCharSet = ANSI_CHARSET;
578 hdrFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
579 hdrFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
580 hdrFont.lfQuality = PROOF_QUALITY;
581 hdrFont.lfPitchAndFamily = VARIABLE_PITCH | FF_ROMAN;
582 _tcscpy(hdrFont.lfFaceName, times_new_roman);
583
584 font = CreateFontIndirect(&hdrFont);
585
586 /* Get Current Settings */
587 ZeroMemory(&printer, sizeof(printer));
588 printer.lStructSize = sizeof(printer);
589 printer.hwndOwner = Globals.hMainWnd;
590 printer.hInstance = Globals.hInstance;
591
592 /* Set some default flags */
593 printer.Flags = PD_RETURNDC | PD_SELECTION;
594
595 /* Disable the selection radio button if there is no text selected */
596 if (!GetSelectionTextLength(Globals.hEdit))
597 {
598 printer.Flags = printer.Flags | PD_NOSELECTION;
599 }
600
601 printer.nFromPage = 0;
602 printer.nMinPage = 1;
603 /* we really need to calculate number of pages to set nMaxPage and nToPage */
604 printer.nToPage = (WORD)-1;
605 printer.nMaxPage = (WORD)-1;
606
607 /* Let commdlg manage copy settings */
608 printer.nCopies = (WORD)PD_USEDEVMODECOPIES;
609
610 printer.hDevMode = Globals.hDevMode;
611 printer.hDevNames = Globals.hDevNames;
612
613 if (!PrintDlg(&printer))
614 {
615 DeleteObject(font);
616 return;
617 }
618
619 Globals.hDevMode = printer.hDevMode;
620 Globals.hDevNames = printer.hDevNames;
621
622 assert(printer.hDC != 0);
623
624 /* initialize DOCINFO */
625 di.cbSize = sizeof(DOCINFO);
626 di.lpszDocName = Globals.szFileTitle;
627 di.lpszOutput = NULL;
628 di.lpszDatatype = NULL;
629 di.fwType = 0;
630
631 if (StartDoc(printer.hDC, &di) <= 0)
632 {
633 DeleteObject(font);
634 return;
635 }
636
637
638 /* Get the file text */
639 if (printer.Flags & PD_SELECTION)
640 {
641 size = GetSelectionTextLength(Globals.hEdit) + 1;
642 }
643 else
644 {
645 size = GetWindowTextLength(Globals.hEdit) + 1;
646 }
647
648 pTemp = HeapAlloc(GetProcessHeap(), 0, size * sizeof(TCHAR));
649 if (!pTemp)
650 {
651 EndDoc(printer.hDC);
652 DeleteObject(font);
653 ShowLastError();
654 return;
655 }
656
657 if (printer.Flags & PD_SELECTION)
658 {
659 size = GetSelectionText(Globals.hEdit, pTemp, size);
660 }
661 else
662 {
663 size = GetWindowText(Globals.hEdit, pTemp, size);
664 }
665
666 /* Get the current printing area */
667 rcPrintRect = GetPrintingRect(printer.hDC, Globals.lMargins);
668
669 /* Ensure that each logical unit maps to one pixel */
670 SetMapMode(printer.hDC, MM_TEXT);
671
672 /* Needed to get the correct height of a text line */
673 GetTextMetrics(printer.hDC, &tm);
674
675 border = 15;
676 for (copycount=1; copycount <= printer.nCopies; copycount++) {
677 i = 0;
678 pagecount = 1;
679 do {
680 /* Don't start a page if none of the conditions below are true */
681 dopage = 0;
682
683 /* The user wants to print the current selection */
684 if (printer.Flags & PD_SELECTION)
685 {
686 dopage = 1;
687 }
688
689 /* The user wants to print the entire document */
690 if (!(printer.Flags & PD_PAGENUMS) && !(printer.Flags & PD_SELECTION))
691 {
692 dopage = 1;
693 }
694
695 /* The user wants to print a specified range of pages */
696 if ((pagecount >= printer.nFromPage && pagecount <= printer.nToPage))
697 {
698 dopage = 1;
699 }
700
701 old_font = SelectObject(printer.hDC, font);
702
703 if (dopage) {
704 if (StartPage(printer.hDC) <= 0) {
705 SelectObject(printer.hDC, old_font);
706 EndDoc(printer.hDC);
707 DeleteDC(printer.hDC);
708 HeapFree(GetProcessHeap(), 0, pTemp);
709 DeleteObject(font);
710 AlertPrintError();
711 return;
712 }
713
714 SetViewportOrgEx(printer.hDC, rcPrintRect.left, rcPrintRect.top, NULL);
715
716 /* Write a rectangle and header at the top of each page */
717 Rectangle(printer.hDC, border, border, rcPrintRect.right - border, border + tm.tmHeight * 2);
718 /* I don't know what's up with this TextOut command. This comes out
719 kind of mangled.
720 */
721 TextOut(printer.hDC,
722 border * 2,
723 border + tm.tmHeight / 2,
724 Globals.szFileTitle,
725 lstrlen(Globals.szFileTitle));
726 }
727
728 /* The starting point for the main text */
729 xLeft = 0;
730 yTop = border + tm.tmHeight * 4;
731
732 SelectObject(printer.hDC, old_font);
733
734 /* Since outputting strings is giving me problems, output the main
735 * text one character at a time. */
736 do {
737 if (pTemp[i] == '\n') {
738 xLeft = 0;
739 yTop += tm.tmHeight;
740 }
741 else if (pTemp[i] != '\r') {
742 if (dopage)
743 TextOut(printer.hDC, xLeft, yTop, &pTemp[i], 1);
744
745 /* We need to get the width for each individual char, since a proportional font may be used */
746 GetTextExtentPoint32(printer.hDC, &pTemp[i], 1, &szMetric);
747 xLeft += szMetric.cx;
748
749 /* Insert a line break if the current line does not fit into the printing area */
750 if (xLeft > rcPrintRect.right)
751 {
752 xLeft = 0;
753 yTop = yTop + tm.tmHeight;
754 }
755 }
756 } while (i++ < size && yTop < rcPrintRect.bottom);
757
758 if (dopage)
759 EndPage(printer.hDC);
760 pagecount++;
761 } while (i < size);
762 }
763
764 if (old_font != 0)
765 SelectObject(printer.hDC, old_font);
766 EndDoc(printer.hDC);
767 DeleteDC(printer.hDC);
768 HeapFree(GetProcessHeap(), 0, pTemp);
769 DeleteObject(font);
770 }
771
772 VOID DIALOG_FileExit(VOID)
773 {
774 PostMessage(Globals.hMainWnd, WM_CLOSE, 0, 0l);
775 }
776
777 VOID DIALOG_EditUndo(VOID)
778 {
779 SendMessage(Globals.hEdit, EM_UNDO, 0, 0);
780 }
781
782 VOID DIALOG_EditCut(VOID)
783 {
784 SendMessage(Globals.hEdit, WM_CUT, 0, 0);
785 }
786
787 VOID DIALOG_EditCopy(VOID)
788 {
789 SendMessage(Globals.hEdit, WM_COPY, 0, 0);
790 }
791
792 VOID DIALOG_EditPaste(VOID)
793 {
794 SendMessage(Globals.hEdit, WM_PASTE, 0, 0);
795 }
796
797 VOID DIALOG_EditDelete(VOID)
798 {
799 SendMessage(Globals.hEdit, WM_CLEAR, 0, 0);
800 }
801
802 VOID DIALOG_EditSelectAll(VOID)
803 {
804 SendMessage(Globals.hEdit, EM_SETSEL, 0, (LPARAM)-1);
805 }
806
807 VOID DIALOG_EditTimeDate(VOID)
808 {
809 SYSTEMTIME st;
810 TCHAR szDate[MAX_STRING_LEN];
811 TCHAR szText[MAX_STRING_LEN * 2 + 2];
812
813 GetLocalTime(&st);
814
815 GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, szDate, MAX_STRING_LEN);
816 _tcscpy(szText, szDate);
817 _tcscat(szText, _T(" "));
818 GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, szDate, MAX_STRING_LEN);
819 _tcscat(szText, szDate);
820 SendMessage(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)szText);
821 }
822
823 VOID DoCreateStatusBar(VOID)
824 {
825 RECT rc;
826 RECT rcstatus;
827 BOOL bStatusBarVisible;
828
829 /* Check if status bar object already exists. */
830 if (Globals.hStatusBar == NULL)
831 {
832 /* Try to create the status bar */
833 Globals.hStatusBar = CreateStatusWindow(WS_CHILD | WS_VISIBLE | WS_EX_STATICEDGE,
834 NULL,
835 Globals.hMainWnd,
836 CMD_STATUSBAR_WND_ID);
837
838 if (Globals.hStatusBar == NULL)
839 {
840 ShowLastError();
841 return;
842 }
843
844 /* Load the string for formatting column/row text output */
845 LoadString(Globals.hInstance, STRING_LINE_COLUMN, Globals.szStatusBarLineCol, MAX_PATH - 1);
846
847 /* Set the status bar for single-text output */
848 SendMessage(Globals.hStatusBar, SB_SIMPLE, (WPARAM)TRUE, (LPARAM)0);
849 }
850
851 /* Set status bar visiblity according to the settings. */
852 if ((Globals.bWrapLongLines != FALSE) || (Globals.bShowStatusBar == FALSE))
853 {
854 bStatusBarVisible = FALSE;
855 ShowWindow(Globals.hStatusBar, SW_HIDE);
856 }
857 else
858 {
859 bStatusBarVisible = TRUE;
860 ShowWindow(Globals.hStatusBar, SW_SHOW);
861 SendMessage(Globals.hStatusBar, WM_SIZE, 0, 0);
862 }
863
864 /* Set check state in show status bar item. */
865 if (bStatusBarVisible)
866 {
867 CheckMenuItem(Globals.hMenu, CMD_STATUSBAR, MF_BYCOMMAND | MF_CHECKED);
868 }
869 else
870 {
871 CheckMenuItem(Globals.hMenu, CMD_STATUSBAR, MF_BYCOMMAND | MF_UNCHECKED);
872 }
873
874 /* Update menu mar with the previous changes */
875 DrawMenuBar(Globals.hMainWnd);
876
877 /* Sefety test is edit control exists */
878 if (Globals.hEdit != NULL)
879 {
880 /* Retrieve the sizes of the controls */
881 GetClientRect(Globals.hMainWnd, &rc);
882 GetClientRect(Globals.hStatusBar, &rcstatus);
883
884 /* If status bar is currently visible, update dimensions of edit control */
885 if (bStatusBarVisible)
886 rc.bottom -= (rcstatus.bottom - rcstatus.top);
887
888 /* Resize edit control to right size. */
889 MoveWindow(Globals.hEdit,
890 rc.left,
891 rc.top,
892 rc.right - rc.left,
893 rc.bottom - rc.top,
894 TRUE);
895 }
896
897 /* Update content with current row/column text */
898 DIALOG_StatusBarUpdateCaretPos();
899 }
900
901 VOID DoCreateEditWindow(VOID)
902 {
903 DWORD dwStyle;
904 int iSize;
905 LPTSTR pTemp = NULL;
906 BOOL bModified = FALSE;
907
908 iSize = 0;
909
910 /* If the edit control already exists, try to save its content */
911 if (Globals.hEdit != NULL)
912 {
913 /* number of chars currently written into the editor. */
914 iSize = GetWindowTextLength(Globals.hEdit);
915 if (iSize)
916 {
917 /* Allocates temporary buffer. */
918 pTemp = HeapAlloc(GetProcessHeap(), 0, (iSize + 1) * sizeof(TCHAR));
919 if (!pTemp)
920 {
921 ShowLastError();
922 return;
923 }
924
925 /* Recover the text into the control. */
926 GetWindowText(Globals.hEdit, pTemp, iSize + 1);
927
928 if (SendMessage(Globals.hEdit, EM_GETMODIFY, 0, 0))
929 bModified = TRUE;
930 }
931
932 /* Restore original window procedure */
933 SetWindowLongPtr(Globals.hEdit, GWLP_WNDPROC, (LONG_PTR)Globals.EditProc);
934
935 /* Destroy the edit control */
936 DestroyWindow(Globals.hEdit);
937 }
938
939 /* Update wrap status into the main menu and recover style flags */
940 if (Globals.bWrapLongLines)
941 {
942 dwStyle = EDIT_STYLE_WRAP;
943 EnableMenuItem(Globals.hMenu, CMD_STATUSBAR, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
944 } else {
945 dwStyle = EDIT_STYLE;
946 EnableMenuItem(Globals.hMenu, CMD_STATUSBAR, MF_BYCOMMAND | MF_ENABLED);
947 }
948
949 /* Update previous changes */
950 DrawMenuBar(Globals.hMainWnd);
951
952 /* Create the new edit control */
953 Globals.hEdit = CreateWindowEx(WS_EX_CLIENTEDGE,
954 EDIT_CLASS,
955 NULL,
956 dwStyle,
957 CW_USEDEFAULT,
958 CW_USEDEFAULT,
959 CW_USEDEFAULT,
960 CW_USEDEFAULT,
961 Globals.hMainWnd,
962 NULL,
963 Globals.hInstance,
964 NULL);
965
966 if (Globals.hEdit == NULL)
967 {
968 if (pTemp)
969 {
970 HeapFree(GetProcessHeap(), 0, pTemp);
971 }
972
973 ShowLastError();
974 return;
975 }
976
977 SendMessage(Globals.hEdit, WM_SETFONT, (WPARAM)Globals.hFont, FALSE);
978 SendMessage(Globals.hEdit, EM_LIMITTEXT, 0, 0);
979
980 /* If some text was previously saved, restore it. */
981 if (iSize != 0)
982 {
983 SetWindowText(Globals.hEdit, pTemp);
984 HeapFree(GetProcessHeap(), 0, pTemp);
985
986 if (bModified)
987 SendMessage(Globals.hEdit, EM_SETMODIFY, TRUE, 0);
988 }
989
990 /* Sub-class a new window callback for row/column detection. */
991 Globals.EditProc = (WNDPROC)SetWindowLongPtr(Globals.hEdit,
992 GWLP_WNDPROC,
993 (LONG_PTR)EDIT_WndProc);
994
995 /* Create/update status bar */
996 DoCreateStatusBar();
997
998 /* Finally shows new edit control and set focus into it. */
999 ShowWindow(Globals.hEdit, SW_SHOW);
1000 SetFocus(Globals.hEdit);
1001 }
1002
1003 VOID DIALOG_EditWrap(VOID)
1004 {
1005 Globals.bWrapLongLines = !Globals.bWrapLongLines;
1006
1007 if (Globals.bWrapLongLines)
1008 {
1009 EnableMenuItem(Globals.hMenu, CMD_GOTO, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1010 }
1011 else
1012 {
1013 EnableMenuItem(Globals.hMenu, CMD_GOTO, MF_BYCOMMAND | MF_ENABLED);
1014 }
1015
1016 DoCreateEditWindow();
1017 }
1018
1019 VOID DIALOG_SelectFont(VOID)
1020 {
1021 CHOOSEFONT cf;
1022 LOGFONT lf = Globals.lfFont;
1023
1024 ZeroMemory( &cf, sizeof(cf) );
1025 cf.lStructSize = sizeof(cf);
1026 cf.hwndOwner = Globals.hMainWnd;
1027 cf.lpLogFont = &lf;
1028 cf.Flags = CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT | CF_NOVERTFONTS;
1029
1030 if (ChooseFont(&cf))
1031 {
1032 HFONT currfont = Globals.hFont;
1033
1034 Globals.hFont = CreateFontIndirect(&lf);
1035 Globals.lfFont = lf;
1036 SendMessage(Globals.hEdit, WM_SETFONT, (WPARAM)Globals.hFont, (LPARAM)TRUE);
1037 if (currfont != NULL)
1038 DeleteObject(currfont);
1039 }
1040 }
1041
1042 typedef HWND (WINAPI *FINDPROC)(LPFINDREPLACE lpfr);
1043
1044 static VOID DIALOG_SearchDialog(FINDPROC pfnProc)
1045 {
1046 if (Globals.hFindReplaceDlg != NULL)
1047 {
1048 SetFocus(Globals.hFindReplaceDlg);
1049 return;
1050 }
1051
1052 ZeroMemory(&Globals.find, sizeof(Globals.find));
1053 Globals.find.lStructSize = sizeof(Globals.find);
1054 Globals.find.hwndOwner = Globals.hMainWnd;
1055 Globals.find.hInstance = Globals.hInstance;
1056 Globals.find.lpstrFindWhat = Globals.szFindText;
1057 Globals.find.wFindWhatLen = ARRAY_SIZE(Globals.szFindText);
1058 Globals.find.lpstrReplaceWith = Globals.szReplaceText;
1059 Globals.find.wReplaceWithLen = ARRAY_SIZE(Globals.szReplaceText);
1060 Globals.find.Flags = FR_DOWN;
1061
1062 /* We only need to create the modal FindReplace dialog which will */
1063 /* notify us of incoming events using hMainWnd Window Messages */
1064
1065 Globals.hFindReplaceDlg = pfnProc(&Globals.find);
1066 assert(Globals.hFindReplaceDlg != NULL);
1067 }
1068
1069 VOID DIALOG_Search(VOID)
1070 {
1071 DIALOG_SearchDialog(FindText);
1072 }
1073
1074 VOID DIALOG_SearchNext(VOID)
1075 {
1076 if (Globals.find.lpstrFindWhat != NULL)
1077 NOTEPAD_FindNext(&Globals.find, FALSE, TRUE);
1078 else
1079 DIALOG_Search();
1080 }
1081
1082 VOID DIALOG_Replace(VOID)
1083 {
1084 DIALOG_SearchDialog(ReplaceText);
1085 }
1086
1087 static INT_PTR
1088 CALLBACK
1089 DIALOG_GoTo_DialogProc(HWND hwndDialog, UINT uMsg, WPARAM wParam, LPARAM lParam)
1090 {
1091 BOOL bResult = FALSE;
1092 HWND hTextBox;
1093 TCHAR szText[32];
1094
1095 switch(uMsg) {
1096 case WM_INITDIALOG:
1097 hTextBox = GetDlgItem(hwndDialog, ID_LINENUMBER);
1098 _sntprintf(szText, ARRAY_SIZE(szText), _T("%Id"), lParam);
1099 SetWindowText(hTextBox, szText);
1100 break;
1101 case WM_COMMAND:
1102 if (HIWORD(wParam) == BN_CLICKED)
1103 {
1104 if (LOWORD(wParam) == IDOK)
1105 {
1106 hTextBox = GetDlgItem(hwndDialog, ID_LINENUMBER);
1107 GetWindowText(hTextBox, szText, ARRAY_SIZE(szText));
1108 EndDialog(hwndDialog, _ttoi(szText));
1109 bResult = TRUE;
1110 }
1111 else if (LOWORD(wParam) == IDCANCEL)
1112 {
1113 EndDialog(hwndDialog, 0);
1114 bResult = TRUE;
1115 }
1116 }
1117 break;
1118 }
1119
1120 return bResult;
1121 }
1122
1123 VOID DIALOG_GoTo(VOID)
1124 {
1125 INT_PTR nLine;
1126 LPTSTR pszText;
1127 int nLength, i;
1128 DWORD dwStart, dwEnd;
1129
1130 nLength = GetWindowTextLength(Globals.hEdit);
1131 pszText = (LPTSTR) HeapAlloc(GetProcessHeap(), 0, (nLength + 1) * sizeof(*pszText));
1132 if (!pszText)
1133 return;
1134
1135 /* Retrieve current text */
1136 GetWindowText(Globals.hEdit, pszText, nLength + 1);
1137 SendMessage(Globals.hEdit, EM_GETSEL, (WPARAM) &dwStart, (LPARAM) &dwEnd);
1138
1139 nLine = 1;
1140 for (i = 0; (i < (int) dwStart) && pszText[i]; i++)
1141 {
1142 if (pszText[i] == '\n')
1143 nLine++;
1144 }
1145
1146 nLine = DialogBoxParam(Globals.hInstance,
1147 MAKEINTRESOURCE(DIALOG_GOTO),
1148 Globals.hMainWnd,
1149 DIALOG_GoTo_DialogProc,
1150 nLine);
1151
1152 if (nLine >= 1)
1153 {
1154 for (i = 0; pszText[i] && (nLine > 1) && (i < nLength - 1); i++)
1155 {
1156 if (pszText[i] == '\n')
1157 nLine--;
1158 }
1159 SendMessage(Globals.hEdit, EM_SETSEL, i, i);
1160 SendMessage(Globals.hEdit, EM_SCROLLCARET, 0, 0);
1161 }
1162 HeapFree(GetProcessHeap(), 0, pszText);
1163 }
1164
1165 VOID DIALOG_StatusBarUpdateCaretPos(VOID)
1166 {
1167 int line, col;
1168 TCHAR buff[MAX_PATH];
1169 DWORD dwStart, dwSize;
1170
1171 SendMessage(Globals.hEdit, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwSize);
1172 line = SendMessage(Globals.hEdit, EM_LINEFROMCHAR, (WPARAM)dwStart, 0);
1173 col = dwStart - SendMessage(Globals.hEdit, EM_LINEINDEX, (WPARAM)line, 0);
1174
1175 _stprintf(buff, Globals.szStatusBarLineCol, line + 1, col + 1);
1176 SendMessage(Globals.hStatusBar, SB_SETTEXT, SB_SIMPLEID, (LPARAM)buff);
1177 }
1178
1179 VOID DIALOG_ViewStatusBar(VOID)
1180 {
1181 Globals.bShowStatusBar = !Globals.bShowStatusBar;
1182
1183 DoCreateStatusBar();
1184 }
1185
1186 VOID DIALOG_HelpContents(VOID)
1187 {
1188 WinHelp(Globals.hMainWnd, helpfile, HELP_INDEX, 0);
1189 }
1190
1191 VOID DIALOG_HelpAboutNotepad(VOID)
1192 {
1193 TCHAR szNotepad[MAX_STRING_LEN];
1194 TCHAR szNotepadAuthors[MAX_STRING_LEN];
1195
1196 HICON notepadIcon = LoadIcon(Globals.hInstance, MAKEINTRESOURCE(IDI_NPICON));
1197
1198 LoadString(Globals.hInstance, STRING_NOTEPAD, szNotepad, ARRAY_SIZE(szNotepad));
1199 LoadString(Globals.hInstance, STRING_NOTEPAD_AUTHORS, szNotepadAuthors, ARRAY_SIZE(szNotepadAuthors));
1200
1201 ShellAbout(Globals.hMainWnd, szNotepad, szNotepadAuthors, notepadIcon);
1202 DeleteObject(notepadIcon);
1203 }
1204
1205 /***********************************************************************
1206 *
1207 * DIALOG_FilePageSetup
1208 */
1209 VOID DIALOG_FilePageSetup(void)
1210 {
1211 PAGESETUPDLG page;
1212
1213 ZeroMemory(&page, sizeof(page));
1214 page.lStructSize = sizeof(page);
1215 page.hwndOwner = Globals.hMainWnd;
1216 page.Flags = PSD_ENABLEPAGESETUPTEMPLATE | PSD_ENABLEPAGESETUPHOOK | PSD_MARGINS;
1217 page.hInstance = Globals.hInstance;
1218 page.rtMargin = Globals.lMargins;
1219 page.hDevMode = Globals.hDevMode;
1220 page.hDevNames = Globals.hDevNames;
1221 page.lpPageSetupTemplateName = MAKEINTRESOURCE(DIALOG_PAGESETUP);
1222 page.lpfnPageSetupHook = DIALOG_PAGESETUP_Hook;
1223
1224 PageSetupDlg(&page);
1225
1226 Globals.hDevMode = page.hDevMode;
1227 Globals.hDevNames = page.hDevNames;
1228 Globals.lMargins = page.rtMargin;
1229 }
1230
1231 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1232 *
1233 * DIALOG_PAGESETUP_Hook
1234 */
1235
1236 static UINT_PTR CALLBACK DIALOG_PAGESETUP_Hook(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
1237 {
1238 switch (msg)
1239 {
1240 case WM_COMMAND:
1241 if (HIWORD(wParam) == BN_CLICKED)
1242 {
1243 switch (LOWORD(wParam))
1244 {
1245 case IDOK:
1246 /* save user input and close dialog */
1247 GetDlgItemText(hDlg, 0x141, Globals.szHeader, ARRAY_SIZE(Globals.szHeader));
1248 GetDlgItemText(hDlg, 0x143, Globals.szFooter, ARRAY_SIZE(Globals.szFooter));
1249 return FALSE;
1250
1251 case IDCANCEL:
1252 /* discard user input and close dialog */
1253 return FALSE;
1254
1255 case IDHELP:
1256 {
1257 /* FIXME: Bring this to work */
1258 static const TCHAR sorry[] = _T("Sorry, no help available");
1259 static const TCHAR help[] = _T("Help");
1260 MessageBox(Globals.hMainWnd, sorry, help, MB_ICONEXCLAMATION);
1261 return TRUE;
1262 }
1263
1264 default:
1265 break;
1266 }
1267 }
1268 break;
1269
1270 case WM_INITDIALOG:
1271 /* fetch last user input prior to display dialog */
1272 SetDlgItemText(hDlg, 0x141, Globals.szHeader);
1273 SetDlgItemText(hDlg, 0x143, Globals.szFooter);
1274 break;
1275 }
1276
1277 return FALSE;
1278 }