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