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