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