[APPLICATIONS]
[reactos.git] / reactos / base / applications / notepad / main.c
1 /*
2 * Notepad
3 *
4 * Copyright 2000 Mike McCormack <Mike_McCormack@looksmart.com.au>
5 * Copyright 1997,98 Marcel Baur <mbaur@g26.ethz.ch>
6 * Copyright 2002 Sylvain Petreolle <spetreolle@yahoo.fr>
7 * Copyright 2002 Andriy Palamarchuk
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 *
23 */
24
25 #include "notepad.h"
26
27 NOTEPAD_GLOBALS Globals;
28 static ATOM aFINDMSGSTRING;
29
30 VOID NOTEPAD_EnableSearchMenu()
31 {
32 EnableMenuItem(Globals.hMenu, CMD_SEARCH,
33 MF_BYCOMMAND | ((GetWindowTextLength(Globals.hEdit) == 0) ? MF_DISABLED | MF_GRAYED : MF_ENABLED));
34 EnableMenuItem(Globals.hMenu, CMD_SEARCH_NEXT,
35 MF_BYCOMMAND | ((GetWindowTextLength(Globals.hEdit) == 0) ? MF_DISABLED | MF_GRAYED : MF_ENABLED));
36 }
37
38 /***********************************************************************
39 *
40 * SetFileName
41 *
42 * Sets Global File Name.
43 */
44 VOID SetFileName(LPCTSTR szFileName)
45 {
46 _tcscpy(Globals.szFileName, szFileName);
47 Globals.szFileTitle[0] = 0;
48 GetFileTitle(szFileName, Globals.szFileTitle, SIZEOF(Globals.szFileTitle));
49 }
50
51 /***********************************************************************
52 *
53 * NOTEPAD_MenuCommand
54 *
55 * All handling of main menu events
56 */
57 static int NOTEPAD_MenuCommand(WPARAM wParam)
58 {
59 switch (wParam)
60 {
61 case CMD_NEW: DIALOG_FileNew(); break;
62 case CMD_OPEN: DIALOG_FileOpen(); break;
63 case CMD_SAVE: DIALOG_FileSave(); break;
64 case CMD_SAVE_AS: DIALOG_FileSaveAs(); break;
65 case CMD_PRINT: DIALOG_FilePrint(); break;
66 case CMD_PAGE_SETUP: DIALOG_FilePageSetup(); break;
67 case CMD_PRINTER_SETUP: DIALOG_FilePrinterSetup();break;
68 case CMD_EXIT: DIALOG_FileExit(); break;
69
70 case CMD_UNDO: DIALOG_EditUndo(); break;
71 case CMD_CUT: DIALOG_EditCut(); break;
72 case CMD_COPY: DIALOG_EditCopy(); break;
73 case CMD_PASTE: DIALOG_EditPaste(); break;
74 case CMD_DELETE: DIALOG_EditDelete(); break;
75 case CMD_SELECT_ALL: DIALOG_EditSelectAll(); break;
76 case CMD_TIME_DATE: DIALOG_EditTimeDate();break;
77
78 case CMD_SEARCH: DIALOG_Search(); break;
79 case CMD_SEARCH_NEXT: DIALOG_SearchNext(); break;
80 case CMD_REPLACE: DIALOG_Replace(); break;
81 case CMD_GOTO: DIALOG_GoTo(); break;
82
83 case CMD_WRAP: DIALOG_EditWrap(); break;
84 case CMD_FONT: DIALOG_SelectFont(); break;
85
86 case CMD_STATUSBAR: DIALOG_ViewStatusBar(); break;
87
88 case CMD_HELP_CONTENTS: DIALOG_HelpContents(); break;
89 case CMD_HELP_SEARCH: DIALOG_HelpSearch(); break;
90 case CMD_HELP_ON_HELP: DIALOG_HelpHelp(); break;
91 case CMD_ABOUT: DialogBox(GetModuleHandle(NULL),
92 MAKEINTRESOURCE(IDD_ABOUTBOX),
93 Globals.hMainWnd,
94 AboutDialogProc);
95 break;
96 case CMD_ABOUT_WINE: DIALOG_HelpAboutWine(); break;
97
98 default:
99 break;
100 }
101 return 0;
102 }
103
104 /***********************************************************************
105 *
106 * NOTEPAD_FindTextAt
107 */
108
109 static BOOL NOTEPAD_FindTextAt(FINDREPLACE *pFindReplace, LPCTSTR pszText, int iTextLength, DWORD dwPosition)
110 {
111 BOOL bMatches;
112 size_t iTargetLength;
113
114 iTargetLength = _tcslen(pFindReplace->lpstrFindWhat);
115
116 /* Make proper comparison */
117 if (pFindReplace->Flags & FR_MATCHCASE)
118 bMatches = !_tcsncmp(&pszText[dwPosition], pFindReplace->lpstrFindWhat, iTargetLength);
119 else
120 bMatches = !_tcsnicmp(&pszText[dwPosition], pFindReplace->lpstrFindWhat, iTargetLength);
121
122 if (bMatches && pFindReplace->Flags & FR_WHOLEWORD)
123 {
124 if ((dwPosition > 0) && !_istspace(pszText[dwPosition-1]))
125 bMatches = FALSE;
126 if ((dwPosition < (DWORD) iTextLength - 1) && !_istspace(pszText[dwPosition+1]))
127 bMatches = FALSE;
128 }
129
130 return bMatches;
131 }
132
133 /***********************************************************************
134 *
135 * NOTEPAD_FindNext
136 */
137
138 BOOL NOTEPAD_FindNext(FINDREPLACE *pFindReplace, BOOL bReplace, BOOL bShowAlert)
139 {
140 int iTextLength, iTargetLength;
141 size_t iAdjustment = 0;
142 LPTSTR pszText = NULL;
143 DWORD dwPosition, dwBegin, dwEnd;
144 BOOL bMatches = FALSE;
145 TCHAR szResource[128], szText[128];
146 BOOL bSuccess;
147
148 iTargetLength = (int) _tcslen(pFindReplace->lpstrFindWhat);
149
150 /* Retrieve the window text */
151 iTextLength = GetWindowTextLength(Globals.hEdit);
152 if (iTextLength > 0)
153 {
154 pszText = (LPTSTR) HeapAlloc(GetProcessHeap(), 0, (iTextLength + 1) * sizeof(TCHAR));
155 if (!pszText)
156 return FALSE;
157
158 GetWindowText(Globals.hEdit, pszText, iTextLength + 1);
159 }
160
161 SendMessage(Globals.hEdit, EM_GETSEL, (WPARAM) &dwBegin, (LPARAM) &dwEnd);
162 if (bReplace && ((dwEnd - dwBegin) == (DWORD) iTargetLength))
163 {
164 if (NOTEPAD_FindTextAt(pFindReplace, pszText, iTextLength, dwBegin))
165 {
166 SendMessage(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM) pFindReplace->lpstrReplaceWith);
167 iAdjustment = _tcslen(pFindReplace->lpstrReplaceWith) - (dwEnd - dwBegin);
168 }
169 }
170
171 if (pFindReplace->Flags & FR_DOWN)
172 {
173 /* Find Down */
174 dwPosition = dwEnd;
175 while(dwPosition < (DWORD) iTextLength)
176 {
177 bMatches = NOTEPAD_FindTextAt(pFindReplace, pszText, iTextLength, dwPosition);
178 if (bMatches)
179 break;
180 dwPosition++;
181 }
182 }
183 else
184 {
185 /* Find Up */
186 dwPosition = dwBegin;
187 while(dwPosition > 0)
188 {
189 dwPosition--;
190 bMatches = NOTEPAD_FindTextAt(pFindReplace, pszText, iTextLength, dwPosition);
191 if (bMatches)
192 break;
193 }
194 }
195
196 if (bMatches)
197 {
198 /* Found target */
199 if (dwPosition > dwBegin)
200 dwPosition += (DWORD) iAdjustment;
201 SendMessage(Globals.hEdit, EM_SETSEL, dwPosition, dwPosition + iTargetLength);
202 SendMessage(Globals.hEdit, EM_SCROLLCARET, 0, 0);
203 bSuccess = TRUE;
204 }
205 else
206 {
207 /* Can't find target */
208 if (bShowAlert)
209 {
210 LoadString(Globals.hInstance, STRING_CANNOTFIND, szResource, SIZEOF(szResource));
211 _sntprintf(szText, SIZEOF(szText), szResource, pFindReplace->lpstrFindWhat);
212 LoadString(Globals.hInstance, STRING_NOTEPAD, szResource, SIZEOF(szResource));
213 MessageBox(Globals.hFindReplaceDlg, szText, szResource, MB_OK);
214 }
215 bSuccess = FALSE;
216 }
217
218 if (pszText)
219 HeapFree(GetProcessHeap(), 0, pszText);
220 return bSuccess;
221 }
222
223 /***********************************************************************
224 *
225 * NOTEPAD_ReplaceAll
226 */
227
228 static VOID NOTEPAD_ReplaceAll(FINDREPLACE *pFindReplace)
229 {
230 BOOL bShowAlert = TRUE;
231
232 SendMessage(Globals.hEdit, EM_SETSEL, 0, 0);
233
234 while (NOTEPAD_FindNext(pFindReplace, TRUE, bShowAlert))
235 {
236 bShowAlert = FALSE;
237 }
238 }
239
240 /***********************************************************************
241 *
242 * NOTEPAD_FindTerm
243 */
244
245 static VOID NOTEPAD_FindTerm(VOID)
246 {
247 Globals.hFindReplaceDlg = NULL;
248 }
249
250 /***********************************************************************
251 * Data Initialization
252 */
253 static VOID NOTEPAD_InitData(VOID)
254 {
255 LPTSTR p = Globals.szFilter;
256 static const TCHAR txt_files[] = _T("*.txt");
257 static const TCHAR all_files[] = _T("*.*");
258
259 p += LoadString(Globals.hInstance, STRING_TEXT_FILES_TXT, p, MAX_STRING_LEN)+1;
260 _tcscpy(p, txt_files);
261 p += SIZEOF(txt_files);
262
263 p += LoadString(Globals.hInstance, STRING_ALL_FILES, p, MAX_STRING_LEN)+1;
264 _tcscpy(p, all_files);
265 p += SIZEOF(all_files);
266 *p = '\0';
267 Globals.find.lpstrFindWhat = NULL;
268 }
269
270 /***********************************************************************
271 * Enable/disable items on the menu based on control state
272 */
273 static VOID NOTEPAD_InitMenuPopup(HMENU menu, LPARAM index)
274 {
275 int enable;
276
277 UNREFERENCED_PARAMETER(index);
278
279 CheckMenuItem(GetMenu(Globals.hMainWnd), CMD_WRAP,
280 MF_BYCOMMAND | (Globals.bWrapLongLines ? MF_CHECKED : MF_UNCHECKED));
281 if ( !Globals.bWrapLongLines )
282 {
283 CheckMenuItem(GetMenu(Globals.hMainWnd), CMD_STATUSBAR,
284 MF_BYCOMMAND | (Globals.bShowStatusBar ? MF_CHECKED : MF_UNCHECKED));
285 }
286 EnableMenuItem(menu, CMD_UNDO,
287 SendMessage(Globals.hEdit, EM_CANUNDO, 0, 0) ? MF_ENABLED : MF_GRAYED);
288 EnableMenuItem(menu, CMD_PASTE,
289 IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED);
290 enable = (int) SendMessage(Globals.hEdit, EM_GETSEL, 0, 0);
291 enable = (HIWORD(enable) == LOWORD(enable)) ? MF_GRAYED : MF_ENABLED;
292 EnableMenuItem(menu, CMD_CUT, enable);
293 EnableMenuItem(menu, CMD_COPY, enable);
294 EnableMenuItem(menu, CMD_DELETE, enable);
295
296 EnableMenuItem(menu, CMD_SELECT_ALL,
297 GetWindowTextLength(Globals.hEdit) ? MF_ENABLED : MF_GRAYED);
298 DrawMenuBar(Globals.hMainWnd);
299 }
300
301 LRESULT CALLBACK EDIT_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
302 {
303 switch (msg)
304 {
305 case WM_KEYDOWN:
306 case WM_KEYUP:
307 {
308 switch (wParam)
309 {
310 case VK_UP:
311 case VK_DOWN:
312 case VK_LEFT:
313 case VK_RIGHT:
314 DIALOG_StatusBarUpdateCaretPos();
315 break;
316 }
317 }
318 case WM_LBUTTONUP:
319 {
320 DIALOG_StatusBarUpdateCaretPos();
321 break;
322 }
323 }
324 return CallWindowProc( Globals.EditProc, hWnd, msg, wParam, lParam);
325 }
326
327 /***********************************************************************
328 *
329 * NOTEPAD_WndProc
330 */
331 static LRESULT WINAPI NOTEPAD_WndProc(HWND hWnd, UINT msg, WPARAM wParam,
332 LPARAM lParam)
333 {
334 switch (msg) {
335
336 case WM_CREATE:
337 Globals.hMenu = GetMenu(hWnd);
338 break;
339
340 case WM_COMMAND:
341 if (HIWORD(wParam) == EN_CHANGE || HIWORD(wParam) == EN_HSCROLL || HIWORD(wParam) == EN_VSCROLL)
342 DIALOG_StatusBarUpdateCaretPos();
343 if ((HIWORD(wParam) == EN_CHANGE))
344 NOTEPAD_EnableSearchMenu();
345 NOTEPAD_MenuCommand(LOWORD(wParam));
346 break;
347
348 case WM_DESTROYCLIPBOARD:
349 /*MessageBox(Globals.hMainWnd, "Empty clipboard", "Debug", MB_ICONEXCLAMATION);*/
350 break;
351
352 case WM_CLOSE:
353 if (DoCloseFile()) {
354 if (Globals.hFont)
355 DeleteObject(Globals.hFont);
356 DestroyWindow(hWnd);
357 }
358 break;
359
360 case WM_QUERYENDSESSION:
361 if (DoCloseFile()) {
362 return 1;
363 }
364 break;
365
366 case WM_DESTROY:
367 SetWindowLongPtr(Globals.hEdit, GWLP_WNDPROC, (LONG_PTR)Globals.EditProc);
368 SaveSettings();
369 PostQuitMessage(0);
370 break;
371
372 case WM_SIZE:
373 {
374 if (Globals.bShowStatusBar == TRUE &&
375 Globals.bWrapLongLines == FALSE)
376 {
377 RECT rcStatusBar;
378 HDWP hdwp;
379
380 if (!GetWindowRect(Globals.hStatusBar, &rcStatusBar))
381 break;
382
383 hdwp = BeginDeferWindowPos(2);
384 if (hdwp == NULL)
385 break;
386
387 hdwp = DeferWindowPos(hdwp, Globals.hEdit, NULL, 0, 0, LOWORD(lParam), HIWORD(lParam) - (rcStatusBar.bottom - rcStatusBar.top), SWP_NOZORDER | SWP_NOMOVE);
388 if (hdwp == NULL)
389 break;
390
391 hdwp = DeferWindowPos(hdwp, Globals.hStatusBar, NULL, 0, 0, LOWORD(lParam), LOWORD(wParam), SWP_NOZORDER);
392
393 if (hdwp != NULL)
394 EndDeferWindowPos(hdwp);
395 }
396 else
397 SetWindowPos(Globals.hEdit, NULL, 0, 0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER | SWP_NOMOVE);
398
399 break;
400 }
401
402 // The entire client area is covered by edit control and by
403 // the status bar. So there is no need to erase main background.
404 // This resolves the horrible fliker effect during windows resizes.
405 case WM_ERASEBKGND:
406 return 1;
407
408 case WM_SETFOCUS:
409 SetFocus(Globals.hEdit);
410 break;
411
412 case WM_DROPFILES:
413 {
414 TCHAR szFileName[MAX_PATH];
415 HDROP hDrop = (HDROP) wParam;
416
417 DragQueryFile(hDrop, 0, szFileName, SIZEOF(szFileName));
418 DragFinish(hDrop);
419 DoOpenFile(szFileName);
420 break;
421 }
422 case WM_CHAR:
423 case WM_INITMENUPOPUP:
424 NOTEPAD_InitMenuPopup((HMENU)wParam, lParam);
425 break;
426 default:
427 if (msg == aFINDMSGSTRING)
428 {
429 FINDREPLACE *pFindReplace = (FINDREPLACE *) lParam;
430 Globals.find = *(FINDREPLACE *) lParam;
431
432 if (pFindReplace->Flags & FR_FINDNEXT)
433 NOTEPAD_FindNext(pFindReplace, FALSE, TRUE);
434 else if (pFindReplace->Flags & FR_REPLACE)
435 NOTEPAD_FindNext(pFindReplace, TRUE, TRUE);
436 else if (pFindReplace->Flags & FR_REPLACEALL)
437 NOTEPAD_ReplaceAll(pFindReplace);
438 else if (pFindReplace->Flags & FR_DIALOGTERM)
439 NOTEPAD_FindTerm();
440 break;
441 }
442
443 return DefWindowProc(hWnd, msg, wParam, lParam);
444 }
445 return 0;
446 }
447
448 static int AlertFileDoesNotExist(LPCTSTR szFileName)
449 {
450 int nResult;
451 TCHAR szMessage[MAX_STRING_LEN];
452 TCHAR szResource[MAX_STRING_LEN];
453
454 LoadString(Globals.hInstance, STRING_DOESNOTEXIST, szResource, SIZEOF(szResource));
455 wsprintf(szMessage, szResource, szFileName);
456
457 LoadString(Globals.hInstance, STRING_NOTEPAD, szResource, SIZEOF(szResource));
458
459 nResult = MessageBox(Globals.hMainWnd, szMessage, szResource,
460 MB_ICONEXCLAMATION | MB_YESNO);
461
462 return(nResult);
463 }
464
465 static BOOL HandleCommandLine(LPTSTR cmdline)
466 {
467 int opt_print = 0;
468
469 while (*cmdline == _T(' ') || *cmdline == _T('-') || *cmdline == _T('/'))
470 {
471 TCHAR option;
472
473 if (*cmdline++ == _T(' ')) continue;
474
475 option = *cmdline;
476 if (option) cmdline++;
477 while (*cmdline == _T(' ')) cmdline++;
478
479 switch(option)
480 {
481 case 'p':
482 case 'P':
483 opt_print = 1;
484 break;
485 }
486 }
487
488 if (*cmdline)
489 {
490 /* file name is passed in the command line */
491 LPCTSTR file_name = NULL;
492 BOOL file_exists = FALSE;
493 TCHAR buf[MAX_PATH];
494
495 if (cmdline[0] == _T('"'))
496 {
497 cmdline++;
498 cmdline[lstrlen(cmdline) - 1] = 0;
499 }
500
501 file_name = cmdline;
502 if (FileExists(file_name))
503 {
504 file_exists = TRUE;
505 }
506 else if (!HasFileExtension(cmdline))
507 {
508 static const TCHAR txt[] = _T(".txt");
509
510 /* try to find file with ".txt" extension */
511 if (!_tcscmp(txt, cmdline + _tcslen(cmdline) - _tcslen(txt)))
512 {
513 file_exists = FALSE;
514 }
515 else
516 {
517 _tcsncpy(buf, cmdline, MAX_PATH - _tcslen(txt) - 1);
518 _tcscat(buf, txt);
519 file_name = buf;
520 file_exists = FileExists(file_name);
521 }
522 }
523
524 if (file_exists)
525 {
526 DoOpenFile(file_name);
527 InvalidateRect(Globals.hMainWnd, NULL, FALSE);
528 if (opt_print)
529 {
530 DIALOG_FilePrint();
531 return FALSE;
532 }
533 }
534 else
535 {
536 switch (AlertFileDoesNotExist(file_name)) {
537 case IDYES:
538 DoOpenFile(file_name);
539 break;
540
541 case IDNO:
542 break;
543 }
544 }
545 }
546
547 return TRUE;
548 }
549
550 /***********************************************************************
551 *
552 * WinMain
553 */
554 int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE prev, LPTSTR cmdline, int show)
555 {
556 MSG msg;
557 HACCEL hAccel;
558 WNDCLASSEX wndclass;
559 HMONITOR monitor;
560 MONITORINFO info;
561 INT x, y;
562
563 static const TCHAR className[] = _T("NPClass");
564 static const TCHAR winName[] = _T("Notepad");
565
566 switch (GetUserDefaultUILanguage())
567 {
568 case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT):
569 SetProcessDefaultLayout(LAYOUT_RTL);
570 break;
571
572 default:
573 break;
574 }
575
576 UNREFERENCED_PARAMETER(prev);
577
578 aFINDMSGSTRING = (ATOM) RegisterWindowMessage(FINDMSGSTRING);
579
580 ZeroMemory(&Globals, sizeof(Globals));
581 Globals.hInstance = hInstance;
582 LoadSettings();
583
584 ZeroMemory(&wndclass, sizeof(wndclass));
585 wndclass.cbSize = sizeof(wndclass);
586 wndclass.lpfnWndProc = NOTEPAD_WndProc;
587 wndclass.hInstance = Globals.hInstance;
588 wndclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_NPICON));
589 wndclass.hCursor = LoadCursor(0, IDC_ARROW);
590 wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
591 wndclass.lpszMenuName = MAKEINTRESOURCE(MAIN_MENU);
592 wndclass.lpszClassName = className;
593 wndclass.hIconSm = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_NPICON),
594 IMAGE_ICON, 16, 16, 0);
595
596 if (!RegisterClassEx(&wndclass)) return FALSE;
597
598 /* Setup windows */
599
600 monitor = MonitorFromRect( &Globals.main_rect, MONITOR_DEFAULTTOPRIMARY );
601 info.cbSize = sizeof(info);
602 GetMonitorInfoW( monitor, &info );
603
604 x = Globals.main_rect.left;
605 y = Globals.main_rect.top;
606 if (Globals.main_rect.left >= info.rcWork.right ||
607 Globals.main_rect.top >= info.rcWork.bottom ||
608 Globals.main_rect.right < info.rcWork.left ||
609 Globals.main_rect.bottom < info.rcWork.top)
610 x = y = CW_USEDEFAULT;
611
612 Globals.hMainWnd =
613 CreateWindow(className, winName, WS_OVERLAPPEDWINDOW,
614 x, y, Globals.main_rect.right - Globals.main_rect.left,
615 Globals.main_rect.bottom - Globals.main_rect.top,
616 NULL, NULL, Globals.hInstance, NULL);
617 if (!Globals.hMainWnd)
618 {
619 ShowLastError();
620 ExitProcess(1);
621 }
622
623 DoCreateEditWindow();
624
625 NOTEPAD_InitData();
626 DIALOG_FileNew();
627
628 ShowWindow(Globals.hMainWnd, show);
629 UpdateWindow(Globals.hMainWnd);
630 DragAcceptFiles(Globals.hMainWnd, TRUE);
631
632 DIALOG_ViewStatusBar();
633
634 if (!HandleCommandLine(cmdline))
635 {
636 return 0;
637 }
638
639 hAccel = LoadAccelerators( hInstance, MAKEINTRESOURCE(ID_ACCEL) );
640
641 while (GetMessage(&msg, 0, 0, 0))
642 {
643 if (!IsDialogMessage(Globals.hFindReplaceDlg, &msg) &&
644 !TranslateAccelerator(Globals.hMainWnd, hAccel, &msg))
645 {
646 TranslateMessage(&msg);
647 DispatchMessage(&msg);
648 }
649 }
650 return (int) msg.wParam;
651 }