[NOTEPAD] Printer output improvements by Ricardo Hanke. See CORE-9088 for more details.
[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 szPath[MAX_PATH];
377
378 ZeroMemory(&openfilename, sizeof(openfilename));
379
380 if (Globals.szFileName[0] == 0)
381 _tcscpy(szPath, txt_files);
382 else
383 _tcscpy(szPath, Globals.szFileName);
384
385 openfilename.lStructSize = sizeof(openfilename);
386 openfilename.hwndOwner = Globals.hMainWnd;
387 openfilename.hInstance = Globals.hInstance;
388 openfilename.lpstrFilter = Globals.szFilter;
389 openfilename.lpstrFile = szPath;
390 openfilename.nMaxFile = ARRAY_SIZE(szPath);
391 openfilename.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
392 openfilename.lpstrDefExt = szDefaultExt;
393
394 if (GetOpenFileName(&openfilename)) {
395 if (FileExists(openfilename.lpstrFile))
396 DoOpenFile(openfilename.lpstrFile);
397 else
398 AlertFileNotFound(openfilename.lpstrFile);
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
411 CALLBACK
412 DIALOG_FileSaveAs_Hook(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
413 {
414 TCHAR szText[128];
415 HWND hCombo;
416
417 UNREFERENCED_PARAMETER(wParam);
418
419 switch(msg)
420 {
421 case WM_INITDIALOG:
422 hCombo = GetDlgItem(hDlg, ID_ENCODING);
423
424 LoadString(Globals.hInstance, STRING_ANSI, szText, ARRAY_SIZE(szText));
425 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
426
427 LoadString(Globals.hInstance, STRING_UNICODE, szText, ARRAY_SIZE(szText));
428 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
429
430 LoadString(Globals.hInstance, STRING_UNICODE_BE, szText, ARRAY_SIZE(szText));
431 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
432
433 LoadString(Globals.hInstance, STRING_UTF8, szText, ARRAY_SIZE(szText));
434 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
435
436 SendMessage(hCombo, CB_SETCURSEL, Globals.encFile, 0);
437
438 hCombo = GetDlgItem(hDlg, ID_EOLN);
439
440 LoadString(Globals.hInstance, STRING_CRLF, szText, ARRAY_SIZE(szText));
441 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
442
443 LoadString(Globals.hInstance, STRING_LF, szText, ARRAY_SIZE(szText));
444 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
445
446 LoadString(Globals.hInstance, STRING_CR, szText, ARRAY_SIZE(szText));
447 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
448
449 SendMessage(hCombo, CB_SETCURSEL, Globals.iEoln, 0);
450 break;
451
452 case WM_NOTIFY:
453 if (((NMHDR *) lParam)->code == CDN_FILEOK)
454 {
455 hCombo = GetDlgItem(hDlg, ID_ENCODING);
456 if (hCombo)
457 Globals.encFile = (int) SendMessage(hCombo, CB_GETCURSEL, 0, 0);
458
459 hCombo = GetDlgItem(hDlg, ID_EOLN);
460 if (hCombo)
461 Globals.iEoln = (int) SendMessage(hCombo, CB_GETCURSEL, 0, 0);
462 }
463 break;
464 }
465 return 0;
466 }
467
468 BOOL DIALOG_FileSaveAs(VOID)
469 {
470 OPENFILENAME saveas;
471 TCHAR szPath[MAX_PATH];
472
473 ZeroMemory(&saveas, sizeof(saveas));
474
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 = ARRAY_SIZE(szPath);
486 saveas.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY |
487 OFN_EXPLORER | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK;
488 saveas.lpstrDefExt = szDefaultExt;
489 saveas.lpTemplateName = MAKEINTRESOURCE(DIALOG_ENCODING);
490 saveas.lpfnHook = DIALOG_FileSaveAs_Hook;
491
492 if (GetSaveFileName(&saveas))
493 {
494 /* HACK: Because in ROS, Save-As boxes don't check the validity
495 * of file names and thus, here, szPath can be invalid !! We only
496 * see its validity when we call DoSaveFile()... */
497 SetFileName(szPath);
498 if (DoSaveFile())
499 {
500 UpdateWindowCaption();
501 return TRUE;
502 }
503 else
504 {
505 SetFileName(_T(""));
506 return FALSE;
507 }
508 }
509 else
510 {
511 return FALSE;
512 }
513 }
514
515 VOID DIALOG_FilePrint(VOID)
516 {
517 DOCINFO di;
518 TEXTMETRIC tm;
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 | PD_SELECTION;
551
552 /* Disable the selection radio button if there is no text selected */
553 if (!GetSelectionTextLength(Globals.hEdit))
554 {
555 printer.Flags = printer.Flags | PD_NOSELECTION;
556 }
557
558 printer.nFromPage = 0;
559 printer.nMinPage = 1;
560 /* we really need to calculate number of pages to set nMaxPage and nToPage */
561 printer.nToPage = (WORD)-1;
562 printer.nMaxPage = (WORD)-1;
563
564 /* Let commdlg manage copy settings */
565 printer.nCopies = (WORD)PD_USEDEVMODECOPIES;
566
567 printer.hDevMode = Globals.hDevMode;
568 printer.hDevNames = Globals.hDevNames;
569
570 if (!PrintDlg(&printer))
571 {
572 DeleteObject(font);
573 return;
574 }
575
576 Globals.hDevMode = printer.hDevMode;
577 Globals.hDevNames = printer.hDevNames;
578
579 assert(printer.hDC != 0);
580
581 /* initialize DOCINFO */
582 di.cbSize = sizeof(DOCINFO);
583 di.lpszDocName = Globals.szFileTitle;
584 di.lpszOutput = NULL;
585 di.lpszDatatype = NULL;
586 di.fwType = 0;
587
588 if (StartDoc(printer.hDC, &di) <= 0)
589 {
590 DeleteObject(font);
591 return;
592 }
593
594 /* Get the page dimensions in pixels. */
595 cWidthPels = GetDeviceCaps(printer.hDC, HORZRES);
596 cHeightPels = GetDeviceCaps(printer.hDC, VERTRES);
597
598 /* Get the file text */
599 if (printer.Flags & PD_SELECTION)
600 {
601 size = GetSelectionTextLength(Globals.hEdit) + 1;
602 }
603 else
604 {
605 size = GetWindowTextLength(Globals.hEdit) + 1;
606 }
607
608 pTemp = HeapAlloc(GetProcessHeap(), 0, size * sizeof(TCHAR));
609 if (!pTemp)
610 {
611 EndDoc(printer.hDC);
612 DeleteObject(font);
613 ShowLastError();
614 return;
615 }
616
617 if (printer.Flags & PD_SELECTION)
618 {
619 size = GetSelectionText(Globals.hEdit, pTemp, size);
620 }
621 else
622 {
623 size = GetWindowText(Globals.hEdit, pTemp, size);
624 }
625
626 /* Ensure that each logical unit maps to one pixel */
627 SetMapMode(printer.hDC, MM_TEXT);
628
629 /* Needed to get the correct height of a text line */
630 GetTextMetrics(printer.hDC, &tm);
631
632 border = 150;
633 for (copycount=1; copycount <= printer.nCopies; copycount++) {
634 i = 0;
635 pagecount = 1;
636 do {
637 /* Don't start a page if none of the conditions below are true */
638 dopage = 0;
639
640 /* The user wants to print the current selection */
641 if (printer.Flags & PD_SELECTION)
642 {
643 dopage = 1;
644 }
645
646 /* The user wants to print the entire document */
647 if (!(printer.Flags & PD_PAGENUMS) && !(printer.Flags & PD_SELECTION))
648 {
649 dopage = 1;
650 }
651
652 /* The user wants to print a specified range of pages */
653 if ((pagecount >= printer.nFromPage && pagecount <= printer.nToPage))
654 {
655 dopage = 1;
656 }
657
658 old_font = SelectObject(printer.hDC, font);
659
660 if (dopage) {
661 if (StartPage(printer.hDC) <= 0) {
662 SelectObject(printer.hDC, old_font);
663 EndDoc(printer.hDC);
664 DeleteDC(printer.hDC);
665 HeapFree(GetProcessHeap(), 0, pTemp);
666 DeleteObject(font);
667 AlertPrintError();
668 return;
669 }
670 /* Write a rectangle and header at the top of each page */
671 Rectangle(printer.hDC, border, border, cWidthPels-border, border + tm.tmHeight * 2);
672 /* I don't know what's up with this TextOut command. This comes out
673 kind of mangled.
674 */
675 TextOut(printer.hDC,
676 border * 2,
677 border + tm.tmHeight / 2,
678 Globals.szFileTitle,
679 lstrlen(Globals.szFileTitle));
680 }
681
682 /* The starting point for the main text */
683 xLeft = border * 2;
684 yTop = border + tm.tmHeight * 4;
685
686 SelectObject(printer.hDC, old_font);
687
688 /* Since outputting strings is giving me problems, output the main
689 * text one character at a time. */
690 do {
691 if (pTemp[i] == '\n') {
692 xLeft = border * 2;
693 yTop += tm.tmHeight;
694 }
695 else if (pTemp[i] != '\r') {
696 if (dopage)
697 TextOut(printer.hDC, xLeft, yTop, &pTemp[i], 1);
698
699 /* We need to get the width for each individual char, since a proportional font may be used */
700 GetTextExtentPoint32(printer.hDC, &pTemp[i], 1, &szMetric);
701 xLeft += szMetric.cx;
702
703 /* Insert a line break if the current line does not fit into the printing area */
704 if (xLeft > (cWidthPels - border * 2))
705 {
706 xLeft = border * 2;
707 yTop = yTop + tm.tmHeight;
708 }
709 }
710 } while (i++ < size && yTop < (cHeightPels - border * 2));
711
712 if (dopage)
713 EndPage(printer.hDC);
714 pagecount++;
715 } while (i < size);
716 }
717
718 if (old_font != 0)
719 SelectObject(printer.hDC, old_font);
720 EndDoc(printer.hDC);
721 DeleteDC(printer.hDC);
722 HeapFree(GetProcessHeap(), 0, pTemp);
723 DeleteObject(font);
724 }
725
726 VOID DIALOG_FileExit(VOID)
727 {
728 PostMessage(Globals.hMainWnd, WM_CLOSE, 0, 0l);
729 }
730
731 VOID DIALOG_EditUndo(VOID)
732 {
733 SendMessage(Globals.hEdit, EM_UNDO, 0, 0);
734 }
735
736 VOID DIALOG_EditCut(VOID)
737 {
738 SendMessage(Globals.hEdit, WM_CUT, 0, 0);
739 }
740
741 VOID DIALOG_EditCopy(VOID)
742 {
743 SendMessage(Globals.hEdit, WM_COPY, 0, 0);
744 }
745
746 VOID DIALOG_EditPaste(VOID)
747 {
748 SendMessage(Globals.hEdit, WM_PASTE, 0, 0);
749 }
750
751 VOID DIALOG_EditDelete(VOID)
752 {
753 SendMessage(Globals.hEdit, WM_CLEAR, 0, 0);
754 }
755
756 VOID DIALOG_EditSelectAll(VOID)
757 {
758 SendMessage(Globals.hEdit, EM_SETSEL, 0, (LPARAM)-1);
759 }
760
761 VOID DIALOG_EditTimeDate(VOID)
762 {
763 SYSTEMTIME st;
764 TCHAR szDate[MAX_STRING_LEN];
765 TCHAR szText[MAX_STRING_LEN * 2 + 2];
766
767 GetLocalTime(&st);
768
769 GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, szDate, MAX_STRING_LEN);
770 _tcscpy(szText, szDate);
771 _tcscat(szText, _T(" "));
772 GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, szDate, MAX_STRING_LEN);
773 _tcscat(szText, szDate);
774 SendMessage(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)szText);
775 }
776
777 VOID DoCreateStatusBar(VOID)
778 {
779 RECT rc;
780 RECT rcstatus;
781 BOOL bStatusBarVisible;
782
783 /* Check if status bar object already exists. */
784 if (Globals.hStatusBar == NULL)
785 {
786 /* Try to create the status bar */
787 Globals.hStatusBar = CreateStatusWindow(WS_CHILD | WS_VISIBLE | WS_EX_STATICEDGE,
788 NULL,
789 Globals.hMainWnd,
790 CMD_STATUSBAR_WND_ID);
791
792 if (Globals.hStatusBar == NULL)
793 {
794 ShowLastError();
795 return;
796 }
797
798 /* Load the string for formatting column/row text output */
799 LoadString(Globals.hInstance, STRING_LINE_COLUMN, Globals.szStatusBarLineCol, MAX_PATH - 1);
800
801 /* Set the status bar for single-text output */
802 SendMessage(Globals.hStatusBar, SB_SIMPLE, (WPARAM)TRUE, (LPARAM)0);
803 }
804
805 /* Set status bar visiblity according to the settings. */
806 if (Globals.bWrapLongLines == TRUE || Globals.bShowStatusBar == FALSE)
807 {
808 bStatusBarVisible = FALSE;
809 ShowWindow(Globals.hStatusBar, SW_HIDE);
810 }
811 else
812 {
813 bStatusBarVisible = TRUE;
814 ShowWindow(Globals.hStatusBar, SW_SHOW);
815 SendMessage(Globals.hStatusBar, WM_SIZE, 0, 0);
816 }
817
818 /* Set check state in show status bar item. */
819 if (bStatusBarVisible)
820 {
821 CheckMenuItem(Globals.hMenu, CMD_STATUSBAR, MF_BYCOMMAND | MF_CHECKED);
822 }
823 else
824 {
825 CheckMenuItem(Globals.hMenu, CMD_STATUSBAR, MF_BYCOMMAND | MF_UNCHECKED);
826 }
827
828 /* Update menu mar with the previous changes */
829 DrawMenuBar(Globals.hMainWnd);
830
831 /* Sefety test is edit control exists */
832 if (Globals.hEdit != NULL)
833 {
834 /* Retrieve the sizes of the controls */
835 GetClientRect(Globals.hMainWnd, &rc);
836 GetClientRect(Globals.hStatusBar, &rcstatus);
837
838 /* If status bar is currently visible, update dimensions of edit control */
839 if (bStatusBarVisible)
840 rc.bottom -= (rcstatus.bottom - rcstatus.top);
841
842 /* Resize edit control to right size. */
843 MoveWindow(Globals.hEdit,
844 rc.left,
845 rc.top,
846 rc.right - rc.left,
847 rc.bottom - rc.top,
848 TRUE);
849 }
850
851 /* Update content with current row/column text */
852 DIALOG_StatusBarUpdateCaretPos();
853 }
854
855 VOID DoCreateEditWindow(VOID)
856 {
857 DWORD dwStyle;
858 int iSize;
859 LPTSTR pTemp = NULL;
860 BOOL bModified = FALSE;
861
862 iSize = 0;
863
864 /* If the edit control already exists, try to save its content */
865 if (Globals.hEdit != NULL)
866 {
867 /* number of chars currently written into the editor. */
868 iSize = GetWindowTextLength(Globals.hEdit);
869 if (iSize)
870 {
871 /* Allocates temporary buffer. */
872 pTemp = HeapAlloc(GetProcessHeap(), 0, (iSize + 1) * sizeof(TCHAR));
873 if (!pTemp)
874 {
875 ShowLastError();
876 return;
877 }
878
879 /* Recover the text into the control. */
880 GetWindowText(Globals.hEdit, pTemp, iSize + 1);
881
882 if (SendMessage(Globals.hEdit, EM_GETMODIFY, 0, 0))
883 bModified = TRUE;
884 }
885
886 /* Restore original window procedure */
887 SetWindowLongPtr(Globals.hEdit, GWLP_WNDPROC, (LONG_PTR)Globals.EditProc);
888
889 /* Destroy the edit control */
890 DestroyWindow(Globals.hEdit);
891 }
892
893 /* Update wrap status into the main menu and recover style flags */
894 if (Globals.bWrapLongLines)
895 {
896 dwStyle = EDIT_STYLE_WRAP;
897 EnableMenuItem(Globals.hMenu, CMD_STATUSBAR, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
898 } else {
899 dwStyle = EDIT_STYLE;
900 EnableMenuItem(Globals.hMenu, CMD_STATUSBAR, MF_BYCOMMAND | MF_ENABLED);
901 }
902
903 /* Update previous changes */
904 DrawMenuBar(Globals.hMainWnd);
905
906 /* Create the new edit control */
907 Globals.hEdit = CreateWindowEx(WS_EX_CLIENTEDGE,
908 EDIT_CLASS,
909 NULL,
910 dwStyle,
911 CW_USEDEFAULT,
912 CW_USEDEFAULT,
913 CW_USEDEFAULT,
914 CW_USEDEFAULT,
915 Globals.hMainWnd,
916 NULL,
917 Globals.hInstance,
918 NULL);
919
920 if (Globals.hEdit == NULL)
921 {
922 if (pTemp)
923 {
924 HeapFree(GetProcessHeap(), 0, pTemp);
925 }
926
927 ShowLastError();
928 return;
929 }
930
931 SendMessage(Globals.hEdit, WM_SETFONT, (WPARAM)Globals.hFont, FALSE);
932 SendMessage(Globals.hEdit, EM_LIMITTEXT, 0, 0);
933
934 /* If some text was previously saved, restore it. */
935 if (iSize != 0)
936 {
937 SetWindowText(Globals.hEdit, pTemp);
938 HeapFree(GetProcessHeap(), 0, pTemp);
939
940 if (bModified)
941 SendMessage(Globals.hEdit, EM_SETMODIFY, TRUE, 0);
942 }
943
944 /* Sub-class a new window callback for row/column detection. */
945 Globals.EditProc = (WNDPROC)SetWindowLongPtr(Globals.hEdit,
946 GWLP_WNDPROC,
947 (LONG_PTR)EDIT_WndProc);
948
949 /* Create/update status bar */
950 DoCreateStatusBar();
951
952 /* Finally shows new edit control and set focus into it. */
953 ShowWindow(Globals.hEdit, SW_SHOW);
954 SetFocus(Globals.hEdit);
955 }
956
957 VOID DIALOG_EditWrap(VOID)
958 {
959 Globals.bWrapLongLines = !Globals.bWrapLongLines;
960 DoCreateEditWindow();
961 }
962
963 VOID DIALOG_SelectFont(VOID)
964 {
965 CHOOSEFONT cf;
966 LOGFONT lf = Globals.lfFont;
967
968 ZeroMemory( &cf, sizeof(cf) );
969 cf.lStructSize = sizeof(cf);
970 cf.hwndOwner = Globals.hMainWnd;
971 cf.lpLogFont = &lf;
972 cf.Flags = CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT | CF_NOVERTFONTS;
973
974 if (ChooseFont(&cf))
975 {
976 HFONT currfont = Globals.hFont;
977
978 Globals.hFont = CreateFontIndirect(&lf);
979 Globals.lfFont = lf;
980 SendMessage(Globals.hEdit, WM_SETFONT, (WPARAM)Globals.hFont, (LPARAM)TRUE);
981 if (currfont != NULL)
982 DeleteObject(currfont);
983 }
984 }
985
986 typedef HWND (WINAPI *FINDPROC)(LPFINDREPLACE lpfr);
987
988 static VOID DIALOG_SearchDialog(FINDPROC pfnProc)
989 {
990 ZeroMemory(&Globals.find, sizeof(Globals.find));
991 Globals.find.lStructSize = sizeof(Globals.find);
992 Globals.find.hwndOwner = Globals.hMainWnd;
993 Globals.find.hInstance = Globals.hInstance;
994 Globals.find.lpstrFindWhat = Globals.szFindText;
995 Globals.find.wFindWhatLen = ARRAY_SIZE(Globals.szFindText);
996 Globals.find.lpstrReplaceWith = Globals.szReplaceText;
997 Globals.find.wReplaceWithLen = ARRAY_SIZE(Globals.szReplaceText);
998 Globals.find.Flags = FR_DOWN;
999
1000 /* We only need to create the modal FindReplace dialog which will */
1001 /* notify us of incoming events using hMainWnd Window Messages */
1002
1003 Globals.hFindReplaceDlg = pfnProc(&Globals.find);
1004 assert(Globals.hFindReplaceDlg != 0);
1005 }
1006
1007 VOID DIALOG_Search(VOID)
1008 {
1009 DIALOG_SearchDialog(FindText);
1010 }
1011
1012 VOID DIALOG_SearchNext(VOID)
1013 {
1014 if (Globals.find.lpstrFindWhat != NULL)
1015 NOTEPAD_FindNext(&Globals.find, FALSE, TRUE);
1016 else
1017 DIALOG_Search();
1018 }
1019
1020 VOID DIALOG_Replace(VOID)
1021 {
1022 DIALOG_SearchDialog(ReplaceText);
1023 }
1024
1025 static INT_PTR
1026 CALLBACK
1027 DIALOG_GoTo_DialogProc(HWND hwndDialog, UINT uMsg, WPARAM wParam, LPARAM lParam)
1028 {
1029 BOOL bResult = FALSE;
1030 HWND hTextBox;
1031 TCHAR szText[32];
1032
1033 switch(uMsg) {
1034 case WM_INITDIALOG:
1035 hTextBox = GetDlgItem(hwndDialog, ID_LINENUMBER);
1036 _sntprintf(szText, ARRAY_SIZE(szText), _T("%ld"), lParam);
1037 SetWindowText(hTextBox, szText);
1038 break;
1039 case WM_COMMAND:
1040 if (HIWORD(wParam) == BN_CLICKED)
1041 {
1042 if (LOWORD(wParam) == IDOK)
1043 {
1044 hTextBox = GetDlgItem(hwndDialog, ID_LINENUMBER);
1045 GetWindowText(hTextBox, szText, ARRAY_SIZE(szText));
1046 EndDialog(hwndDialog, _ttoi(szText));
1047 bResult = TRUE;
1048 }
1049 else if (LOWORD(wParam) == IDCANCEL)
1050 {
1051 EndDialog(hwndDialog, 0);
1052 bResult = TRUE;
1053 }
1054 }
1055 break;
1056 }
1057
1058 return bResult;
1059 }
1060
1061 VOID DIALOG_GoTo(VOID)
1062 {
1063 INT_PTR nLine;
1064 LPTSTR pszText;
1065 int nLength, i;
1066 DWORD dwStart, dwEnd;
1067
1068 nLength = GetWindowTextLength(Globals.hEdit);
1069 pszText = (LPTSTR) HeapAlloc(GetProcessHeap(), 0, (nLength + 1) * sizeof(*pszText));
1070 if (!pszText)
1071 return;
1072
1073 /* Retrieve current text */
1074 GetWindowText(Globals.hEdit, pszText, nLength + 1);
1075 SendMessage(Globals.hEdit, EM_GETSEL, (WPARAM) &dwStart, (LPARAM) &dwEnd);
1076
1077 nLine = 1;
1078 for (i = 0; pszText[i] && (i < (int) dwStart); i++)
1079 {
1080 if (pszText[i] == '\n')
1081 nLine++;
1082 }
1083
1084 nLine = DialogBoxParam(Globals.hInstance,
1085 MAKEINTRESOURCE(DIALOG_GOTO),
1086 Globals.hMainWnd,
1087 DIALOG_GoTo_DialogProc,
1088 nLine);
1089
1090 if (nLine >= 1)
1091 {
1092 for (i = 0; pszText[i] && (nLine > 1) && (i < nLength - 1); i++)
1093 {
1094 if (pszText[i] == '\n')
1095 nLine--;
1096 }
1097 SendMessage(Globals.hEdit, EM_SETSEL, i, i);
1098 SendMessage(Globals.hEdit, EM_SCROLLCARET, 0, 0);
1099 }
1100 HeapFree(GetProcessHeap(), 0, pszText);
1101 }
1102
1103 VOID DIALOG_StatusBarUpdateCaretPos(VOID)
1104 {
1105 int line, col;
1106 TCHAR buff[MAX_PATH];
1107 DWORD dwStart, dwSize;
1108
1109 SendMessage(Globals.hEdit, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwSize);
1110 line = SendMessage(Globals.hEdit, EM_LINEFROMCHAR, (WPARAM)dwStart, 0);
1111 col = dwStart - SendMessage(Globals.hEdit, EM_LINEINDEX, (WPARAM)line, 0);
1112
1113 _stprintf(buff, Globals.szStatusBarLineCol, line + 1, col + 1);
1114 SendMessage(Globals.hStatusBar, SB_SETTEXT, SB_SIMPLEID, (LPARAM)buff);
1115 }
1116
1117 VOID DIALOG_ViewStatusBar(VOID)
1118 {
1119 Globals.bShowStatusBar = !Globals.bShowStatusBar;
1120
1121 DoCreateStatusBar();
1122 }
1123
1124 VOID DIALOG_HelpContents(VOID)
1125 {
1126 WinHelp(Globals.hMainWnd, helpfile, HELP_INDEX, 0);
1127 }
1128
1129 VOID DIALOG_HelpAboutNotepad(VOID)
1130 {
1131 TCHAR szNotepad[MAX_STRING_LEN];
1132 HICON notepadIcon = LoadIcon(Globals.hInstance, MAKEINTRESOURCE(IDI_NPICON));
1133
1134 LoadString(Globals.hInstance, STRING_NOTEPAD, szNotepad, ARRAY_SIZE(szNotepad));
1135 ShellAbout(Globals.hMainWnd, szNotepad, 0, notepadIcon);
1136 DeleteObject(notepadIcon);
1137 }
1138
1139 INT_PTR
1140 CALLBACK
1141 AboutDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
1142 {
1143 HWND hLicenseEditWnd;
1144 TCHAR *strLicense;
1145
1146 switch (message)
1147 {
1148 case WM_INITDIALOG:
1149
1150 hLicenseEditWnd = GetDlgItem(hDlg, IDC_LICENSE);
1151
1152 /* 0x1000 should be enough */
1153 strLicense = (TCHAR *)_alloca(0x1000);
1154 LoadString(GetModuleHandle(NULL), STRING_LICENSE, strLicense, 0x1000);
1155
1156 SetWindowText(hLicenseEditWnd, strLicense);
1157
1158 return TRUE;
1159
1160 case WM_COMMAND:
1161
1162 if ((LOWORD(wParam) == IDOK) || (LOWORD(wParam) == IDCANCEL))
1163 {
1164 EndDialog(hDlg, LOWORD(wParam));
1165 return TRUE;
1166 }
1167
1168 break;
1169 }
1170
1171 return 0;
1172 }
1173
1174 /***********************************************************************
1175 *
1176 * DIALOG_FilePageSetup
1177 */
1178 VOID DIALOG_FilePageSetup(void)
1179 {
1180 PAGESETUPDLG page;
1181
1182 ZeroMemory(&page, sizeof(page));
1183 page.lStructSize = sizeof(page);
1184 page.hwndOwner = Globals.hMainWnd;
1185 page.Flags = PSD_ENABLEPAGESETUPTEMPLATE | PSD_ENABLEPAGESETUPHOOK | PSD_MARGINS;
1186 page.hInstance = Globals.hInstance;
1187 page.rtMargin.left = Globals.lMarginLeft;
1188 page.rtMargin.top = Globals.lMarginTop;
1189 page.rtMargin.right = Globals.lMarginRight;
1190 page.rtMargin.bottom = Globals.lMarginBottom;
1191 page.hDevMode = Globals.hDevMode;
1192 page.hDevNames = Globals.hDevNames;
1193 page.lpPageSetupTemplateName = MAKEINTRESOURCE(DIALOG_PAGESETUP);
1194 page.lpfnPageSetupHook = DIALOG_PAGESETUP_Hook;
1195
1196 PageSetupDlg(&page);
1197
1198 Globals.hDevMode = page.hDevMode;
1199 Globals.hDevNames = page.hDevNames;
1200 Globals.lMarginLeft = page.rtMargin.left;
1201 Globals.lMarginTop = page.rtMargin.top;
1202 Globals.lMarginRight = page.rtMargin.right;
1203 Globals.lMarginBottom = page.rtMargin.bottom;
1204 }
1205
1206 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1207 *
1208 * DIALOG_PAGESETUP_Hook
1209 */
1210
1211 static UINT_PTR CALLBACK DIALOG_PAGESETUP_Hook(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
1212 {
1213 switch (msg)
1214 {
1215 case WM_COMMAND:
1216 if (HIWORD(wParam) == BN_CLICKED)
1217 {
1218 switch (LOWORD(wParam))
1219 {
1220 case IDOK:
1221 /* save user input and close dialog */
1222 GetDlgItemText(hDlg, 0x141, Globals.szHeader, ARRAY_SIZE(Globals.szHeader));
1223 GetDlgItemText(hDlg, 0x143, Globals.szFooter, ARRAY_SIZE(Globals.szFooter));
1224 return FALSE;
1225
1226 case IDCANCEL:
1227 /* discard user input and close dialog */
1228 return FALSE;
1229
1230 case IDHELP:
1231 {
1232 /* FIXME: Bring this to work */
1233 static const TCHAR sorry[] = _T("Sorry, no help available");
1234 static const TCHAR help[] = _T("Help");
1235 MessageBox(Globals.hMainWnd, sorry, help, MB_ICONEXCLAMATION);
1236 return TRUE;
1237 }
1238
1239 default:
1240 break;
1241 }
1242 }
1243 break;
1244
1245 case WM_INITDIALOG:
1246 /* fetch last user input prior to display dialog */
1247 SetDlgItemText(hDlg, 0x141, Globals.szHeader);
1248 SetDlgItemText(hDlg, 0x143, Globals.szFooter);
1249 break;
1250 }
1251
1252 return FALSE;
1253 }