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