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