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