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