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