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