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