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