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