Sync to trunk revision 63857.
[reactos.git] / base / applications / 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 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include "notepad.h"
24
25 #include <assert.h>
26 #include <commctrl.h>
27 #include <strsafe.h>
28
29 LRESULT CALLBACK EDIT_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
30
31 static const TCHAR helpfile[] = _T("notepad.hlp");
32 static const TCHAR empty_str[] = _T("");
33 static const TCHAR szDefaultExt[] = _T("txt");
34 static const TCHAR txt_files[] = _T("*.txt");
35
36 static INT_PTR WINAPI DIALOG_PAGESETUP_DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
37
38 #ifndef UNICODE
39 static LPSTR ConvertToASCII(LPSTR pszText)
40 {
41 int sz;
42 LPWSTR pszTextW = (LPWSTR)pszText;
43
44 /* default return value */
45 pszText = NULL;
46 do {
47 /* query about requested size for conversion */
48 sz = WideCharToMultiByte(CP_ACP, 0, pszTextW, -1, NULL, 0, NULL, NULL);
49 if (!sz)
50 break;
51
52 /* get space for ASCII buffer */
53 pszText = (LPSTR)HeapAlloc(GetProcessHeap(), 0, sz);
54 if (pszText == NULL)
55 break;
56
57 /* if previous diagnostic call worked fine,
58 * then this one will work too,
59 * so no need to test return value here
60 */
61 WideCharToMultiByte(CP_ACP, 0, pszTextW, -1, pszText, sz, NULL, NULL);
62 } while (0);
63
64 HeapFree(GetProcessHeap(), 0, pszTextW);
65 return pszText;
66 }
67
68 static LPWSTR ConvertToUNICODE(LPSTR pszText, DWORD *pdwSize)
69 {
70 int sz;
71 LPWSTR pszTextW = NULL;
72
73 do {
74 /* query about requested size for conversion */
75 sz = MultiByteToWideChar(CP_ACP, 0, pszText, -1, NULL, 0);
76 if (!sz)
77 break;
78
79 /* get space for UNICODE buffer */
80 pszTextW = HeapAlloc(GetProcessHeap(), 0, sz*sizeof(WCHAR));
81 if (pszText == NULL)
82 break;
83
84 /* if previous diagnostic call worked fine,
85 * then this one will work too,
86 * so no need to test return value here
87 */
88 MultiByteToWideChar(CP_ACP, 0, pszText, -1, pszTextW, sz);
89
90 /* report the new size of the text to the caller */
91 *pdwSize = sz;
92 } while (0);
93
94 HeapFree(GetProcessHeap(), 0, pszText);
95 return pszTextW;
96 }
97 #endif
98
99 VOID ShowLastError(VOID)
100 {
101 DWORD error = GetLastError();
102 if (error != NO_ERROR)
103 {
104 LPTSTR lpMsgBuf = NULL;
105 TCHAR szTitle[MAX_STRING_LEN];
106
107 LoadString(Globals.hInstance, STRING_ERROR, szTitle, SIZEOF(szTitle));
108 FormatMessage(
109 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
110 NULL, error, 0,
111 (LPTSTR) &lpMsgBuf, 0, NULL);
112 MessageBox(NULL, lpMsgBuf, szTitle, MB_OK | MB_ICONERROR);
113 LocalFree(lpMsgBuf);
114 }
115 }
116
117 /**
118 * Sets the caption of the main window according to Globals.szFileTitle:
119 * (untitled) - Notepad if no file is open
120 * [filename] - Notepad if a file is given
121 */
122 static void UpdateWindowCaption(void)
123 {
124 TCHAR szCaption[MAX_STRING_LEN];
125 TCHAR szNotepad[MAX_STRING_LEN];
126
127 LoadString(Globals.hInstance, STRING_NOTEPAD, szNotepad, SIZEOF(szNotepad));
128
129 if (Globals.szFileTitle[0] != 0)
130 {
131 StringCchCopy(szCaption, SIZEOF(szCaption), Globals.szFileTitle);
132 }
133 else
134 {
135 LoadString(Globals.hInstance, STRING_UNTITLED, szCaption, SIZEOF(szCaption));
136 }
137
138 StringCchCat(szCaption, SIZEOF(szCaption), _T(" - "));
139 StringCchCat(szCaption, SIZEOF(szCaption), szNotepad);
140 SetWindowText(Globals.hMainWnd, szCaption);
141 }
142
143 static void AlertFileNotFound(LPCTSTR szFileName)
144 {
145 TCHAR szMessage[MAX_STRING_LEN];
146 TCHAR szResource[MAX_STRING_LEN];
147
148 /* Load and format szMessage */
149 LoadString(Globals.hInstance, STRING_NOTFOUND, szResource, SIZEOF(szResource));
150 wsprintf(szMessage, szResource, szFileName);
151
152 /* Load szCaption */
153 LoadString(Globals.hInstance, STRING_NOTEPAD, szResource, SIZEOF(szResource));
154
155 /* Display Modal Dialog */
156 MessageBox(Globals.hMainWnd, szMessage, szResource, MB_ICONEXCLAMATION);
157 }
158
159 static int AlertFileNotSaved(LPCTSTR szFileName)
160 {
161 TCHAR szMessage[MAX_STRING_LEN];
162 TCHAR szResource[MAX_STRING_LEN];
163 TCHAR szUntitled[MAX_STRING_LEN];
164
165 LoadString(Globals.hInstance, STRING_UNTITLED, szUntitled, SIZEOF(szUntitled));
166
167 /* Load and format Message */
168 LoadString(Globals.hInstance, STRING_NOTSAVED, szResource, SIZEOF(szResource));
169 wsprintf(szMessage, szResource, szFileName[0] ? szFileName : szUntitled);
170
171 /* Load Caption */
172 LoadString(Globals.hInstance, STRING_NOTEPAD, szResource, SIZEOF(szResource));
173
174 /* Display modal */
175 return MessageBox(Globals.hMainWnd, szMessage, szResource, MB_ICONEXCLAMATION|MB_YESNOCANCEL);
176 }
177
178 /**
179 * Returns:
180 * TRUE - if file exists
181 * FALSE - if file does not exist
182 */
183 BOOL FileExists(LPCTSTR szFilename)
184 {
185 WIN32_FIND_DATA entry;
186 HANDLE hFile;
187
188 hFile = FindFirstFile(szFilename, &entry);
189 FindClose(hFile);
190
191 return (hFile != INVALID_HANDLE_VALUE);
192 }
193
194 BOOL HasFileExtension(LPCTSTR szFilename)
195 {
196 LPCTSTR s;
197
198 s = _tcsrchr(szFilename, _T('\\'));
199 if (s)
200 szFilename = s;
201 return _tcsrchr(szFilename, _T('.')) != NULL;
202 }
203
204 static BOOL DoSaveFile(VOID)
205 {
206 BOOL bRet = TRUE;
207 HANDLE hFile;
208 LPTSTR pTemp;
209 DWORD size;
210
211 hFile = CreateFile(Globals.szFileName, GENERIC_WRITE, FILE_SHARE_WRITE,
212 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
213 if(hFile == INVALID_HANDLE_VALUE)
214 {
215 ShowLastError();
216 return FALSE;
217 }
218
219 size = GetWindowTextLength(Globals.hEdit) + 1;
220 pTemp = HeapAlloc(GetProcessHeap(), 0, size * sizeof(*pTemp));
221 if (!pTemp)
222 {
223 CloseHandle(hFile);
224 ShowLastError();
225 return FALSE;
226 }
227 size = GetWindowText(Globals.hEdit, pTemp, size);
228
229 #ifndef UNICODE
230 pTemp = (LPTSTR)ConvertToUNICODE(pTemp, &size);
231 if (!pTemp)
232 {
233 /* original "pTemp" already freed */
234 CloseHandle(hFile);
235 ShowLastError();
236 return FALSE;
237 }
238 #endif
239
240 if (size)
241 {
242 if (!WriteText(hFile, (LPWSTR)pTemp, size, Globals.iEncoding, Globals.iEoln))
243 {
244 ShowLastError();
245 bRet = FALSE;
246 }
247 else
248 {
249 SendMessage(Globals.hEdit, EM_SETMODIFY, FALSE, 0);
250 bRet = TRUE;
251 }
252 }
253
254 CloseHandle(hFile);
255 HeapFree(GetProcessHeap(), 0, pTemp);
256 return bRet;
257 }
258
259 /**
260 * Returns:
261 * TRUE - User agreed to close (both save/don't save)
262 * FALSE - User cancelled close by selecting "Cancel"
263 */
264 BOOL DoCloseFile(VOID)
265 {
266 int nResult;
267
268 if (SendMessage(Globals.hEdit, EM_GETMODIFY, 0, 0))
269 {
270 /* prompt user to save changes */
271 nResult = AlertFileNotSaved(Globals.szFileName);
272 switch (nResult)
273 {
274 case IDYES:
275 if(!DIALOG_FileSave())
276 return FALSE;
277 break;
278
279 case IDNO:
280 break;
281
282 case IDCANCEL:
283 return FALSE;
284
285 default:
286 return FALSE;
287 }
288 }
289
290 SetFileName(empty_str);
291 UpdateWindowCaption();
292
293 return TRUE;
294 }
295
296 VOID DoOpenFile(LPCTSTR szFileName)
297 {
298 static const TCHAR dotlog[] = _T(".LOG");
299 HANDLE hFile;
300 LPTSTR pszText = NULL;
301 DWORD dwTextLen;
302 TCHAR log[5];
303
304 /* Close any files and prompt to save changes */
305 if (!DoCloseFile())
306 return;
307
308 hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
309 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
310 if (hFile == INVALID_HANDLE_VALUE)
311 {
312 ShowLastError();
313 goto done;
314 }
315
316 if (!ReadText(hFile, (LPWSTR *)&pszText, &dwTextLen, &Globals.iEncoding, &Globals.iEoln))
317 {
318 ShowLastError();
319 goto done;
320 }
321 #ifndef UNICODE
322 pszText = ConvertToASCII(pszText);
323 if (pszText == NULL) {
324 ShowLastError();
325 goto done;
326 }
327 #endif
328 SetWindowText(Globals.hEdit, pszText);
329
330 SendMessage(Globals.hEdit, EM_SETMODIFY, FALSE, 0);
331 SendMessage(Globals.hEdit, EM_EMPTYUNDOBUFFER, 0, 0);
332 SetFocus(Globals.hEdit);
333
334 /* If the file starts with .LOG, add a time/date at the end and set cursor after
335 * See http://support.microsoft.com/?kbid=260563
336 */
337 if (GetWindowText(Globals.hEdit, log, SIZEOF(log)) && !_tcscmp(log, dotlog))
338 {
339 static const TCHAR lf[] = _T("\r\n");
340 SendMessage(Globals.hEdit, EM_SETSEL, GetWindowTextLength(Globals.hEdit), -1);
341 SendMessage(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)lf);
342 DIALOG_EditTimeDate();
343 SendMessage(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)lf);
344 }
345
346 SetFileName(szFileName);
347 UpdateWindowCaption();
348 NOTEPAD_EnableSearchMenu();
349 done:
350 if (hFile != INVALID_HANDLE_VALUE)
351 CloseHandle(hFile);
352 if (pszText)
353 HeapFree(GetProcessHeap(), 0, pszText);
354 }
355
356 VOID DIALOG_FileNew(VOID)
357 {
358 /* Close any files and prompt to save changes */
359 if (DoCloseFile()) {
360 SetWindowText(Globals.hEdit, empty_str);
361 SendMessage(Globals.hEdit, EM_EMPTYUNDOBUFFER, 0, 0);
362 SetFocus(Globals.hEdit);
363 NOTEPAD_EnableSearchMenu();
364 }
365 }
366
367 VOID DIALOG_FileOpen(VOID)
368 {
369 OPENFILENAME openfilename;
370 TCHAR szDir[MAX_PATH];
371 TCHAR szPath[MAX_PATH];
372
373 ZeroMemory(&openfilename, sizeof(openfilename));
374
375 GetCurrentDirectory(SIZEOF(szDir), szDir);
376 if (Globals.szFileName[0] == 0)
377 _tcscpy(szPath, txt_files);
378 else
379 _tcscpy(szPath, Globals.szFileName);
380
381 openfilename.lStructSize = sizeof(openfilename);
382 openfilename.hwndOwner = Globals.hMainWnd;
383 openfilename.hInstance = Globals.hInstance;
384 openfilename.lpstrFilter = Globals.szFilter;
385 openfilename.lpstrFile = szPath;
386 openfilename.nMaxFile = SIZEOF(szPath);
387 openfilename.lpstrInitialDir = szDir;
388 openfilename.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST |
389 OFN_HIDEREADONLY;
390 openfilename.lpstrDefExt = szDefaultExt;
391
392
393 if (GetOpenFileName(&openfilename)) {
394 if (FileExists(openfilename.lpstrFile))
395 DoOpenFile(openfilename.lpstrFile);
396 else
397 AlertFileNotFound(openfilename.lpstrFile);
398 }
399 }
400
401
402 BOOL DIALOG_FileSave(VOID)
403 {
404 if (Globals.szFileName[0] == 0)
405 return DIALOG_FileSaveAs();
406 else
407 return DoSaveFile();
408 }
409
410 static UINT_PTR CALLBACK DIALOG_FileSaveAs_Hook(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
411 {
412 TCHAR szText[128];
413 HWND hCombo;
414
415 UNREFERENCED_PARAMETER(wParam);
416
417 switch(msg)
418 {
419 case WM_INITDIALOG:
420 hCombo = GetDlgItem(hDlg, ID_ENCODING);
421
422 LoadString(Globals.hInstance, STRING_ANSI, szText, SIZEOF(szText));
423 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
424
425 LoadString(Globals.hInstance, STRING_UNICODE, szText, SIZEOF(szText));
426 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
427
428 LoadString(Globals.hInstance, STRING_UNICODE_BE, szText, SIZEOF(szText));
429 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
430
431 LoadString(Globals.hInstance, STRING_UTF8, szText, SIZEOF(szText));
432 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
433
434 SendMessage(hCombo, CB_SETCURSEL, Globals.iEncoding, 0);
435
436 hCombo = GetDlgItem(hDlg, ID_EOLN);
437
438 LoadString(Globals.hInstance, STRING_CRLF, szText, SIZEOF(szText));
439 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
440
441 LoadString(Globals.hInstance, STRING_LF, szText, SIZEOF(szText));
442 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
443
444 LoadString(Globals.hInstance, STRING_CR, szText, SIZEOF(szText));
445 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
446
447 SendMessage(hCombo, CB_SETCURSEL, Globals.iEoln, 0);
448 break;
449
450 case WM_NOTIFY:
451 if (((NMHDR *) lParam)->code == CDN_FILEOK)
452 {
453 hCombo = GetDlgItem(hDlg, ID_ENCODING);
454 if (hCombo)
455 Globals.iEncoding = (int) SendMessage(hCombo, CB_GETCURSEL, 0, 0);
456
457 hCombo = GetDlgItem(hDlg, ID_EOLN);
458 if (hCombo)
459 Globals.iEoln = (int) SendMessage(hCombo, CB_GETCURSEL, 0, 0);
460 }
461 break;
462 }
463 return 0;
464 }
465
466 BOOL DIALOG_FileSaveAs(VOID)
467 {
468 OPENFILENAME saveas;
469 TCHAR szDir[MAX_PATH];
470 TCHAR szPath[MAX_PATH];
471
472 ZeroMemory(&saveas, sizeof(saveas));
473
474 GetCurrentDirectory(SIZEOF(szDir), szDir);
475 if (Globals.szFileName[0] == 0)
476 _tcscpy(szPath, txt_files);
477 else
478 _tcscpy(szPath, Globals.szFileName);
479
480 saveas.lStructSize = sizeof(OPENFILENAME);
481 saveas.hwndOwner = Globals.hMainWnd;
482 saveas.hInstance = Globals.hInstance;
483 saveas.lpstrFilter = Globals.szFilter;
484 saveas.lpstrFile = szPath;
485 saveas.nMaxFile = SIZEOF(szPath);
486 saveas.lpstrInitialDir = szDir;
487 saveas.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT |
488 OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK;
489 saveas.lpstrDefExt = szDefaultExt;
490 saveas.lpTemplateName = MAKEINTRESOURCE(DIALOG_ENCODING);
491 saveas.lpfnHook = DIALOG_FileSaveAs_Hook;
492
493 if (GetSaveFileName(&saveas))
494 {
495 // HACK: Because in ROS, Save-As boxes don't check the validity
496 // of file names and thus, here, szPath can be invalid !! We only
497 // see its validity when we call DoSaveFile()...
498 SetFileName(szPath);
499 if (DoSaveFile())
500 {
501 UpdateWindowCaption();
502 return TRUE;
503 }
504 else
505 {
506 SetFileName(_T(""));
507 return FALSE;
508 }
509 }
510 else
511 {
512 return FALSE;
513 }
514 }
515
516 VOID DIALOG_FilePrint(VOID)
517 {
518 DOCINFO di;
519 PRINTDLG printer;
520 SIZE szMetric;
521 int cWidthPels, cHeightPels, border;
522 int xLeft, yTop, pagecount, dopage, copycount;
523 unsigned int i;
524 LOGFONT hdrFont;
525 HFONT font, old_font=0;
526 DWORD size;
527 LPTSTR pTemp;
528 static const TCHAR times_new_roman[] = _T("Times New Roman");
529
530 /* Get a small font and print some header info on each page */
531 ZeroMemory(&hdrFont, sizeof(hdrFont));
532 hdrFont.lfHeight = 100;
533 hdrFont.lfWeight = FW_BOLD;
534 hdrFont.lfCharSet = ANSI_CHARSET;
535 hdrFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
536 hdrFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
537 hdrFont.lfQuality = PROOF_QUALITY;
538 hdrFont.lfPitchAndFamily = VARIABLE_PITCH | FF_ROMAN;
539 _tcscpy(hdrFont.lfFaceName, times_new_roman);
540
541 font = CreateFontIndirect(&hdrFont);
542
543 /* Get Current Settings */
544 ZeroMemory(&printer, sizeof(printer));
545 printer.lStructSize = sizeof(printer);
546 printer.hwndOwner = Globals.hMainWnd;
547 printer.hInstance = Globals.hInstance;
548
549 /* Set some default flags */
550 printer.Flags = PD_RETURNDC;
551 printer.nFromPage = 0;
552 printer.nMinPage = 1;
553 /* we really need to calculate number of pages to set nMaxPage and nToPage */
554 printer.nToPage = 0;
555 printer.nMaxPage = (WORD) -1;
556
557 /* Let commdlg manage copy settings */
558 printer.nCopies = (WORD)PD_USEDEVMODECOPIES;
559
560 if (!PrintDlg(&printer)) return;
561
562 assert(printer.hDC != 0);
563
564 /* initialize DOCINFO */
565 di.cbSize = sizeof(DOCINFO);
566 di.lpszDocName = Globals.szFileTitle;
567 di.lpszOutput = NULL;
568 di.lpszDatatype = NULL;
569 di.fwType = 0;
570
571 if (StartDoc(printer.hDC, &di) <= 0) return;
572
573 /* Get the page dimensions in pixels. */
574 cWidthPels = GetDeviceCaps(printer.hDC, HORZRES);
575 cHeightPels = GetDeviceCaps(printer.hDC, VERTRES);
576
577 /* Get the file text */
578 size = GetWindowTextLength(Globals.hEdit) + 1;
579 pTemp = HeapAlloc(GetProcessHeap(), 0, size * sizeof(TCHAR));
580 if (!pTemp)
581 {
582 ShowLastError();
583 return;
584 }
585 size = GetWindowText(Globals.hEdit, pTemp, size);
586
587 border = 150;
588 for (copycount=1; copycount <= printer.nCopies; copycount++) {
589 i = 0;
590 pagecount = 1;
591 do {
592 static const TCHAR letterM[] = _T("M");
593
594 if (pagecount >= printer.nFromPage &&
595 /* ((printer.Flags & PD_PAGENUMS) == 0 || pagecount <= printer.nToPage))*/
596 pagecount <= printer.nToPage)
597 dopage = 1;
598 else
599 dopage = 0;
600
601 old_font = SelectObject(printer.hDC, font);
602 GetTextExtentPoint32(printer.hDC, letterM, 1, &szMetric);
603
604 if (dopage) {
605 if (StartPage(printer.hDC) <= 0) {
606 static const TCHAR failed[] = _T("StartPage failed");
607 static const TCHAR error[] = _T("Print Error");
608 MessageBox(Globals.hMainWnd, failed, error, MB_ICONEXCLAMATION);
609 return;
610 }
611 /* Write a rectangle and header at the top of each page */
612 Rectangle(printer.hDC, border, border, cWidthPels-border, border+szMetric.cy*2);
613 /* I don't know what's up with this TextOut command. This comes out
614 kind of mangled.
615 */
616 TextOut(printer.hDC, border*2, border+szMetric.cy/2, Globals.szFileTitle, lstrlen(Globals.szFileTitle));
617 }
618
619 /* The starting point for the main text */
620 xLeft = border*2;
621 yTop = border+szMetric.cy*4;
622
623 SelectObject(printer.hDC, old_font);
624 GetTextExtentPoint32(printer.hDC, letterM, 1, &szMetric);
625
626 /* Since outputting strings is giving me problems, output the main
627 text one character at a time.
628 */
629 do {
630 if (pTemp[i] == '\n') {
631 xLeft = border*2;
632 yTop += szMetric.cy;
633 }
634 else if (pTemp[i] != '\r') {
635 if (dopage)
636 TextOut(printer.hDC, xLeft, yTop, &pTemp[i], 1);
637 xLeft += szMetric.cx;
638 }
639 } while (i++<size && yTop<(cHeightPels-border*2));
640
641 if (dopage)
642 EndPage(printer.hDC);
643 pagecount++;
644 } while (i<size);
645 }
646
647 EndDoc(printer.hDC);
648 DeleteDC(printer.hDC);
649 HeapFree(GetProcessHeap(), 0, pTemp);
650 }
651
652 VOID DIALOG_FilePrinterSetup(VOID)
653 {
654 PRINTDLG printer;
655
656 ZeroMemory(&printer, sizeof(printer));
657 printer.lStructSize = sizeof(printer);
658 printer.hwndOwner = Globals.hMainWnd;
659 printer.hInstance = Globals.hInstance;
660 printer.Flags = PD_PRINTSETUP;
661 printer.nCopies = 1;
662
663 PrintDlg(&printer);
664 }
665
666 VOID DIALOG_FileExit(VOID)
667 {
668 PostMessage(Globals.hMainWnd, WM_CLOSE, 0, 0l);
669 }
670
671 VOID DIALOG_EditUndo(VOID)
672 {
673 SendMessage(Globals.hEdit, EM_UNDO, 0, 0);
674 }
675
676 VOID DIALOG_EditCut(VOID)
677 {
678 SendMessage(Globals.hEdit, WM_CUT, 0, 0);
679 }
680
681 VOID DIALOG_EditCopy(VOID)
682 {
683 SendMessage(Globals.hEdit, WM_COPY, 0, 0);
684 }
685
686 VOID DIALOG_EditPaste(VOID)
687 {
688 SendMessage(Globals.hEdit, WM_PASTE, 0, 0);
689 }
690
691 VOID DIALOG_EditDelete(VOID)
692 {
693 SendMessage(Globals.hEdit, WM_CLEAR, 0, 0);
694 }
695
696 VOID DIALOG_EditSelectAll(VOID)
697 {
698 SendMessage(Globals.hEdit, EM_SETSEL, 0, (LPARAM)-1);
699 }
700
701 VOID DIALOG_EditTimeDate(VOID)
702 {
703 SYSTEMTIME st;
704 TCHAR szDate[MAX_STRING_LEN];
705 TCHAR szText[MAX_STRING_LEN * 2 + 2];
706
707 GetLocalTime(&st);
708
709 GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, szDate, MAX_STRING_LEN);
710 _tcscpy(szText, szDate);
711 _tcscat(szText, _T(" "));
712 GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, szDate, MAX_STRING_LEN);
713 _tcscat(szText, szDate);
714 SendMessage(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)szText);
715 }
716
717 VOID DoCreateStatusBar(VOID)
718 {
719 RECT rc;
720 RECT rcstatus;
721 BOOL bStatusBarVisible;
722
723 // Check if status bar object already exists.
724 if (Globals.hStatusBar == NULL)
725 {
726 // Try to create the status bar
727 Globals.hStatusBar = CreateStatusWindow(
728 WS_CHILD | WS_VISIBLE | WS_EX_STATICEDGE,
729 NULL,
730 Globals.hMainWnd,
731 CMD_STATUSBAR_WND_ID);
732
733 if (Globals.hStatusBar == NULL)
734 {
735 ShowLastError();
736 return;
737 }
738
739 // Load the string for formatting column/row text output
740 LoadString(Globals.hInstance, STRING_LINE_COLUMN, Globals.szStatusBarLineCol, MAX_PATH-1);
741
742 // Set the status bar for single-text output
743 SendMessage(Globals.hStatusBar, SB_SIMPLE, (WPARAM)TRUE, (LPARAM)0);
744 }
745
746 // Set status bar visible or not accordind the the settings.
747 if (Globals.bWrapLongLines == TRUE ||
748 Globals.bShowStatusBar == FALSE)
749 {
750 bStatusBarVisible = FALSE;
751 ShowWindow(Globals.hStatusBar, SW_HIDE);
752 }
753 else
754 {
755 bStatusBarVisible = TRUE;
756 ShowWindow(Globals.hStatusBar, SW_SHOW);
757 SendMessage(Globals.hStatusBar, WM_SIZE, 0, 0);
758 }
759
760 // Set check state in show status bar item.
761 if (Globals.bShowStatusBar == TRUE)
762 {
763 CheckMenuItem(Globals.hMenu, CMD_STATUSBAR, MF_BYCOMMAND | MF_CHECKED);
764 }
765 else
766 {
767 CheckMenuItem(Globals.hMenu, CMD_STATUSBAR, MF_BYCOMMAND | MF_UNCHECKED);
768 }
769
770 // Update menu mar with the previous changes
771 DrawMenuBar(Globals.hMainWnd);
772
773 // Sefety test is edit control exists
774 if (Globals.hEdit != NULL)
775 {
776 // Retrieve the sizes of the controls
777 GetClientRect(Globals.hMainWnd, &rc);
778 GetClientRect(Globals.hStatusBar, &rcstatus);
779
780 // If status bar is currently visible, update dimensions of edir control
781 if (bStatusBarVisible)
782 rc.bottom -= (rcstatus.bottom - rcstatus.top);
783
784 // Resize edit control to right size.
785 MoveWindow(Globals.hEdit, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
786 }
787
788 // Update content with current row/column text
789 DIALOG_StatusBarUpdateCaretPos();
790 }
791
792 VOID DoCreateEditWindow(VOID)
793 {
794 DWORD dwStyle;
795 int iSize;
796 LPTSTR pTemp = NULL;
797 BOOL bModified = FALSE;
798
799 iSize = 0;
800
801 // If the edit control already exists, try to save its content
802 if (Globals.hEdit != NULL)
803 {
804 // number of chars currently written into the editor.
805 iSize = GetWindowTextLength(Globals.hEdit);
806
807 if (iSize)
808 {
809 // Allocates temporary buffer.
810 pTemp = HeapAlloc(GetProcessHeap(), 0, (iSize + 1) * sizeof(TCHAR));
811
812 if (!pTemp)
813 {
814 ShowLastError();
815 return;
816 }
817
818 // Recover the text into the control.
819 GetWindowText(Globals.hEdit, pTemp, iSize + 1);
820
821 if (SendMessage(Globals.hEdit, EM_GETMODIFY, 0, 0))
822 bModified = TRUE;
823 }
824
825 // Restore original window procedure
826 SetWindowLongPtr(Globals.hEdit, GWLP_WNDPROC, (LONG_PTR)Globals.EditProc);
827
828 // Destroy the edit control
829 DestroyWindow(Globals.hEdit);
830 }
831
832 // Update wrap status into the main menu and recover style flags
833 if (Globals.bWrapLongLines)
834 {
835 dwStyle = EDIT_STYLE_WRAP;
836 EnableMenuItem(Globals.hMenu, CMD_STATUSBAR, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
837 } else {
838 dwStyle = EDIT_STYLE;
839 EnableMenuItem(Globals.hMenu, CMD_STATUSBAR, MF_BYCOMMAND | MF_ENABLED);
840 }
841
842 // Update previous changes
843 DrawMenuBar(Globals.hMainWnd);
844
845 // Create the new edit control
846 Globals.hEdit = CreateWindowEx(
847 WS_EX_CLIENTEDGE,
848 EDIT_CLASS,
849 NULL,
850 dwStyle,
851 CW_USEDEFAULT,
852 CW_USEDEFAULT,
853 CW_USEDEFAULT,
854 CW_USEDEFAULT,
855 Globals.hMainWnd,
856 NULL,
857 Globals.hInstance,
858 NULL);
859
860 if (Globals.hEdit == NULL)
861 {
862 ShowLastError();
863 return;
864 }
865
866 SendMessage(Globals.hEdit, WM_SETFONT, (WPARAM)Globals.hFont, FALSE);
867 SendMessage(Globals.hEdit, EM_LIMITTEXT, 0, 0);
868
869 // If some text was previously saved, restore it.
870 if (iSize != 0)
871 {
872 SetWindowText(Globals.hEdit, pTemp);
873 HeapFree(GetProcessHeap(), 0, pTemp);
874
875 if (bModified)
876 SendMessage(Globals.hEdit, EM_SETMODIFY, TRUE, 0);
877 }
878
879 // Sub-class a new window callback for row/column detection.
880 Globals.EditProc = (WNDPROC) SetWindowLongPtr(Globals.hEdit, GWLP_WNDPROC, (LONG_PTR)EDIT_WndProc);
881
882 // Create/update status bar
883 DoCreateStatusBar();
884
885 // Finally shows new edit control and set focus into it.
886 ShowWindow(Globals.hEdit, SW_SHOW);
887 SetFocus(Globals.hEdit);
888 }
889
890 VOID DIALOG_EditWrap(VOID)
891 {
892 Globals.bWrapLongLines = !Globals.bWrapLongLines;
893
894 DoCreateEditWindow();
895 }
896
897 VOID DIALOG_SelectFont(VOID)
898 {
899 CHOOSEFONT cf;
900 LOGFONT lf=Globals.lfFont;
901
902 ZeroMemory( &cf, sizeof(cf) );
903 cf.lStructSize=sizeof(cf);
904 cf.hwndOwner=Globals.hMainWnd;
905 cf.lpLogFont=&lf;
906 cf.Flags=CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT;
907
908 if( ChooseFont(&cf) )
909 {
910 HFONT currfont=Globals.hFont;
911
912 Globals.hFont=CreateFontIndirect( &lf );
913 Globals.lfFont=lf;
914 SendMessage( Globals.hEdit, WM_SETFONT, (WPARAM)Globals.hFont, (LPARAM)TRUE );
915 if( currfont!=NULL )
916 DeleteObject( currfont );
917 }
918 }
919
920 typedef HWND (WINAPI *FINDPROC)(LPFINDREPLACE lpfr);
921
922 static VOID DIALOG_SearchDialog(FINDPROC pfnProc)
923 {
924 ZeroMemory(&Globals.find, sizeof(Globals.find));
925 Globals.find.lStructSize = sizeof(Globals.find);
926 Globals.find.hwndOwner = Globals.hMainWnd;
927 Globals.find.hInstance = Globals.hInstance;
928 Globals.find.lpstrFindWhat = Globals.szFindText;
929 Globals.find.wFindWhatLen = SIZEOF(Globals.szFindText);
930 Globals.find.lpstrReplaceWith = Globals.szReplaceText;
931 Globals.find.wReplaceWithLen = SIZEOF(Globals.szReplaceText);
932 Globals.find.Flags = FR_DOWN;
933
934 /* We only need to create the modal FindReplace dialog which will */
935 /* notify us of incoming events using hMainWnd Window Messages */
936
937 Globals.hFindReplaceDlg = pfnProc(&Globals.find);
938 assert(Globals.hFindReplaceDlg !=0);
939 }
940
941 VOID DIALOG_Search(VOID)
942 {
943 DIALOG_SearchDialog(FindText);
944 }
945
946 VOID DIALOG_SearchNext(VOID)
947 {
948 if (Globals.find.lpstrFindWhat != NULL)
949 NOTEPAD_FindNext(&Globals.find, FALSE, TRUE);
950 else
951 DIALOG_Search();
952 }
953
954 VOID DIALOG_Replace(VOID)
955 {
956 DIALOG_SearchDialog(ReplaceText);
957 }
958
959 static INT_PTR CALLBACK DIALOG_GoTo_DialogProc(HWND hwndDialog, UINT uMsg, WPARAM wParam, LPARAM lParam)
960 {
961 BOOL bResult = FALSE;
962 HWND hTextBox;
963 TCHAR szText[32];
964
965 switch(uMsg) {
966 case WM_INITDIALOG:
967 hTextBox = GetDlgItem(hwndDialog, ID_LINENUMBER);
968 _sntprintf(szText, SIZEOF(szText), _T("%ld"), lParam);
969 SetWindowText(hTextBox, szText);
970 break;
971 case WM_COMMAND:
972 if (HIWORD(wParam) == BN_CLICKED)
973 {
974 if (LOWORD(wParam) == IDOK)
975 {
976 hTextBox = GetDlgItem(hwndDialog, ID_LINENUMBER);
977 GetWindowText(hTextBox, szText, SIZEOF(szText));
978 EndDialog(hwndDialog, _ttoi(szText));
979 bResult = TRUE;
980 }
981 else if (LOWORD(wParam) == IDCANCEL)
982 {
983 EndDialog(hwndDialog, 0);
984 bResult = TRUE;
985 }
986 }
987 break;
988 }
989
990 return bResult;
991 }
992
993 VOID DIALOG_GoTo(VOID)
994 {
995 INT_PTR nLine;
996 LPTSTR pszText;
997 int nLength, i;
998 DWORD dwStart, dwEnd;
999
1000 nLength = GetWindowTextLength(Globals.hEdit);
1001 pszText = (LPTSTR) HeapAlloc(GetProcessHeap(), 0, (nLength + 1) * sizeof(*pszText));
1002 if (!pszText)
1003 return;
1004
1005 /* Retrieve current text */
1006 GetWindowText(Globals.hEdit, pszText, nLength + 1);
1007 SendMessage(Globals.hEdit, EM_GETSEL, (WPARAM) &dwStart, (LPARAM) &dwEnd);
1008
1009 nLine = 1;
1010 for (i = 0; pszText[i] && (i < (int) dwStart); i++)
1011 {
1012 if (pszText[i] == '\n')
1013 nLine++;
1014 }
1015
1016 nLine = DialogBoxParam(Globals.hInstance, MAKEINTRESOURCE(DIALOG_GOTO),
1017 Globals.hMainWnd, DIALOG_GoTo_DialogProc, nLine);
1018
1019 if (nLine >= 1)
1020 {
1021 for (i = 0; pszText[i] && (nLine > 1) && (i < nLength - 1); i++)
1022 {
1023 if (pszText[i] == '\n')
1024 nLine--;
1025 }
1026 SendMessage(Globals.hEdit, EM_SETSEL, i, i);
1027 SendMessage(Globals.hEdit, EM_SCROLLCARET, 0, 0);
1028 }
1029 HeapFree(GetProcessHeap(), 0, pszText);
1030 }
1031
1032 VOID DIALOG_StatusBarUpdateCaretPos(VOID)
1033 {
1034 int line, col;
1035 TCHAR buff[MAX_PATH];
1036 DWORD dwStart, dwSize;
1037
1038 SendMessage(Globals.hEdit, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwSize);
1039 line = SendMessage(Globals.hEdit, EM_LINEFROMCHAR, (WPARAM)dwStart, 0);
1040 col = dwStart - SendMessage(Globals.hEdit, EM_LINEINDEX, (WPARAM)line, 0);
1041
1042 _stprintf(buff, Globals.szStatusBarLineCol, line+1, col+1);
1043 SendMessage(Globals.hStatusBar, SB_SETTEXT, SB_SIMPLEID, (LPARAM)buff);
1044 }
1045
1046 VOID DIALOG_ViewStatusBar(VOID)
1047 {
1048 Globals.bShowStatusBar = !Globals.bShowStatusBar;
1049
1050 DoCreateStatusBar();
1051 }
1052
1053 VOID DIALOG_HelpContents(VOID)
1054 {
1055 WinHelp(Globals.hMainWnd, helpfile, HELP_INDEX, 0);
1056 }
1057
1058 VOID DIALOG_HelpSearch(VOID)
1059 {
1060 /* Search Help */
1061 }
1062
1063 VOID DIALOG_HelpHelp(VOID)
1064 {
1065 WinHelp(Globals.hMainWnd, helpfile, HELP_HELPONHELP, 0);
1066 }
1067
1068 #ifdef _MSC_VER
1069 #pragma warning(disable : 4100)
1070 #endif
1071 INT_PTR CALLBACK
1072 AboutDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
1073 {
1074 HWND hLicenseEditWnd;
1075 TCHAR *strLicense;
1076
1077 switch (message)
1078 {
1079 case WM_INITDIALOG:
1080
1081 hLicenseEditWnd = GetDlgItem(hDlg, IDC_LICENSE);
1082
1083 /* 0x1000 should be enought */
1084 strLicense = (TCHAR *)_alloca(0x1000);
1085 LoadString(GetModuleHandle(NULL), STRING_LICENSE, strLicense, 0x1000);
1086
1087 SetWindowText(hLicenseEditWnd, strLicense);
1088
1089 return TRUE;
1090
1091 case WM_COMMAND:
1092
1093 if ((LOWORD(wParam) == IDOK) || (LOWORD(wParam) == IDCANCEL))
1094 {
1095 EndDialog(hDlg, LOWORD(wParam));
1096 return TRUE;
1097 }
1098
1099 break;
1100 }
1101
1102 return 0;
1103 }
1104
1105
1106
1107 VOID DIALOG_HelpAboutWine(VOID)
1108 {
1109 TCHAR szNotepad[MAX_STRING_LEN];
1110 HICON notepadIcon = LoadIcon(Globals.hInstance, MAKEINTRESOURCE(IDI_NPICON));
1111
1112 LoadString(Globals.hInstance, STRING_NOTEPAD, szNotepad, SIZEOF(szNotepad));
1113 ShellAbout(Globals.hMainWnd, szNotepad, 0, notepadIcon);
1114 DeleteObject(notepadIcon);
1115 }
1116
1117
1118 /***********************************************************************
1119 *
1120 * DIALOG_FilePageSetup
1121 */
1122 VOID DIALOG_FilePageSetup(void)
1123 {
1124 DialogBox(Globals.hInstance, MAKEINTRESOURCE(DIALOG_PAGESETUP),
1125 Globals.hMainWnd, DIALOG_PAGESETUP_DlgProc);
1126 }
1127
1128
1129 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1130 *
1131 * DIALOG_PAGESETUP_DlgProc
1132 */
1133
1134 static INT_PTR WINAPI DIALOG_PAGESETUP_DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
1135 {
1136
1137 switch (msg)
1138 {
1139 case WM_COMMAND:
1140 if (HIWORD(wParam) == BN_CLICKED)
1141 {
1142 switch (LOWORD(wParam))
1143 {
1144 case IDOK:
1145 /* save user input and close dialog */
1146 GetDlgItemText(hDlg, 0x141, Globals.szHeader, SIZEOF(Globals.szHeader));
1147 GetDlgItemText(hDlg, 0x143, Globals.szFooter, SIZEOF(Globals.szFooter));
1148 GetDlgItemText(hDlg, 0x14A, Globals.szMarginTop, SIZEOF(Globals.szMarginTop));
1149 GetDlgItemText(hDlg, 0x150, Globals.szMarginBottom, SIZEOF(Globals.szMarginBottom));
1150 GetDlgItemText(hDlg, 0x147, Globals.szMarginLeft, SIZEOF(Globals.szMarginLeft));
1151 GetDlgItemText(hDlg, 0x14D, Globals.szMarginRight, SIZEOF(Globals.szMarginRight));
1152 EndDialog(hDlg, IDOK);
1153 return TRUE;
1154
1155 case IDCANCEL:
1156 /* discard user input and close dialog */
1157 EndDialog(hDlg, IDCANCEL);
1158 return TRUE;
1159
1160 case IDHELP:
1161 {
1162 /* FIXME: Bring this to work */
1163 static const TCHAR sorry[] = _T("Sorry, no help available");
1164 static const TCHAR help[] = _T("Help");
1165 MessageBox(Globals.hMainWnd, sorry, help, MB_ICONEXCLAMATION);
1166 return TRUE;
1167 }
1168
1169 default:
1170 break;
1171 }
1172 }
1173 break;
1174
1175 case WM_INITDIALOG:
1176 /* fetch last user input prior to display dialog */
1177 SetDlgItemText(hDlg, 0x141, Globals.szHeader);
1178 SetDlgItemText(hDlg, 0x143, Globals.szFooter);
1179 SetDlgItemText(hDlg, 0x14A, Globals.szMarginTop);
1180 SetDlgItemText(hDlg, 0x150, Globals.szMarginBottom);
1181 SetDlgItemText(hDlg, 0x147, Globals.szMarginLeft);
1182 SetDlgItemText(hDlg, 0x14D, Globals.szMarginRight);
1183 break;
1184 }
1185
1186 return FALSE;
1187 }