notepad: Implement printing to file.
[wine] / programs / 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  *  Copyright 2007 Rolf Kalbermatter
8  *  Copyright 2010 Vitaly Perov
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24
25 #include <assert.h>
26 #include <stdio.h>
27 #include <windows.h>
28 #include <commdlg.h>
29 #include <shlwapi.h>
30 #include <winternl.h>
31
32 #include "main.h"
33 #include "dialog.h"
34
35 #define SPACES_IN_TAB 8
36 #define PRINT_LEN_MAX 500
37
38 static const WCHAR helpfileW[] = { 'n','o','t','e','p','a','d','.','h','l','p',0 };
39
40 static INT_PTR WINAPI DIALOG_PAGESETUP_DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
41
42 /* Swap bytes of WCHAR buffer (big-endian <-> little-endian). */
43 static inline void byteswap_wide_string(LPWSTR str, UINT num)
44 {
45     UINT i;
46     for (i = 0; i < num; i++) str[i] = RtlUshortByteSwap(str[i]);
47 }
48
49 static void load_encoding_name(ENCODING enc, WCHAR* buffer, int length)
50 {
51     switch (enc)
52     {
53         case ENCODING_UTF16LE:
54             LoadStringW(Globals.hInstance, STRING_UNICODE_LE, buffer, length);
55             break;
56
57         case ENCODING_UTF16BE:
58             LoadStringW(Globals.hInstance, STRING_UNICODE_BE, buffer, length);
59             break;
60
61         default:
62         {
63             CPINFOEXW cpi;
64             GetCPInfoExW((enc==ENCODING_UTF8) ? CP_UTF8 : CP_ACP, 0, &cpi);
65             lstrcpynW(buffer, cpi.CodePageName, length);
66             break;
67         }
68     }
69 }
70
71 VOID ShowLastError(void)
72 {
73     DWORD error = GetLastError();
74     if (error != NO_ERROR)
75     {
76         LPWSTR lpMsgBuf;
77         WCHAR szTitle[MAX_STRING_LEN];
78
79         LoadStringW(Globals.hInstance, STRING_ERROR, szTitle, ARRAY_SIZE(szTitle));
80         FormatMessageW(
81             FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
82             NULL, error, 0, (LPWSTR)&lpMsgBuf, 0, NULL);
83         MessageBoxW(NULL, lpMsgBuf, szTitle, MB_OK | MB_ICONERROR);
84         LocalFree(lpMsgBuf);
85     }
86 }
87
88 /**
89  * Sets the caption of the main window according to Globals.szFileTitle:
90  *    Untitled - Notepad        if no file is open
91  *    filename - Notepad        if a file is given
92  */
93 void UpdateWindowCaption(void)
94 {
95   WCHAR szCaption[MAX_STRING_LEN];
96   WCHAR szNotepad[MAX_STRING_LEN];
97   static const WCHAR hyphenW[] = { ' ','-',' ',0 };
98
99   if (Globals.szFileTitle[0] != '\0')
100       lstrcpyW(szCaption, Globals.szFileTitle);
101   else
102       LoadStringW(Globals.hInstance, STRING_UNTITLED, szCaption, ARRAY_SIZE(szCaption));
103
104   LoadStringW(Globals.hInstance, STRING_NOTEPAD, szNotepad, ARRAY_SIZE(szNotepad));
105   lstrcatW(szCaption, hyphenW);
106   lstrcatW(szCaption, szNotepad);
107
108   SetWindowTextW(Globals.hMainWnd, szCaption);
109 }
110
111 int DIALOG_StringMsgBox(HWND hParent, int formatId, LPCWSTR szString, DWORD dwFlags)
112 {
113    WCHAR szMessage[MAX_STRING_LEN];
114    WCHAR szResource[MAX_STRING_LEN];
115
116    /* Load and format szMessage */
117    LoadStringW(Globals.hInstance, formatId, szResource, ARRAY_SIZE(szResource));
118    wnsprintfW(szMessage, ARRAY_SIZE(szMessage), szResource, szString);
119
120    /* Load szCaption */
121    if ((dwFlags & MB_ICONMASK) == MB_ICONEXCLAMATION)
122      LoadStringW(Globals.hInstance, STRING_ERROR,  szResource, ARRAY_SIZE(szResource));
123    else
124      LoadStringW(Globals.hInstance, STRING_NOTEPAD,  szResource, ARRAY_SIZE(szResource));
125
126    /* Display Modal Dialog */
127    if (hParent == NULL)
128      hParent = Globals.hMainWnd;
129    return MessageBoxW(hParent, szMessage, szResource, dwFlags);
130 }
131
132 static void AlertFileNotFound(LPCWSTR szFileName)
133 {
134    DIALOG_StringMsgBox(NULL, STRING_NOTFOUND, szFileName, MB_ICONEXCLAMATION|MB_OK);
135 }
136
137 static int AlertFileNotSaved(LPCWSTR szFileName)
138 {
139    WCHAR szUntitled[MAX_STRING_LEN];
140
141    LoadStringW(Globals.hInstance, STRING_UNTITLED, szUntitled, ARRAY_SIZE(szUntitled));
142    return DIALOG_StringMsgBox(NULL, STRING_NOTSAVED, szFileName[0] ? szFileName : szUntitled,
143      MB_ICONQUESTION|MB_YESNOCANCEL);
144 }
145
146 static int AlertUnicodeCharactersLost(LPCWSTR szFileName)
147 {
148     WCHAR szMsgFormat[MAX_STRING_LEN];
149     WCHAR szEnc[MAX_STRING_LEN];
150     WCHAR szMsg[ARRAY_SIZE(szMsgFormat) + MAX_PATH + ARRAY_SIZE(szEnc)];
151     WCHAR szCaption[MAX_STRING_LEN];
152
153     LoadStringW(Globals.hInstance, STRING_LOSS_OF_UNICODE_CHARACTERS,
154                 szMsgFormat, ARRAY_SIZE(szMsgFormat));
155     load_encoding_name(ENCODING_ANSI, szEnc, ARRAY_SIZE(szEnc));
156     wnsprintfW(szMsg, ARRAY_SIZE(szMsg), szMsgFormat, szFileName, szEnc);
157     LoadStringW(Globals.hInstance, STRING_NOTEPAD, szCaption,
158                 ARRAY_SIZE(szCaption));
159     return MessageBoxW(Globals.hMainWnd, szMsg, szCaption,
160                        MB_OKCANCEL|MB_ICONEXCLAMATION);
161 }
162
163 /**
164  * Returns:
165  *   TRUE  - if file exists
166  *   FALSE - if file does not exist
167  */
168 BOOL FileExists(LPCWSTR szFilename)
169 {
170    WIN32_FIND_DATAW entry;
171    HANDLE hFile;
172
173    hFile = FindFirstFileW(szFilename, &entry);
174    FindClose(hFile);
175
176    return (hFile != INVALID_HANDLE_VALUE);
177 }
178
179 static inline BOOL is_conversion_to_ansi_lossy(LPCWSTR textW, int lenW)
180 {
181     BOOL ret = FALSE;
182     WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, textW, lenW, NULL, 0,
183                         NULL, &ret);
184     return ret;
185 }
186
187 typedef enum
188 {
189     SAVED_OK,
190     SAVE_FAILED,
191     SHOW_SAVEAS_DIALOG
192 } SAVE_STATUS;
193
194 /* szFileName is the filename to save under; enc is the encoding to use.
195  *
196  * If the function succeeds, it returns SAVED_OK.
197  * If the function fails, it returns SAVE_FAILED.
198  * If Unicode data could be lost due to conversion to a non-Unicode character
199  * set, a warning is displayed. The user can continue (and the function carries
200  * on), or cancel (and the function returns SHOW_SAVEAS_DIALOG).
201  */
202 static SAVE_STATUS DoSaveFile(LPCWSTR szFileName, ENCODING enc)
203 {
204     int lenW;
205     WCHAR* textW;
206     HANDLE hFile;
207     DWORD dwNumWrite;
208     PVOID pBytes;
209     DWORD size;
210
211     /* lenW includes the byte-order mark, but not the \0. */
212     lenW = GetWindowTextLengthW(Globals.hEdit) + 1;
213     textW = HeapAlloc(GetProcessHeap(), 0, (lenW+1) * sizeof(WCHAR));
214     if (!textW)
215     {
216         ShowLastError();
217         return SAVE_FAILED;
218     }
219     textW[0] = (WCHAR) 0xfeff;
220     lenW = GetWindowTextW(Globals.hEdit, textW+1, lenW) + 1;
221
222     switch (enc)
223     {
224     case ENCODING_UTF16BE:
225         byteswap_wide_string(textW, lenW);
226         /* fall through */
227
228     case ENCODING_UTF16LE:
229         size = lenW * sizeof(WCHAR);
230         pBytes = textW;
231         break;
232
233     case ENCODING_UTF8:
234         size = WideCharToMultiByte(CP_UTF8, 0, textW, lenW, NULL, 0, NULL, NULL);
235         pBytes = HeapAlloc(GetProcessHeap(), 0, size);
236         if (!pBytes)
237         {
238             ShowLastError();
239             HeapFree(GetProcessHeap(), 0, textW);
240             return SAVE_FAILED;
241         }
242         WideCharToMultiByte(CP_UTF8, 0, textW, lenW, pBytes, size, NULL, NULL);
243         HeapFree(GetProcessHeap(), 0, textW);
244         break;
245
246     default:
247         if (is_conversion_to_ansi_lossy(textW+1, lenW-1)
248             && AlertUnicodeCharactersLost(szFileName) == IDCANCEL)
249         {
250             HeapFree(GetProcessHeap(), 0, textW);
251             return SHOW_SAVEAS_DIALOG;
252         }
253
254         size = WideCharToMultiByte(CP_ACP, 0, textW+1, lenW-1, NULL, 0, NULL, NULL);
255         pBytes = HeapAlloc(GetProcessHeap(), 0, size);
256         if (!pBytes)
257         {
258             ShowLastError();
259             HeapFree(GetProcessHeap(), 0, textW);
260             return SAVE_FAILED;
261         }
262         WideCharToMultiByte(CP_ACP, 0, textW+1, lenW-1, pBytes, size, NULL, NULL);
263         HeapFree(GetProcessHeap(), 0, textW);
264         break;
265     }
266
267     hFile = CreateFileW(szFileName, GENERIC_WRITE, FILE_SHARE_WRITE,
268                        NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
269     if(hFile == INVALID_HANDLE_VALUE)
270     {
271         ShowLastError();
272         HeapFree(GetProcessHeap(), 0, pBytes);
273         return SAVE_FAILED;
274     }
275     if (!WriteFile(hFile, pBytes, size, &dwNumWrite, NULL))
276     {
277         ShowLastError();
278         CloseHandle(hFile);
279         HeapFree(GetProcessHeap(), 0, pBytes);
280         return SAVE_FAILED;
281     }
282     SetEndOfFile(hFile);
283     CloseHandle(hFile);
284     HeapFree(GetProcessHeap(), 0, pBytes);
285
286     SendMessageW(Globals.hEdit, EM_SETMODIFY, FALSE, 0);
287     return SAVED_OK;
288 }
289
290 /**
291  * Returns:
292  *   TRUE  - User agreed to close (both save/don't save)
293  *   FALSE - User cancelled close by selecting "Cancel"
294  */
295 BOOL DoCloseFile(void)
296 {
297     int nResult;
298     static const WCHAR empty_strW[] = { 0 };
299
300     if (SendMessageW(Globals.hEdit, EM_GETMODIFY, 0, 0))
301     {
302         /* prompt user to save changes */
303         nResult = AlertFileNotSaved(Globals.szFileName);
304         switch (nResult) {
305             case IDYES:     return DIALOG_FileSave();
306
307             case IDNO:      break;
308
309             case IDCANCEL:  return(FALSE);
310
311             default:        return(FALSE);
312         } /* switch */
313     } /* if */
314
315     SetFileNameAndEncoding(empty_strW, ENCODING_ANSI);
316
317     UpdateWindowCaption();
318     return(TRUE);
319 }
320
321 static inline ENCODING detect_encoding_of_buffer(const void* buffer, int size)
322 {
323     static const char bom_utf8[] = { 0xef, 0xbb, 0xbf };
324     if (size >= sizeof(bom_utf8) && !memcmp(buffer, bom_utf8, sizeof(bom_utf8)))
325         return ENCODING_UTF8;
326     else
327     {
328         int flags = IS_TEXT_UNICODE_SIGNATURE |
329                     IS_TEXT_UNICODE_REVERSE_SIGNATURE |
330                     IS_TEXT_UNICODE_ODD_LENGTH;
331         IsTextUnicode(buffer, size, &flags);
332         if (flags & IS_TEXT_UNICODE_SIGNATURE)
333             return ENCODING_UTF16LE;
334         else if (flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE)
335             return ENCODING_UTF16BE;
336         else
337             return ENCODING_ANSI;
338     }
339 }
340
341 void DoOpenFile(LPCWSTR szFileName, ENCODING enc)
342 {
343     static const WCHAR dotlog[] = { '.','L','O','G',0 };
344     HANDLE hFile;
345     LPSTR pTemp;
346     DWORD size;
347     DWORD dwNumRead;
348     int lenW;
349     WCHAR* textW;
350     int i;
351     WCHAR log[5];
352
353     /* Close any files and prompt to save changes */
354     if (!DoCloseFile())
355         return;
356
357     hFile = CreateFileW(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
358                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
359     if(hFile == INVALID_HANDLE_VALUE)
360     {
361         AlertFileNotFound(szFileName);
362         return;
363     }
364
365     size = GetFileSize(hFile, NULL);
366     if (size == INVALID_FILE_SIZE)
367     {
368         CloseHandle(hFile);
369         ShowLastError();
370         return;
371     }
372
373     /* Extra memory for (WCHAR)'\0'-termination. */
374     pTemp = HeapAlloc(GetProcessHeap(), 0, size+2);
375     if (!pTemp)
376     {
377         CloseHandle(hFile);
378         ShowLastError();
379         return;
380     }
381
382     if (!ReadFile(hFile, pTemp, size, &dwNumRead, NULL))
383     {
384         CloseHandle(hFile);
385         HeapFree(GetProcessHeap(), 0, pTemp);
386         ShowLastError();
387         return;
388     }
389
390     CloseHandle(hFile);
391
392     size = dwNumRead;
393
394     if (enc == ENCODING_AUTO)
395         enc = detect_encoding_of_buffer(pTemp, size);
396     else if (size >= 2 && (enc==ENCODING_UTF16LE || enc==ENCODING_UTF16BE))
397     {
398         /* If UTF-16 (BE or LE) is selected, and there is a UTF-16 BOM,
399          * override the selection (like native Notepad).
400          */
401         if ((BYTE)pTemp[0] == 0xff && (BYTE)pTemp[1] == 0xfe)
402             enc = ENCODING_UTF16LE;
403         else if ((BYTE)pTemp[0] == 0xfe && (BYTE)pTemp[1] == 0xff)
404             enc = ENCODING_UTF16BE;
405     }
406
407     switch (enc)
408     {
409     case ENCODING_UTF16BE:
410         byteswap_wide_string((WCHAR*) pTemp, size/sizeof(WCHAR));
411         /* Forget whether the file is BE or LE, like native Notepad. */
412         enc = ENCODING_UTF16LE;
413
414         /* fall through */
415
416     case ENCODING_UTF16LE:
417         textW = (LPWSTR)pTemp;
418         lenW  = size/sizeof(WCHAR);
419         break;
420
421     default:
422         {
423             int cp = (enc==ENCODING_UTF8) ? CP_UTF8 : CP_ACP;
424             lenW = MultiByteToWideChar(cp, 0, pTemp, size, NULL, 0);
425             textW = HeapAlloc(GetProcessHeap(), 0, (lenW+1) * sizeof(WCHAR));
426             if (!textW)
427             {
428                 ShowLastError();
429                 HeapFree(GetProcessHeap(), 0, pTemp);
430                 return;
431             }
432             MultiByteToWideChar(cp, 0, pTemp, size, textW, lenW);
433             HeapFree(GetProcessHeap(), 0, pTemp);
434             break;
435         }
436     }
437
438     /* Replace '\0's with spaces. Other than creating a custom control that
439      * can deal with '\0' characters, it's the best that can be done.
440      */
441     for (i = 0; i < lenW; i++)
442         if (textW[i] == '\0')
443             textW[i] = ' ';
444     textW[lenW] = '\0';
445
446     if (lenW >= 1 && textW[0] == 0xfeff)
447         SetWindowTextW(Globals.hEdit, textW+1);
448     else
449         SetWindowTextW(Globals.hEdit, textW);
450
451     HeapFree(GetProcessHeap(), 0, textW);
452
453     SendMessageW(Globals.hEdit, EM_SETMODIFY, FALSE, 0);
454     SendMessageW(Globals.hEdit, EM_EMPTYUNDOBUFFER, 0, 0);
455     SetFocus(Globals.hEdit);
456     
457     /*  If the file starts with .LOG, add a time/date at the end and set cursor after */
458     if (GetWindowTextW(Globals.hEdit, log, ARRAY_SIZE(log)) && !lstrcmpW(log, dotlog))
459     {
460         static const WCHAR lfW[] = { '\r','\n',0 };
461         SendMessageW(Globals.hEdit, EM_SETSEL, GetWindowTextLengthW(Globals.hEdit), -1);
462         SendMessageW(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)lfW);
463         DIALOG_EditTimeDate();
464         SendMessageW(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)lfW);
465     }
466
467     SetFileNameAndEncoding(szFileName, enc);
468     UpdateWindowCaption();
469 }
470
471 VOID DIALOG_FileNew(VOID)
472 {
473     static const WCHAR empty_strW[] = { 0 };
474
475     /* Close any files and prompt to save changes */
476     if (DoCloseFile()) {
477         SetWindowTextW(Globals.hEdit, empty_strW);
478         SendMessageW(Globals.hEdit, EM_EMPTYUNDOBUFFER, 0, 0);
479         SetFocus(Globals.hEdit);
480     }
481 }
482
483 /* Used to detect encoding of files selected in Open dialog.
484  * Returns ENCODING_AUTO if file can't be read, etc.
485  */
486 static ENCODING detect_encoding_of_file(LPCWSTR szFileName)
487 {
488     DWORD size;
489     HANDLE hFile = CreateFileW(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
490                                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
491     if (hFile == INVALID_HANDLE_VALUE)
492         return ENCODING_AUTO;
493     size = GetFileSize(hFile, NULL);
494     if (size == INVALID_FILE_SIZE)
495     {
496         CloseHandle(hFile);
497         return ENCODING_AUTO;
498     }
499     else
500     {
501         DWORD dwNumRead;
502         BYTE buffer[MAX_STRING_LEN];
503         if (!ReadFile(hFile, buffer, min(size, sizeof(buffer)), &dwNumRead, NULL))
504         {
505             CloseHandle(hFile);
506             return ENCODING_AUTO;
507         }
508         CloseHandle(hFile);
509         return detect_encoding_of_buffer(buffer, dwNumRead);
510     }
511 }
512
513 static LPWSTR dialog_print_to_file(HWND hMainWnd)
514 {
515     OPENFILENAMEW ofn;
516     static WCHAR file[MAX_PATH] = {'o','u','t','p','u','t','.','p','r','n',0};
517     static const WCHAR defExt[] = {'p','r','n',0};
518
519     ZeroMemory(&ofn, sizeof(ofn));
520
521     ofn.lStructSize = sizeof(ofn);
522     ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
523     ofn.hwndOwner = hMainWnd;
524     ofn.lpstrFile = file;
525     ofn.nMaxFile = MAX_PATH;
526     ofn.lpstrDefExt = defExt;
527
528     if(GetSaveFileNameW(&ofn))
529         return file;
530     else
531         return FALSE;
532 }
533 static UINT_PTR CALLBACK OfnHookProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
534 {
535     static HWND hEncCombo;
536
537     switch (uMsg)
538     {
539     case WM_INITDIALOG:
540         {
541             ENCODING enc;
542             hEncCombo = GetDlgItem(hdlg, IDC_OFN_ENCCOMBO);
543             for (enc = MIN_ENCODING; enc <= MAX_ENCODING; enc++)
544             {
545                 WCHAR szEnc[MAX_STRING_LEN];
546                 load_encoding_name(enc, szEnc, ARRAY_SIZE(szEnc));
547                 SendMessageW(hEncCombo, CB_ADDSTRING, 0, (LPARAM)szEnc);
548             }
549             SendMessageW(hEncCombo, CB_SETCURSEL, (WPARAM)Globals.encOfnCombo, 0);
550         }
551         break;
552
553     case WM_COMMAND:
554         if (LOWORD(wParam) == IDC_OFN_ENCCOMBO &&
555             HIWORD(wParam) == CBN_SELCHANGE)
556         {
557             int index = SendMessageW(hEncCombo, CB_GETCURSEL, 0, 0);
558             Globals.encOfnCombo = index==CB_ERR ? ENCODING_ANSI : (ENCODING)index;
559         }
560
561         break;
562
563     case WM_NOTIFY:
564         switch (((OFNOTIFYW*)lParam)->hdr.code)
565         {
566             case CDN_SELCHANGE:
567                 if (Globals.bOfnIsOpenDialog)
568                 {
569                     /* Check the start of the selected file for a BOM. */
570                     ENCODING enc;
571                     WCHAR szFileName[MAX_PATH];
572                     SendMessageW(GetParent(hdlg), CDM_GETFILEPATH,
573                                  ARRAY_SIZE(szFileName), (LPARAM)szFileName);
574                     enc = detect_encoding_of_file(szFileName);
575                     if (enc != ENCODING_AUTO)
576                     {
577                         Globals.encOfnCombo = enc;
578                         SendMessageW(hEncCombo, CB_SETCURSEL, (WPARAM)enc, 0);
579                     }
580                 }
581                 break;
582
583             default:
584                 break;
585         }
586         break;
587
588     default:
589         break;
590     }
591     return 0;
592 }
593
594 VOID DIALOG_FileOpen(VOID)
595 {
596     OPENFILENAMEW openfilename;
597     WCHAR szPath[MAX_PATH];
598     WCHAR szDir[MAX_PATH];
599     static const WCHAR szDefaultExt[] = { 't','x','t',0 };
600     static const WCHAR txt_files[] = { '*','.','t','x','t',0 };
601
602     ZeroMemory(&openfilename, sizeof(openfilename));
603
604     GetCurrentDirectoryW(ARRAY_SIZE(szDir), szDir);
605     lstrcpyW(szPath, txt_files);
606
607     openfilename.lStructSize       = sizeof(openfilename);
608     openfilename.hwndOwner         = Globals.hMainWnd;
609     openfilename.hInstance         = Globals.hInstance;
610     openfilename.lpstrFilter       = Globals.szFilter;
611     openfilename.lpstrFile         = szPath;
612     openfilename.nMaxFile          = ARRAY_SIZE(szPath);
613     openfilename.lpstrInitialDir   = szDir;
614     openfilename.Flags = OFN_ENABLETEMPLATE | OFN_ENABLEHOOK | OFN_EXPLORER |
615                          OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST |
616                          OFN_HIDEREADONLY | OFN_ENABLESIZING;
617     openfilename.lpfnHook          = OfnHookProc;
618     openfilename.lpTemplateName    = MAKEINTRESOURCEW(IDD_OFN_TEMPLATE);
619     openfilename.lpstrDefExt       = szDefaultExt;
620
621     Globals.encOfnCombo = ENCODING_ANSI;
622     Globals.bOfnIsOpenDialog = TRUE;
623
624     if (GetOpenFileNameW(&openfilename))
625         DoOpenFile(openfilename.lpstrFile, Globals.encOfnCombo);
626 }
627
628 /* Return FALSE to cancel close */
629 BOOL DIALOG_FileSave(VOID)
630 {
631     if (Globals.szFileName[0] == '\0')
632         return DIALOG_FileSaveAs();
633     else
634     {
635         switch (DoSaveFile(Globals.szFileName, Globals.encFile))
636         {
637             case SAVED_OK:           return TRUE;
638             case SHOW_SAVEAS_DIALOG: return DIALOG_FileSaveAs();
639             default:                 return FALSE;
640         }
641     }
642 }
643
644 BOOL DIALOG_FileSaveAs(VOID)
645 {
646     OPENFILENAMEW saveas;
647     WCHAR szPath[MAX_PATH];
648     WCHAR szDir[MAX_PATH];
649     static const WCHAR szDefaultExt[] = { 't','x','t',0 };
650     static const WCHAR txt_files[] = { '*','.','t','x','t',0 };
651
652     ZeroMemory(&saveas, sizeof(saveas));
653
654     GetCurrentDirectoryW(ARRAY_SIZE(szDir), szDir);
655     lstrcpyW(szPath, txt_files);
656
657     saveas.lStructSize       = sizeof(OPENFILENAMEW);
658     saveas.hwndOwner         = Globals.hMainWnd;
659     saveas.hInstance         = Globals.hInstance;
660     saveas.lpstrFilter       = Globals.szFilter;
661     saveas.lpstrFile         = szPath;
662     saveas.nMaxFile          = ARRAY_SIZE(szPath);
663     saveas.lpstrInitialDir   = szDir;
664     saveas.Flags          = OFN_ENABLETEMPLATE | OFN_ENABLEHOOK | OFN_EXPLORER |
665                             OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT |
666                             OFN_HIDEREADONLY | OFN_ENABLESIZING;
667     saveas.lpfnHook          = OfnHookProc;
668     saveas.lpTemplateName    = MAKEINTRESOURCEW(IDD_OFN_TEMPLATE);
669     saveas.lpstrDefExt       = szDefaultExt;
670
671     /* Preset encoding to what file was opened/saved last with. */
672     Globals.encOfnCombo = Globals.encFile;
673     Globals.bOfnIsOpenDialog = FALSE;
674
675 retry:
676     if (!GetSaveFileNameW(&saveas))
677         return FALSE;
678
679     switch (DoSaveFile(szPath, Globals.encOfnCombo))
680     {
681         case SAVED_OK:
682             SetFileNameAndEncoding(szPath, Globals.encOfnCombo);
683             UpdateWindowCaption();
684             return TRUE;
685
686         case SHOW_SAVEAS_DIALOG:
687             goto retry;
688
689         default:
690             return FALSE;
691     }
692 }
693
694 typedef struct {
695     LPWSTR mptr;
696     LPWSTR mend;
697     LPWSTR lptr;
698     DWORD len;
699 } TEXTINFO, *LPTEXTINFO;
700
701 static int notepad_print_header(HDC hdc, RECT *rc, BOOL dopage, BOOL header, int page, LPWSTR text)
702 {
703     SIZE szMetric;
704
705     if (*text)
706     {
707         /* Write the header or footer */
708         GetTextExtentPoint32W(hdc, text, lstrlenW(text), &szMetric);
709         if (dopage)
710             ExtTextOutW(hdc, (rc->left + rc->right - szMetric.cx) / 2,
711                         header ? rc->top : rc->bottom - szMetric.cy,
712                         ETO_CLIPPED, rc, text, lstrlenW(text), NULL);
713         return 1;
714     }
715     return 0;
716 }
717
718 static BOOL notepad_print_page(HDC hdc, RECT *rc, BOOL dopage, int page, LPTEXTINFO tInfo)
719 {
720     int b, y;
721     TEXTMETRICW tm;
722     SIZE szMetrics;
723
724     if (dopage)
725     {
726         if (StartPage(hdc) <= 0)
727         {
728             static const WCHAR failedW[] = { 'S','t','a','r','t','P','a','g','e',' ','f','a','i','l','e','d',0 };
729             static const WCHAR errorW[] = { 'P','r','i','n','t',' ','E','r','r','o','r',0 };
730             MessageBoxW(Globals.hMainWnd, failedW, errorW, MB_ICONEXCLAMATION);
731             return FALSE;
732         }
733     }
734
735     GetTextMetricsW(hdc, &tm);
736     y = rc->top + notepad_print_header(hdc, rc, dopage, TRUE, page, Globals.szFileName) * tm.tmHeight;
737     b = rc->bottom - 2 * notepad_print_header(hdc, rc, FALSE, FALSE, page, Globals.szFooter) * tm.tmHeight;
738
739     do {
740         INT m, n;
741
742         if (!tInfo->len)
743         {
744             /* find the end of the line */
745             while (tInfo->mptr < tInfo->mend && *tInfo->mptr != '\n' && *tInfo->mptr != '\r')
746             {
747                 if (*tInfo->mptr == '\t')
748                 {
749                     /* replace tabs with spaces */
750                     for (m = 0; m < SPACES_IN_TAB; m++)
751                     {
752                         if (tInfo->len < PRINT_LEN_MAX)
753                             tInfo->lptr[tInfo->len++] = ' ';
754                         else if (Globals.bWrapLongLines)
755                             break;
756                     }
757                 }
758                 else if (tInfo->len < PRINT_LEN_MAX)
759                     tInfo->lptr[tInfo->len++] = *tInfo->mptr;
760
761                 if (tInfo->len >= PRINT_LEN_MAX && Globals.bWrapLongLines)
762                      break;
763
764                 tInfo->mptr++;
765             }
766         }
767
768         /* Find out how much we should print if line wrapping is enabled */
769         if (Globals.bWrapLongLines)
770         {
771             GetTextExtentExPointW(hdc, tInfo->lptr, tInfo->len, rc->right - rc->left, &n, NULL, &szMetrics);
772             if (n < tInfo->len && tInfo->lptr[n] != ' ')
773             {
774                 m = n;
775                 /* Don't wrap words unless it's a single word over the entire line */
776                 while (m  && tInfo->lptr[m] != ' ') m--;
777                 if (m > 0) n = m + 1;
778             }
779         }
780         else
781             n = tInfo->len;
782
783         if (dopage)
784             ExtTextOutW(hdc, rc->left, y, ETO_CLIPPED, rc, tInfo->lptr, n, NULL);
785
786         tInfo->len -= n;
787
788         if (tInfo->len)
789         {
790             memcpy(tInfo->lptr, tInfo->lptr + n, tInfo->len * sizeof(WCHAR));
791             y += tm.tmHeight + tm.tmExternalLeading;
792         }
793         else
794         {
795             /* find the next line */
796             while (tInfo->mptr < tInfo->mend && y < b && (*tInfo->mptr == '\n' || *tInfo->mptr == '\r'))
797             {
798                 if (*tInfo->mptr == '\n')
799                     y += tm.tmHeight + tm.tmExternalLeading;
800                 tInfo->mptr++;
801             }
802         }
803     } while (tInfo->mptr < tInfo->mend && y < b);
804
805     notepad_print_header(hdc, rc, dopage, FALSE, page, Globals.szFooter);
806     if (dopage)
807     {
808         EndPage(hdc);
809     }
810     return TRUE;
811 }
812
813 VOID DIALOG_FilePrint(VOID)
814 {
815     DOCINFOW di;
816     PRINTDLGW printer;
817     int page, dopage, copy;
818     LOGFONTW lfFont;
819     HFONT hTextFont, old_font = 0;
820     DWORD size;
821     BOOL ret = FALSE;
822     RECT rc;
823     LPWSTR pTemp;
824     TEXTINFO tInfo;
825     WCHAR cTemp[PRINT_LEN_MAX];
826
827     /* Get Current Settings */
828     ZeroMemory(&printer, sizeof(printer));
829     printer.lStructSize           = sizeof(printer);
830     printer.hwndOwner             = Globals.hMainWnd;
831     printer.hDevMode              = Globals.hDevMode;
832     printer.hDevNames             = Globals.hDevNames;
833     printer.hInstance             = Globals.hInstance;
834
835     /* Set some default flags */
836     printer.Flags                 = PD_RETURNDC | PD_NOSELECTION;
837     printer.nFromPage             = 0;
838     printer.nMinPage              = 1;
839     /* we really need to calculate number of pages to set nMaxPage and nToPage */
840     printer.nToPage               = 0;
841     printer.nMaxPage              = -1;
842     /* Let commdlg manage copy settings */
843     printer.nCopies               = (WORD)PD_USEDEVMODECOPIES;
844
845     if (!PrintDlgW(&printer)) return;
846
847     Globals.hDevMode = printer.hDevMode;
848     Globals.hDevNames = printer.hDevNames;
849
850     SetMapMode(printer.hDC, MM_TEXT);
851
852     /* initialize DOCINFO */
853     di.cbSize = sizeof(DOCINFOW);
854     di.lpszDocName = Globals.szFileTitle;
855     di.lpszOutput = NULL;
856     di.lpszDatatype = NULL;
857     di.fwType = 0; 
858
859     if(printer.Flags & PD_PRINTTOFILE)
860     {
861         di.lpszOutput = dialog_print_to_file(printer.hwndOwner);
862         if(!di.lpszOutput)
863             return;
864     }
865
866     /* Get the file text */
867     size = GetWindowTextLengthW(Globals.hEdit) + 1;
868     pTemp = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
869     if (!pTemp)
870     {
871        DeleteDC(printer.hDC);
872        ShowLastError();
873        return;
874     }
875     size = GetWindowTextW(Globals.hEdit, pTemp, size);
876
877     if (StartDocW(printer.hDC, &di) > 0)
878     {
879         /* Get the page margins in pixels. */
880         rc.top =    MulDiv(Globals.iMarginTop, GetDeviceCaps(printer.hDC, LOGPIXELSY), 2540) -
881                     GetDeviceCaps(printer.hDC, PHYSICALOFFSETY);
882         rc.bottom = GetDeviceCaps(printer.hDC, PHYSICALHEIGHT) -
883                     MulDiv(Globals.iMarginBottom, GetDeviceCaps(printer.hDC, LOGPIXELSY), 2540);
884         rc.left =   MulDiv(Globals.iMarginLeft, GetDeviceCaps(printer.hDC, LOGPIXELSX), 2540) -
885                     GetDeviceCaps(printer.hDC, PHYSICALOFFSETX);
886         rc.right =  GetDeviceCaps(printer.hDC, PHYSICALWIDTH) -
887                     MulDiv(Globals.iMarginRight, GetDeviceCaps(printer.hDC, LOGPIXELSX), 2540);
888
889         /* Create a font for the printer resolution */
890         lfFont = Globals.lfFont;
891         lfFont.lfHeight = MulDiv(lfFont.lfHeight, GetDeviceCaps(printer.hDC, LOGPIXELSY), get_dpi());
892         /* Make the font a bit lighter */
893         lfFont.lfWeight -= 100;
894         hTextFont = CreateFontIndirectW(&lfFont);
895         old_font = SelectObject(printer.hDC, hTextFont);
896
897         for (copy = 1; copy <= printer.nCopies; copy++)
898         {
899             page = 1;
900
901             tInfo.mptr = pTemp;
902             tInfo.mend = pTemp + size;
903             tInfo.lptr = cTemp;
904             tInfo.len = 0;
905
906             do {
907                 if (printer.Flags & PD_PAGENUMS)
908                 {
909                     /* a specific range of pages is selected, so
910                      * skip pages that are not to be printed
911                      */
912                     if (page > printer.nToPage)
913                         break;
914                     else if (page >= printer.nFromPage)
915                         dopage = 1;
916                     else
917                         dopage = 0;
918                 }
919                 else
920                     dopage = 1;
921
922                 ret = notepad_print_page(printer.hDC, &rc, dopage, page, &tInfo);
923                 page++;
924             } while (ret && tInfo.mptr < tInfo.mend);
925
926             if (!ret) break;
927         }
928         EndDoc(printer.hDC);
929         SelectObject(printer.hDC, old_font);
930         DeleteObject(hTextFont);
931     }
932     DeleteDC(printer.hDC);
933     HeapFree(GetProcessHeap(), 0, pTemp);
934 }
935
936 VOID DIALOG_FilePrinterSetup(VOID)
937 {
938     PRINTDLGW printer;
939
940     ZeroMemory(&printer, sizeof(printer));
941     printer.lStructSize         = sizeof(printer);
942     printer.hwndOwner           = Globals.hMainWnd;
943     printer.hDevMode            = Globals.hDevMode;
944     printer.hDevNames           = Globals.hDevNames;
945     printer.hInstance           = Globals.hInstance;
946     printer.Flags               = PD_PRINTSETUP;
947     printer.nCopies             = 1;
948
949     PrintDlgW(&printer);
950
951     Globals.hDevMode = printer.hDevMode;
952     Globals.hDevNames = printer.hDevNames;
953 }
954
955 VOID DIALOG_FileExit(VOID)
956 {
957     PostMessageW(Globals.hMainWnd, WM_CLOSE, 0, 0l);
958 }
959
960 VOID DIALOG_EditUndo(VOID)
961 {
962     SendMessageW(Globals.hEdit, EM_UNDO, 0, 0);
963 }
964
965 VOID DIALOG_EditCut(VOID)
966 {
967     SendMessageW(Globals.hEdit, WM_CUT, 0, 0);
968 }
969
970 VOID DIALOG_EditCopy(VOID)
971 {
972     SendMessageW(Globals.hEdit, WM_COPY, 0, 0);
973 }
974
975 VOID DIALOG_EditPaste(VOID)
976 {
977     SendMessageW(Globals.hEdit, WM_PASTE, 0, 0);
978 }
979
980 VOID DIALOG_EditDelete(VOID)
981 {
982     SendMessageW(Globals.hEdit, WM_CLEAR, 0, 0);
983 }
984
985 VOID DIALOG_EditSelectAll(VOID)
986 {
987     SendMessageW(Globals.hEdit, EM_SETSEL, 0, -1);
988 }
989
990 VOID DIALOG_EditTimeDate(VOID)
991 {
992     SYSTEMTIME   st;
993     WCHAR        szDate[MAX_STRING_LEN];
994     static const WCHAR spaceW[] = { ' ',0 };
995
996     GetLocalTime(&st);
997
998     GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, szDate, MAX_STRING_LEN);
999     SendMessageW(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)szDate);
1000
1001     SendMessageW(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)spaceW);
1002
1003     GetDateFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, szDate, MAX_STRING_LEN);
1004     SendMessageW(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)szDate);
1005 }
1006
1007 VOID DIALOG_EditWrap(VOID)
1008 {
1009     BOOL modify = FALSE;
1010     static const WCHAR editW[] = { 'e','d','i','t',0 };
1011     DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL |
1012                     ES_AUTOVSCROLL | ES_MULTILINE;
1013     RECT rc;
1014     DWORD size;
1015     LPWSTR pTemp;
1016
1017     size = GetWindowTextLengthW(Globals.hEdit) + 1;
1018     pTemp = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
1019     if (!pTemp)
1020     {
1021         ShowLastError();
1022         return;
1023     }
1024     GetWindowTextW(Globals.hEdit, pTemp, size);
1025     modify = SendMessageW(Globals.hEdit, EM_GETMODIFY, 0, 0);
1026     DestroyWindow(Globals.hEdit);
1027     GetClientRect(Globals.hMainWnd, &rc);
1028     if( Globals.bWrapLongLines ) dwStyle |= WS_HSCROLL | ES_AUTOHSCROLL;
1029     Globals.hEdit = CreateWindowExW(WS_EX_CLIENTEDGE, editW, NULL, dwStyle,
1030                          0, 0, rc.right, rc.bottom, Globals.hMainWnd,
1031                          NULL, Globals.hInstance, NULL);
1032     SendMessageW(Globals.hEdit, WM_SETFONT, (WPARAM)Globals.hFont, FALSE);
1033     SetWindowTextW(Globals.hEdit, pTemp);
1034     SendMessageW(Globals.hEdit, EM_SETMODIFY, modify, 0);
1035     SetFocus(Globals.hEdit);
1036     HeapFree(GetProcessHeap(), 0, pTemp);
1037     
1038     Globals.bWrapLongLines = !Globals.bWrapLongLines;
1039     CheckMenuItem(GetMenu(Globals.hMainWnd), CMD_WRAP,
1040         MF_BYCOMMAND | (Globals.bWrapLongLines ? MF_CHECKED : MF_UNCHECKED));
1041 }
1042
1043 VOID DIALOG_SelectFont(VOID)
1044 {
1045     CHOOSEFONTW cf;
1046     LOGFONTW lf=Globals.lfFont;
1047
1048     ZeroMemory( &cf, sizeof(cf) );
1049     cf.lStructSize=sizeof(cf);
1050     cf.hwndOwner=Globals.hMainWnd;
1051     cf.lpLogFont=&lf;
1052     cf.Flags=CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT;
1053
1054     if( ChooseFontW(&cf) )
1055     {
1056         HFONT currfont=Globals.hFont;
1057
1058         Globals.hFont=CreateFontIndirectW( &lf );
1059         Globals.lfFont=lf;
1060         SendMessageW( Globals.hEdit, WM_SETFONT, (WPARAM)Globals.hFont, TRUE );
1061         if( currfont!=NULL )
1062             DeleteObject( currfont );
1063     }
1064 }
1065
1066 VOID DIALOG_Search(VOID)
1067 {
1068         /* Allow only one search/replace dialog to open */
1069         if(Globals.hFindReplaceDlg != NULL)
1070         {
1071             SetActiveWindow(Globals.hFindReplaceDlg);
1072             return;
1073         }
1074
1075         ZeroMemory(&Globals.find, sizeof(Globals.find));
1076         Globals.find.lStructSize      = sizeof(Globals.find);
1077         Globals.find.hwndOwner        = Globals.hMainWnd;
1078         Globals.find.hInstance        = Globals.hInstance;
1079         Globals.find.lpstrFindWhat    = Globals.szFindText;
1080         Globals.find.wFindWhatLen     = ARRAY_SIZE(Globals.szFindText);
1081         Globals.find.Flags            = FR_DOWN|FR_HIDEWHOLEWORD;
1082
1083         /* We only need to create the modal FindReplace dialog which will */
1084         /* notify us of incoming events using hMainWnd Window Messages    */
1085
1086         Globals.hFindReplaceDlg = FindTextW(&Globals.find);
1087         assert(Globals.hFindReplaceDlg !=0);
1088 }
1089
1090 VOID DIALOG_SearchNext(VOID)
1091 {
1092     if (Globals.lastFind.lpstrFindWhat == NULL)
1093         DIALOG_Search();
1094     else                /* use the last find data */
1095         NOTEPAD_DoFind(&Globals.lastFind);
1096 }
1097
1098 VOID DIALOG_Replace(VOID)
1099 {
1100         /* Allow only one search/replace dialog to open */
1101         if(Globals.hFindReplaceDlg != NULL)
1102         {
1103             SetActiveWindow(Globals.hFindReplaceDlg);
1104             return;
1105         }
1106
1107         ZeroMemory(&Globals.find, sizeof(Globals.find));
1108         Globals.find.lStructSize      = sizeof(Globals.find);
1109         Globals.find.hwndOwner        = Globals.hMainWnd;
1110         Globals.find.hInstance        = Globals.hInstance;
1111         Globals.find.lpstrFindWhat    = Globals.szFindText;
1112         Globals.find.wFindWhatLen     = ARRAY_SIZE(Globals.szFindText);
1113         Globals.find.lpstrReplaceWith = Globals.szReplaceText;
1114         Globals.find.wReplaceWithLen  = ARRAY_SIZE(Globals.szReplaceText);
1115         Globals.find.Flags            = FR_DOWN|FR_HIDEWHOLEWORD;
1116
1117         /* We only need to create the modal FindReplace dialog which will */
1118         /* notify us of incoming events using hMainWnd Window Messages    */
1119
1120         Globals.hFindReplaceDlg = ReplaceTextW(&Globals.find);
1121         assert(Globals.hFindReplaceDlg !=0);
1122 }
1123
1124 VOID DIALOG_HelpContents(VOID)
1125 {
1126     WinHelpW(Globals.hMainWnd, helpfileW, HELP_INDEX, 0);
1127 }
1128
1129 VOID DIALOG_HelpSearch(VOID)
1130 {
1131         /* Search Help */
1132 }
1133
1134 VOID DIALOG_HelpHelp(VOID)
1135 {
1136     WinHelpW(Globals.hMainWnd, helpfileW, HELP_HELPONHELP, 0);
1137 }
1138
1139 VOID DIALOG_HelpAboutNotepad(VOID)
1140 {
1141     static const WCHAR notepadW[] = { 'W','i','n','e',' ','N','o','t','e','p','a','d',0 };
1142     WCHAR szNotepad[MAX_STRING_LEN];
1143     HICON icon = LoadImageW(Globals.hInstance, MAKEINTRESOURCEW(IDI_NOTEPAD),
1144                             IMAGE_ICON, 48, 48, LR_SHARED);
1145
1146     LoadStringW(Globals.hInstance, STRING_NOTEPAD, szNotepad, ARRAY_SIZE(szNotepad));
1147     ShellAboutW(Globals.hMainWnd, szNotepad, notepadW, icon);
1148 }
1149
1150
1151 /***********************************************************************
1152  *
1153  *           DIALOG_FilePageSetup
1154  */
1155 VOID DIALOG_FilePageSetup(void)
1156 {
1157     DialogBoxW(Globals.hInstance, MAKEINTRESOURCEW(DIALOG_PAGESETUP),
1158                Globals.hMainWnd, DIALOG_PAGESETUP_DlgProc);
1159 }
1160
1161
1162 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1163  *
1164  *           DIALOG_PAGESETUP_DlgProc
1165  */
1166
1167 static INT_PTR WINAPI DIALOG_PAGESETUP_DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
1168 {
1169
1170    switch (msg)
1171     {
1172     case WM_COMMAND:
1173       switch (wParam)
1174         {
1175         case IDOK:
1176           /* save user input and close dialog */
1177           GetDlgItemTextW(hDlg, IDC_PAGESETUP_HEADERVALUE, Globals.szHeader, ARRAY_SIZE(Globals.szHeader));
1178           GetDlgItemTextW(hDlg, IDC_PAGESETUP_FOOTERVALUE, Globals.szFooter, ARRAY_SIZE(Globals.szFooter));
1179
1180           Globals.iMarginTop = GetDlgItemInt(hDlg, IDC_PAGESETUP_TOPVALUE, NULL, FALSE) * 100;
1181           Globals.iMarginBottom = GetDlgItemInt(hDlg, IDC_PAGESETUP_BOTTOMVALUE, NULL, FALSE) * 100;
1182           Globals.iMarginLeft = GetDlgItemInt(hDlg, IDC_PAGESETUP_LEFTVALUE, NULL, FALSE) * 100;
1183           Globals.iMarginRight = GetDlgItemInt(hDlg, IDC_PAGESETUP_RIGHTVALUE, NULL, FALSE) * 100;
1184           EndDialog(hDlg, IDOK);
1185           return TRUE;
1186
1187         case IDCANCEL:
1188           /* discard user input and close dialog */
1189           EndDialog(hDlg, IDCANCEL);
1190           return TRUE;
1191
1192         case IDHELP:
1193         {
1194           /* FIXME: Bring this to work */
1195           static const WCHAR sorryW[] = { 'S','o','r','r','y',',',' ','n','o',' ','h','e','l','p',' ','a','v','a','i','l','a','b','l','e',0 };
1196           static const WCHAR helpW[] = { 'H','e','l','p',0 };
1197           MessageBoxW(Globals.hMainWnd, sorryW, helpW, MB_ICONEXCLAMATION);
1198           return TRUE;
1199         }
1200
1201         default:
1202             break;
1203         }
1204       break;
1205
1206     case WM_INITDIALOG:
1207        /* fetch last user input prior to display dialog */
1208        SetDlgItemTextW(hDlg, IDC_PAGESETUP_HEADERVALUE, Globals.szHeader);
1209        SetDlgItemTextW(hDlg, IDC_PAGESETUP_FOOTERVALUE, Globals.szFooter);
1210        SetDlgItemInt(hDlg, IDC_PAGESETUP_TOPVALUE, Globals.iMarginTop / 100, FALSE);
1211        SetDlgItemInt(hDlg, IDC_PAGESETUP_BOTTOMVALUE, Globals.iMarginBottom / 100, FALSE);
1212        SetDlgItemInt(hDlg, IDC_PAGESETUP_LEFTVALUE, Globals.iMarginLeft / 100, FALSE);
1213        SetDlgItemInt(hDlg, IDC_PAGESETUP_RIGHTVALUE, Globals.iMarginRight / 100, FALSE);
1214        break;
1215     }
1216
1217   return FALSE;
1218 }