be3ba17efd1b57a7962eaa7209bc937882f7adcf
[reactos.git] / reactos / subsys / system / 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
24
25 #include <notepad.h>
26
27 NOTEPAD_GLOBALS Globals;
28 static ATOM aFINDMSGSTRING;
29
30 /***********************************************************************
31 *
32 * SetFileName
33 *
34 * Sets Global File Name.
35 */
36 VOID SetFileName(LPCWSTR szFileName)
37 {
38 lstrcpy(Globals.szFileName, szFileName);
39 Globals.szFileTitle[0] = 0;
40 GetFileTitle(szFileName, Globals.szFileTitle, wcslen(Globals.szFileTitle));
41 }
42
43 /***********************************************************************
44 *
45 * NOTEPAD_MenuCommand
46 *
47 * All handling of main menu events
48 */
49 static int NOTEPAD_MenuCommand(WPARAM wParam)
50 {
51 switch (wParam)
52 {
53 case CMD_NEW: DIALOG_FileNew(); break;
54 case CMD_OPEN: DIALOG_FileOpen(); break;
55 case CMD_SAVE: DIALOG_FileSave(); break;
56 case CMD_SAVE_AS: DIALOG_FileSaveAs(); break;
57 case CMD_PRINT: DIALOG_FilePrint(); break;
58 case CMD_PAGE_SETUP: DIALOG_FilePageSetup(); break;
59 case CMD_PRINTER_SETUP: DIALOG_FilePrinterSetup();break;
60 case CMD_EXIT: DIALOG_FileExit(); break;
61
62 case CMD_UNDO: DIALOG_EditUndo(); break;
63 case CMD_CUT: DIALOG_EditCut(); break;
64 case CMD_COPY: DIALOG_EditCopy(); break;
65 case CMD_PASTE: DIALOG_EditPaste(); break;
66 case CMD_DELETE: DIALOG_EditDelete(); break;
67 case CMD_SELECT_ALL: DIALOG_EditSelectAll(); break;
68 case CMD_TIME_DATE: DIALOG_EditTimeDate();break;
69
70 case CMD_SEARCH: DIALOG_Search(); break;
71 case CMD_SEARCH_NEXT: DIALOG_SearchNext(); break;
72 case CMD_REPLACE: DIALOG_Replace(); break;
73 case CMD_GOTO: DIALOG_GoTo(); break;
74
75 case CMD_WRAP: DIALOG_EditWrap(); break;
76 case CMD_FONT: DIALOG_SelectFont(); break;
77
78 case CMD_HELP_CONTENTS: DIALOG_HelpContents(); break;
79 case CMD_HELP_SEARCH: DIALOG_HelpSearch(); break;
80 case CMD_HELP_ON_HELP: DIALOG_HelpHelp(); break;
81 case CMD_LICENSE: DIALOG_HelpLicense(); break;
82 case CMD_NO_WARRANTY: DIALOG_HelpNoWarranty(); break;
83 case CMD_ABOUT_WINE: DIALOG_HelpAboutWine(); break;
84
85 default:
86 break;
87 }
88 return 0;
89 }
90
91 /***********************************************************************
92 *
93 * NOTEPAD_FindTextAt
94 */
95
96 static BOOL NOTEPAD_FindTextAt(FINDREPLACE *pFindReplace, LPCTSTR pszText, int iTextLength, DWORD dwPosition)
97 {
98 BOOL bMatches;
99 int iTargetLength;
100
101 iTargetLength = _tcslen(pFindReplace->lpstrFindWhat);
102
103 /* Make proper comparison */
104 if (pFindReplace->Flags & FR_MATCHCASE)
105 bMatches = !_tcsncmp(&pszText[dwPosition], pFindReplace->lpstrFindWhat, iTargetLength);
106 else
107 bMatches = !_tcsnicmp(&pszText[dwPosition], pFindReplace->lpstrFindWhat, iTargetLength);
108
109 if (bMatches && pFindReplace->Flags & FR_WHOLEWORD)
110 {
111 if ((dwPosition > 0) && !_istspace(pszText[dwPosition-1]))
112 bMatches = FALSE;
113 if ((dwPosition < iTextLength - 1) && !_istspace(pszText[dwPosition+1]))
114 bMatches = FALSE;
115 }
116
117 return bMatches;
118 }
119
120 /***********************************************************************
121 *
122 * NOTEPAD_FindNext
123 */
124
125 static BOOL NOTEPAD_FindNext(FINDREPLACE *pFindReplace, BOOL bReplace, BOOL bShowAlert)
126 {
127 int iTextLength, iTargetLength;
128 int iAdjustment = 0;
129 LPTSTR pszText = NULL;
130 DWORD dwPosition, dwBegin, dwEnd;
131 BOOL bMatches = FALSE;
132 TCHAR szResource[128], szText[128];
133 BOOL bSuccess;
134
135 iTargetLength = _tcslen(pFindReplace->lpstrFindWhat);
136
137 /* Retrieve the window text */
138 iTextLength = GetWindowTextLength(Globals.hEdit);
139 if (iTextLength > 0)
140 {
141 pszText = (LPTSTR) HeapAlloc(GetProcessHeap(), 0, (iTextLength + 1) * sizeof(TCHAR));
142 if (!pszText)
143 return FALSE;
144
145 GetWindowText(Globals.hEdit, pszText, iTextLength + 1);
146 }
147
148 SendMessage(Globals.hEdit, EM_GETSEL, (WPARAM) &dwBegin, (LPARAM) &dwEnd);
149 if (bReplace && ((dwEnd - dwBegin) == iTargetLength))
150 {
151 if (NOTEPAD_FindTextAt(pFindReplace, pszText, iTextLength, dwBegin))
152 {
153 SendMessage(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM) pFindReplace->lpstrReplaceWith);
154 iAdjustment = _tcslen(pFindReplace->lpstrReplaceWith) - (dwEnd - dwBegin);
155 }
156 }
157
158 if (pFindReplace->Flags & FR_DOWN)
159 {
160 /* Find Down */
161 dwPosition = dwEnd;
162 while(dwPosition < iTextLength)
163 {
164 bMatches = NOTEPAD_FindTextAt(pFindReplace, pszText, iTextLength, dwPosition);
165 if (bMatches)
166 break;
167 dwPosition++;
168 }
169 }
170 else
171 {
172 /* Find Up */
173 dwPosition = dwBegin;
174 while(dwPosition > 0)
175 {
176 dwPosition--;
177 bMatches = NOTEPAD_FindTextAt(pFindReplace, pszText, iTextLength, dwPosition);
178 if (bMatches)
179 break;
180 }
181 }
182
183 if (bMatches)
184 {
185 /* Found target */
186 if (dwPosition > dwBegin)
187 dwPosition += iAdjustment;
188 SendMessage(Globals.hEdit, EM_SETSEL, dwPosition, dwPosition + iTargetLength);
189 SendMessage(Globals.hEdit, EM_SCROLLCARET, 0, 0);
190 bSuccess = TRUE;
191 }
192 else
193 {
194 /* Can't find target */
195 if (bShowAlert)
196 {
197 LoadString(Globals.hInstance, STRING_CANNOTFIND, szResource, SIZEOF(szResource));
198 _sntprintf(szText, SIZEOF(szText), szResource, pFindReplace->lpstrFindWhat);
199 LoadString(Globals.hInstance, STRING_NOTEPAD, szResource, SIZEOF(szResource));
200 MessageBox(Globals.hFindReplaceDlg, szText, szResource, MB_OK);
201 }
202 bSuccess = FALSE;
203 }
204
205 if (pszText)
206 HeapFree(GetProcessHeap(), 0, pszText);
207 return bSuccess;
208 }
209
210 /***********************************************************************
211 *
212 * NOTEPAD_ReplaceAll
213 */
214
215 static VOID NOTEPAD_ReplaceAll(FINDREPLACE *pFindReplace)
216 {
217 BOOL bShowAlert = TRUE;
218
219 SendMessage(Globals.hEdit, EM_SETSEL, 0, 0);
220
221 while (NOTEPAD_FindNext(pFindReplace, TRUE, bShowAlert))
222 {
223 bShowAlert = FALSE;
224 }
225 }
226
227 /***********************************************************************
228 *
229 * NOTEPAD_FindTerm
230 */
231
232 static VOID NOTEPAD_FindTerm(VOID)
233 {
234 Globals.hFindReplaceDlg = NULL;
235 }
236
237 /***********************************************************************
238 * Data Initialization
239 */
240 static VOID NOTEPAD_InitData(VOID)
241 {
242 LPWSTR p = Globals.szFilter;
243 static const WCHAR txt_files[] = { '*','.','t','x','t',0 };
244 static const WCHAR all_files[] = { '*','.','*',0 };
245
246 LoadString(Globals.hInstance, STRING_TEXT_FILES_TXT, p, MAX_STRING_LEN);
247 p += lstrlen(p) + 1;
248 lstrcpy(p, txt_files);
249 p += lstrlen(p) + 1;
250 LoadString(Globals.hInstance, STRING_ALL_FILES, p, MAX_STRING_LEN);
251 p += lstrlen(p) + 1;
252 lstrcpy(p, all_files);
253 p += lstrlen(p) + 1;
254 *p = '\0';
255 }
256
257 /***********************************************************************
258 * Enable/disable items on the menu based on control state
259 */
260 static VOID NOTEPAD_InitMenuPopup(HMENU menu, int index)
261 {
262 int enable;
263
264 CheckMenuItem(GetMenu(Globals.hMainWnd), CMD_WRAP,
265 MF_BYCOMMAND | (Globals.bWrapLongLines ? MF_CHECKED : MF_UNCHECKED));
266
267 EnableMenuItem(menu, CMD_UNDO,
268 SendMessage(Globals.hEdit, EM_CANUNDO, 0, 0) ? MF_ENABLED : MF_GRAYED);
269 EnableMenuItem(menu, CMD_PASTE,
270 IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED);
271 enable = SendMessage(Globals.hEdit, EM_GETSEL, 0, 0);
272 enable = (HIWORD(enable) == LOWORD(enable)) ? MF_GRAYED : MF_ENABLED;
273 EnableMenuItem(menu, CMD_CUT, enable);
274 EnableMenuItem(menu, CMD_COPY, enable);
275 EnableMenuItem(menu, CMD_DELETE, enable);
276
277 EnableMenuItem(menu, CMD_SELECT_ALL,
278 GetWindowTextLength(Globals.hEdit) ? MF_ENABLED : MF_GRAYED);
279 }
280
281 /***********************************************************************
282 *
283 * NOTEPAD_WndProc
284 */
285 static LRESULT WINAPI NOTEPAD_WndProc(HWND hWnd, UINT msg, WPARAM wParam,
286 LPARAM lParam)
287 {
288 switch (msg) {
289
290 case WM_CREATE:
291 {
292 static const WCHAR editW[] = { 'e','d','i','t',0 };
293 RECT rc;
294 GetClientRect(hWnd, &rc);
295 Globals.hEdit = CreateWindowEx(EDIT_EXSTYLE, editW, NULL, Globals.bWrapLongLines ? EDIT_STYLE_WRAP : EDIT_STYLE,
296 0, 0, rc.right, rc.bottom, hWnd,
297 NULL, Globals.hInstance, NULL);
298 if (!Globals.hEdit)
299 return -1;
300 SendMessage(Globals.hEdit, EM_LIMITTEXT, 0, 0);
301 if (Globals.hFont)
302 SendMessage(Globals.hEdit, WM_SETFONT, (WPARAM)Globals.hFont, (LPARAM)TRUE);
303 break;
304 }
305
306 case WM_COMMAND:
307 NOTEPAD_MenuCommand(LOWORD(wParam));
308 break;
309
310 case WM_DESTROYCLIPBOARD:
311 /*MessageBox(Globals.hMainWnd, "Empty clipboard", "Debug", MB_ICONEXCLAMATION);*/
312 break;
313
314 case WM_CLOSE:
315 if (DoCloseFile()) {
316 DestroyWindow(hWnd);
317 }
318 break;
319
320 case WM_QUERYENDSESSION:
321 if (DoCloseFile()) {
322 return 1;
323 }
324 break;
325
326 case WM_DESTROY:
327 PostQuitMessage(0);
328 break;
329
330 case WM_SIZE:
331 SetWindowPos(Globals.hEdit, NULL, 0, 0, LOWORD(lParam), HIWORD(lParam),
332 SWP_NOOWNERZORDER | SWP_NOZORDER);
333 break;
334
335 case WM_SETFOCUS:
336 SetFocus(Globals.hEdit);
337 break;
338
339 case WM_DROPFILES:
340 {
341 WCHAR szFileName[MAX_PATH];
342 HANDLE hDrop = (HANDLE) wParam;
343
344 DragQueryFile(hDrop, 0, szFileName, SIZEOF(szFileName));
345 DragFinish(hDrop);
346 DoOpenFile(szFileName);
347 break;
348 }
349
350 case WM_INITMENUPOPUP:
351 NOTEPAD_InitMenuPopup((HMENU)wParam, lParam);
352 break;
353
354 default:
355 if (msg == aFINDMSGSTRING)
356 {
357 FINDREPLACE *pFindReplace = (FINDREPLACE *) lParam;
358
359 if (pFindReplace->Flags & FR_FINDNEXT)
360 NOTEPAD_FindNext(pFindReplace, FALSE, TRUE);
361 else if (pFindReplace->Flags & FR_REPLACE)
362 NOTEPAD_FindNext(pFindReplace, TRUE, TRUE);
363 else if (pFindReplace->Flags & FR_REPLACEALL)
364 NOTEPAD_ReplaceAll(pFindReplace);
365 else if (pFindReplace->Flags & FR_DIALOGTERM)
366 NOTEPAD_FindTerm();
367 break;
368 }
369
370 return DefWindowProc(hWnd, msg, wParam, lParam);
371 }
372 return 0;
373 }
374
375 static int AlertFileDoesNotExist(LPCWSTR szFileName)
376 {
377 int nResult;
378 WCHAR szMessage[MAX_STRING_LEN];
379 WCHAR szResource[MAX_STRING_LEN];
380
381 LoadString(Globals.hInstance, STRING_DOESNOTEXIST, szResource, SIZEOF(szResource));
382 wsprintf(szMessage, szResource, szFileName);
383
384 LoadString(Globals.hInstance, STRING_NOTEPAD, szResource, SIZEOF(szResource));
385
386 nResult = MessageBox(Globals.hMainWnd, szMessage, szResource,
387 MB_ICONEXCLAMATION | MB_YESNO);
388
389 return(nResult);
390 }
391
392 static void HandleCommandLine(LPWSTR cmdline)
393 {
394 WCHAR delimiter;
395 int opt_print=0;
396
397 /* skip white space */
398 while (*cmdline == ' ') cmdline++;
399
400 /* skip executable name */
401 delimiter = (*cmdline == '"' ? '"' : ' ');
402
403 do
404 {
405 cmdline++;
406 }
407 while (*cmdline && *cmdline != delimiter);
408 if (*cmdline == delimiter) cmdline++;
409
410 while (*cmdline == ' ' || *cmdline == '-' || *cmdline == '/')
411 {
412 WCHAR option;
413
414 if (*cmdline++ == ' ') continue;
415
416 option = *cmdline;
417 if (option) cmdline++;
418 while (*cmdline == ' ') cmdline++;
419
420 switch(option)
421 {
422 case 'p':
423 case 'P':
424 opt_print=1;
425 break;
426 }
427 }
428
429 if (*cmdline)
430 {
431 /* file name is passed in the command line */
432 LPCWSTR file_name = NULL;
433 BOOL file_exists = FALSE;
434 WCHAR buf[MAX_PATH];
435
436 if (cmdline[0] == '"')
437 {
438 cmdline++;
439 cmdline[lstrlen(cmdline) - 1] = 0;
440 }
441
442 file_name = cmdline;
443 if (FileExists(file_name))
444 {
445 file_exists = TRUE;
446 }
447 else if (!HasFileExtension(cmdline))
448 {
449 static const WCHAR txtW[] = { '.','t','x','t',0 };
450
451 /* try to find file with ".txt" extension */
452 if (!lstrcmp(txtW, cmdline + lstrlen(cmdline) - lstrlen(txtW)))
453 {
454 file_exists = FALSE;
455 }
456 else
457 {
458 lstrcpyn(buf, cmdline, MAX_PATH - lstrlen(txtW) - 1);
459 lstrcat(buf, txtW);
460 file_name = buf;
461 file_exists = FileExists(file_name);
462 }
463 }
464
465 if (file_exists)
466 {
467 DoOpenFile(file_name);
468 InvalidateRect(Globals.hMainWnd, NULL, FALSE);
469 if (opt_print)
470 DIALOG_FilePrint();
471 }
472 else
473 {
474 switch (AlertFileDoesNotExist(file_name)) {
475 case IDYES:
476 DoOpenFile(file_name);
477 break;
478
479 case IDNO:
480 break;
481 }
482 }
483 }
484 }
485
486 /***********************************************************************
487 *
488 * WinMain
489 */
490 int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
491 {
492 MSG msg;
493 HACCEL hAccel;
494 WNDCLASSEX class;
495 static const WCHAR className[] = {'N','P','C','l','a','s','s',0};
496 static const WCHAR winName[] = {'N','o','t','e','p','a','d',0};
497
498 aFINDMSGSTRING = RegisterWindowMessage(FINDMSGSTRING);
499
500 ZeroMemory(&Globals, sizeof(Globals));
501 Globals.hInstance = hInstance;
502 LoadSettings();
503
504 ZeroMemory(&class, sizeof(class));
505 class.cbSize = sizeof(class);
506 class.lpfnWndProc = NOTEPAD_WndProc;
507 class.hInstance = Globals.hInstance;
508 class.hIcon = LoadIcon(0, IDI_APPLICATION);
509 class.hCursor = LoadCursor(0, IDC_ARROW);
510 class.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
511 class.lpszMenuName = MAKEINTRESOURCE(MAIN_MENU);
512 class.lpszClassName = className;
513
514 if (!RegisterClassEx(&class)) return FALSE;
515
516 /* Setup windows */
517
518 Globals.hMainWnd =
519 CreateWindow(className, winName, WS_OVERLAPPEDWINDOW,
520 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
521 NULL, NULL, Globals.hInstance, NULL);
522 if (!Globals.hMainWnd)
523 {
524 ShowLastError();
525 ExitProcess(1);
526 }
527
528 NOTEPAD_InitData();
529 DIALOG_FileNew();
530
531 ShowWindow(Globals.hMainWnd, show);
532 UpdateWindow(Globals.hMainWnd);
533 DragAcceptFiles(Globals.hMainWnd, TRUE);
534
535 HandleCommandLine(GetCommandLine());
536
537 hAccel = LoadAccelerators( hInstance, MAKEINTRESOURCE(ID_ACCEL) );
538
539 while (GetMessage(&msg, 0, 0, 0))
540 {
541 if (!IsDialogMessage(Globals.hFindReplaceDlg, &msg) &&
542 !TranslateAccelerator(Globals.hMainWnd, hAccel, &msg))
543 {
544 TranslateMessage(&msg);
545 DispatchMessage(&msg);
546 }
547 }
548 SaveSettings();
549 return msg.wParam;
550 }