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