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