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