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