1 /* 2 * PROJECT: PAINT for ReactOS 3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) 4 * PURPOSE: The main window and wWinMain etc. 5 * COPYRIGHT: Copyright 2015 Benedikt Freisen <b.freisen@gmx.net> 6 * Copyright 2017-2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 7 * Copyright 2018 Stanislav Motylkov <x86corez@gmail.com> 8 */ 9 10 #include "precomp.h" 11 12 #include <dlgs.h> 13 #include <mapi.h> 14 #include <assert.h> 15 16 BOOL g_askBeforeEnlarging = FALSE; // TODO: initialize from registry 17 HINSTANCE g_hinstExe = NULL; 18 WCHAR g_szFileName[MAX_LONG_PATH] = { 0 }; 19 WCHAR g_szMailTempFile[MAX_LONG_PATH] = { 0 }; 20 BOOL g_isAFile = FALSE; 21 BOOL g_imageSaved = FALSE; 22 BOOL g_showGrid = FALSE; 23 HWND g_hStatusBar = NULL; 24 25 CMainWindow mainWindow; 26 27 typedef HWND (WINAPI *FN_HtmlHelpW)(HWND, LPCWSTR, UINT, DWORD_PTR); 28 29 static HINSTANCE s_hHHCTRL_OCX = NULL; // HtmlHelpW needs "hhctrl.ocx" 30 static FN_HtmlHelpW s_pHtmlHelpW = NULL; 31 32 /* FUNCTIONS ********************************************************/ 33 34 void ShowOutOfMemory(void) 35 { 36 WCHAR szText[256]; 37 ::FormatMessageW(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, 38 NULL, 39 ERROR_OUTOFMEMORY, 40 0, 41 szText, _countof(szText), 42 NULL); 43 mainWindow.MessageBox(szText, NULL, MB_ICONERROR); 44 } 45 46 // get file name extension from filter string 47 static BOOL 48 FileExtFromFilter(LPWSTR pExt, OPENFILENAME *pOFN) 49 { 50 LPWSTR pchExt = pExt; 51 *pchExt = 0; 52 53 DWORD nIndex = 1; 54 for (LPCWSTR pch = pOFN->lpstrFilter; *pch; ++nIndex) 55 { 56 pch += lstrlen(pch) + 1; 57 if (pOFN->nFilterIndex == nIndex) 58 { 59 for (++pch; *pch && *pch != L';'; ++pch) 60 { 61 *pchExt++ = *pch; 62 } 63 *pchExt = 0; 64 CharLower(pExt); 65 return TRUE; 66 } 67 pch += wcslen(pch) + 1; 68 } 69 return FALSE; 70 } 71 72 // Hook procedure for OPENFILENAME to change the file name extension 73 static UINT_PTR APIENTRY 74 OFNHookProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 75 { 76 HWND hParent; 77 OFNOTIFYW *pon; 78 WCHAR Path[MAX_PATH]; 79 switch (uMsg) 80 { 81 case WM_NOTIFY: 82 pon = (OFNOTIFYW *)lParam; 83 if (pon->hdr.code == CDN_TYPECHANGE) 84 { 85 hParent = GetParent(hwnd); 86 SendMessageW(hParent, CDM_GETFILEPATH, _countof(Path), (LPARAM)Path); 87 FileExtFromFilter(PathFindExtensionW(Path), pon->lpOFN); 88 SendMessageW(hParent, CDM_SETCONTROLTEXT, cmb13, (LPARAM)PathFindFileNameW(Path)); 89 StringCchCopyW(pon->lpOFN->lpstrFile, pon->lpOFN->nMaxFile, Path); 90 } 91 break; 92 } 93 return 0; 94 } 95 96 typedef ULONG (WINAPI *FN_MAPISendMail)(LHANDLE, ULONG_PTR, lpMapiMessage, FLAGS, ULONG); 97 typedef ULONG (WINAPI *FN_MAPISendMailW)(LHANDLE, ULONG_PTR, lpMapiMessageW, FLAGS, ULONG); 98 99 BOOL OpenMailer(HWND hWnd, LPCWSTR pszPathName) 100 { 101 // Delete the temporary file if any 102 if (g_szMailTempFile[0]) 103 { 104 ::DeleteFileW(g_szMailTempFile); 105 g_szMailTempFile[0] = UNICODE_NULL; 106 } 107 108 CStringW strFileTitle; 109 if (PathFileExistsW(pszPathName) && imageModel.IsImageSaved()) 110 { 111 strFileTitle = PathFindFileNameW(pszPathName); 112 } 113 else // Not existing or not saved 114 { 115 // Get the name of a temporary file 116 WCHAR szTempDir[MAX_PATH]; 117 ::GetTempPathW(_countof(szTempDir), szTempDir); 118 if (!::GetTempFileNameW(szTempDir, L"afx", 0, g_szMailTempFile)) 119 return FALSE; // Failure 120 121 if (PathFileExistsW(g_szFileName)) 122 { 123 // Set file title 124 strFileTitle = PathFindFileNameW(g_szFileName); 125 126 // Copy to the temporary file 127 if (!::CopyFileW(g_szFileName, g_szMailTempFile, FALSE)) 128 { 129 g_szMailTempFile[0] = UNICODE_NULL; 130 return FALSE; // Failure 131 } 132 } 133 else 134 { 135 // Set file title 136 strFileTitle.LoadString(IDS_DEFAULTFILENAME); 137 strFileTitle += L".png"; 138 139 // Save it to the temporary file 140 HBITMAP hbmLocked = imageModel.LockBitmap(); 141 BOOL ret = SaveDIBToFile(hbmLocked, g_szMailTempFile, FALSE, Gdiplus::ImageFormatPNG); 142 imageModel.UnlockBitmap(hbmLocked); 143 if (!ret) 144 { 145 g_szMailTempFile[0] = UNICODE_NULL; 146 return FALSE; // Failure 147 } 148 } 149 150 // Use the temporary file 151 pszPathName = g_szMailTempFile; 152 } 153 154 // Load "mapi32.dll" 155 HINSTANCE hMAPI = LoadLibraryW(L"mapi32.dll"); 156 if (!hMAPI) 157 return FALSE; // Failure 158 159 // Attachment 160 MapiFileDescW attachmentW = { 0 }; 161 attachmentW.nPosition = (ULONG)-1; 162 attachmentW.lpszPathName = (LPWSTR)pszPathName; 163 attachmentW.lpszFileName = (LPWSTR)(LPCWSTR)strFileTitle; 164 165 // Message with attachment 166 MapiMessageW messageW = { 0 }; 167 messageW.lpszSubject = NULL; 168 messageW.nFileCount = 1; 169 messageW.lpFiles = &attachmentW; 170 171 // First, try to open the mailer by the function of Unicode version 172 FN_MAPISendMailW pMAPISendMailW = (FN_MAPISendMailW)::GetProcAddress(hMAPI, "MAPISendMailW"); 173 if (pMAPISendMailW) 174 { 175 pMAPISendMailW(0, (ULONG_PTR)hWnd, &messageW, MAPI_DIALOG | MAPI_LOGON_UI, 0); 176 ::FreeLibrary(hMAPI); 177 return TRUE; // MAPISendMailW will show an error message on failure 178 } 179 180 // Convert to ANSI strings 181 CStringA szPathNameA(pszPathName), szFileTitleA(strFileTitle); 182 183 MapiFileDesc attachment = { 0 }; 184 attachment.nPosition = (ULONG)-1; 185 attachment.lpszPathName = (LPSTR)(LPCSTR)szPathNameA; 186 attachment.lpszFileName = (LPSTR)(LPCSTR)szFileTitleA; 187 188 MapiMessage message = { 0 }; 189 message.lpszSubject = NULL; 190 message.nFileCount = 1; 191 message.lpFiles = &attachment; 192 193 // Try again but in ANSI version 194 FN_MAPISendMail pMAPISendMail = (FN_MAPISendMail)::GetProcAddress(hMAPI, "MAPISendMail"); 195 if (pMAPISendMail) 196 { 197 pMAPISendMail(0, (ULONG_PTR)hWnd, &message, MAPI_DIALOG | MAPI_LOGON_UI, 0); 198 ::FreeLibrary(hMAPI); 199 return TRUE; // MAPISendMail will show an error message on failure 200 } 201 202 ::FreeLibrary(hMAPI); 203 return FALSE; // Failure 204 } 205 206 BOOL CMainWindow::GetOpenFileName(IN OUT LPWSTR pszFile, INT cchMaxFile) 207 { 208 static OPENFILENAMEW ofn = { 0 }; 209 static CStringW strFilter; 210 211 if (ofn.lStructSize == 0) 212 { 213 // The "All Files" item text 214 CStringW strAllPictureFiles; 215 strAllPictureFiles.LoadString(g_hinstExe, IDS_ALLPICTUREFILES); 216 217 // Get the import filter 218 CSimpleArray<GUID> aguidFileTypesI; 219 CImage::GetImporterFilterString(strFilter, aguidFileTypesI, strAllPictureFiles, 220 CImage::excludeDefaultLoad, L'|'); 221 strFilter.Replace(L'|', UNICODE_NULL); 222 223 // Initializing the OPENFILENAME structure for GetOpenFileName 224 ZeroMemory(&ofn, sizeof(ofn)); 225 ofn.lStructSize = sizeof(ofn); 226 ofn.hwndOwner = m_hWnd; 227 ofn.hInstance = g_hinstExe; 228 ofn.lpstrFilter = strFilter; 229 ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY; 230 ofn.lpstrDefExt = L"png"; 231 } 232 233 ofn.lpstrFile = pszFile; 234 ofn.nMaxFile = cchMaxFile; 235 return ::GetOpenFileNameW(&ofn); 236 } 237 238 BOOL CMainWindow::GetSaveFileName(IN OUT LPWSTR pszFile, INT cchMaxFile) 239 { 240 static OPENFILENAMEW sfn = { 0 }; 241 static CStringW strFilter; 242 243 if (sfn.lStructSize == 0) 244 { 245 // Get the export filter 246 CSimpleArray<GUID> aguidFileTypesE; 247 CImage::GetExporterFilterString(strFilter, aguidFileTypesE, NULL, 248 CImage::excludeDefaultSave, L'|'); 249 strFilter.Replace(L'|', UNICODE_NULL); 250 251 // Initializing the OPENFILENAME structure for GetSaveFileName 252 ZeroMemory(&sfn, sizeof(sfn)); 253 sfn.lStructSize = sizeof(sfn); 254 sfn.hwndOwner = m_hWnd; 255 sfn.hInstance = g_hinstExe; 256 sfn.lpstrFilter = strFilter; 257 sfn.Flags = OFN_EXPLORER | OFN_OVERWRITEPROMPT | OFN_ENABLEHOOK; 258 sfn.lpfnHook = OFNHookProc; 259 sfn.lpstrDefExt = L"png"; 260 261 LPWSTR pchDotExt = PathFindExtensionW(pszFile); 262 if (*pchDotExt == UNICODE_NULL) 263 { 264 // Choose PNG 265 StringCchCatW(pszFile, cchMaxFile, L".png"); 266 for (INT i = 0; i < aguidFileTypesE.GetSize(); ++i) 267 { 268 if (aguidFileTypesE[i] == Gdiplus::ImageFormatPNG) 269 { 270 sfn.nFilterIndex = i + 1; 271 break; 272 } 273 } 274 } 275 } 276 277 sfn.lpstrFile = pszFile; 278 sfn.nMaxFile = cchMaxFile; 279 return ::GetSaveFileNameW(&sfn); 280 } 281 282 BOOL CMainWindow::ChooseColor(IN OUT COLORREF *prgbColor) 283 { 284 static CHOOSECOLOR choosecolor = { 0 }; 285 static COLORREF custColors[16] = 286 { 287 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 288 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff 289 }; 290 291 if (choosecolor.lStructSize == 0) 292 { 293 // Initializing the CHOOSECOLOR structure for ChooseColor 294 ZeroMemory(&choosecolor, sizeof(choosecolor)); 295 choosecolor.lStructSize = sizeof(choosecolor); 296 choosecolor.hwndOwner = m_hWnd; 297 choosecolor.lpCustColors = custColors; 298 } 299 300 choosecolor.Flags = CC_RGBINIT; 301 choosecolor.rgbResult = *prgbColor; 302 if (!::ChooseColor(&choosecolor)) 303 return FALSE; 304 305 *prgbColor = choosecolor.rgbResult; 306 return TRUE; 307 } 308 309 HWND CMainWindow::DoCreate() 310 { 311 ::LoadStringW(g_hinstExe, IDS_DEFAULTFILENAME, g_szFileName, _countof(g_szFileName)); 312 313 CStringW strTitle; 314 strTitle.Format(IDS_WINDOWTITLE, PathFindFileName(g_szFileName)); 315 316 RECT& rc = registrySettings.WindowPlacement.rcNormalPosition; 317 return Create(HWND_DESKTOP, rc, strTitle, WS_OVERLAPPEDWINDOW, WS_EX_ACCEPTFILES); 318 } 319 320 // A wrapper function for HtmlHelpW 321 static HWND DoHtmlHelpW(HWND hwndCaller, LPCWSTR pszFile, UINT uCommand, DWORD_PTR dwData) 322 { 323 WCHAR szPath[MAX_PATH]; 324 325 if (!s_hHHCTRL_OCX && (uCommand != HH_CLOSE_ALL)) 326 { 327 // The function loads the system library, not local 328 GetSystemDirectoryW(szPath, _countof(szPath)); 329 StringCchCatW(szPath, _countof(szPath), L"\\hhctrl.ocx"); 330 s_hHHCTRL_OCX = LoadLibraryW(szPath); 331 if (s_hHHCTRL_OCX) 332 s_pHtmlHelpW = (FN_HtmlHelpW)GetProcAddress(s_hHHCTRL_OCX, "HtmlHelpW"); 333 } 334 335 if (!s_pHtmlHelpW) 336 return NULL; 337 338 return s_pHtmlHelpW(hwndCaller, pszFile, uCommand, dwData); 339 } 340 341 void CMainWindow::alignChildrenToMainWindow() 342 { 343 RECT clientRect, rc; 344 GetClientRect(&clientRect); 345 RECT rcSpace = clientRect; 346 const UINT uFlags = (SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION | SWP_NOCOPYBITS); 347 348 if (::IsWindowVisible(g_hStatusBar)) 349 { 350 ::GetWindowRect(g_hStatusBar, &rc); 351 rcSpace.bottom -= rc.bottom - rc.top; 352 } 353 354 HDWP hDWP = ::BeginDeferWindowPos(3); 355 356 if (::IsWindowVisible(toolBoxContainer)) 357 { 358 if (registrySettings.Bar2ID == BAR2ID_RIGHT) 359 { 360 hDWP = ::DeferWindowPos(hDWP, toolBoxContainer, NULL, 361 rcSpace.right - CX_TOOLBAR, rcSpace.top, 362 CX_TOOLBAR, rcSpace.bottom - rcSpace.top, 363 uFlags); 364 rcSpace.right -= CX_TOOLBAR; 365 } 366 else 367 { 368 hDWP = ::DeferWindowPos(hDWP, toolBoxContainer, NULL, 369 rcSpace.left, rcSpace.top, 370 CX_TOOLBAR, rcSpace.bottom - rcSpace.top, 371 uFlags); 372 rcSpace.left += CX_TOOLBAR; 373 } 374 } 375 376 if (::IsWindowVisible(paletteWindow)) 377 { 378 if (registrySettings.Bar1ID == BAR1ID_BOTTOM) 379 { 380 hDWP = ::DeferWindowPos(hDWP, paletteWindow, NULL, 381 rcSpace.left, rcSpace.bottom - CY_PALETTE, 382 rcSpace.right - rcSpace.left, CY_PALETTE, 383 uFlags); 384 rcSpace.bottom -= CY_PALETTE; 385 } 386 else 387 { 388 hDWP = ::DeferWindowPos(hDWP, paletteWindow, NULL, 389 rcSpace.left, rcSpace.top, 390 rcSpace.right - rcSpace.left, CY_PALETTE, 391 uFlags); 392 rcSpace.top += CY_PALETTE; 393 } 394 } 395 396 if (canvasWindow.IsWindow()) 397 { 398 hDWP = ::DeferWindowPos(hDWP, canvasWindow, NULL, 399 rcSpace.left, rcSpace.top, 400 rcSpace.right - rcSpace.left, rcSpace.bottom - rcSpace.top, 401 uFlags); 402 } 403 404 ::EndDeferWindowPos(hDWP); 405 } 406 407 void CMainWindow::saveImage(BOOL overwrite) 408 { 409 canvasWindow.OnEndDraw(FALSE); 410 411 // Is the extension not supported? 412 PWCHAR pchDotExt = PathFindExtensionW(g_szFileName); 413 if (pchDotExt && *pchDotExt && !CImageDx::IsExtensionSupported(pchDotExt)) 414 { 415 // Remove the extension 416 PathRemoveExtensionW(g_szFileName); 417 // No overwrite 418 overwrite = FALSE; 419 } 420 421 if (g_isAFile && overwrite) 422 { 423 imageModel.SaveImage(g_szFileName); 424 } 425 else if (GetSaveFileName(g_szFileName, _countof(g_szFileName))) 426 { 427 imageModel.SaveImage(g_szFileName); 428 } 429 } 430 431 void CMainWindow::InsertSelectionFromHBITMAP(HBITMAP bitmap, HWND window) 432 { 433 int width = GetDIBWidth(bitmap); 434 int height = GetDIBHeight(bitmap); 435 int curWidth = imageModel.GetWidth(); 436 int curHeight = imageModel.GetHeight(); 437 438 if (width > curWidth || height > curHeight) 439 { 440 BOOL shouldEnlarge = TRUE; 441 442 if (g_askBeforeEnlarging) 443 { 444 WCHAR programname[20]; 445 WCHAR shouldEnlargePromptText[100]; 446 447 ::LoadStringW(g_hinstExe, IDS_PROGRAMNAME, programname, _countof(programname)); 448 ::LoadStringW(g_hinstExe, IDS_ENLARGEPROMPTTEXT, shouldEnlargePromptText, _countof(shouldEnlargePromptText)); 449 450 switch (MessageBox(shouldEnlargePromptText, programname, MB_YESNOCANCEL | MB_ICONQUESTION)) 451 { 452 case IDYES: 453 break; 454 case IDNO: 455 shouldEnlarge = FALSE; 456 break; 457 case IDCANCEL: 458 return; 459 } 460 } 461 462 if (shouldEnlarge) 463 { 464 if (width > curWidth) 465 curWidth = width; 466 467 if (height > curHeight) 468 curHeight = height; 469 470 imageModel.Crop(curWidth, curHeight, 0, 0); 471 } 472 } 473 474 toolsModel.SetActiveTool(TOOL_RECTSEL); 475 476 selectionModel.InsertFromHBITMAP(bitmap, 0, 0); 477 selectionModel.m_bShow = TRUE; 478 imageModel.NotifyImageChanged(); 479 } 480 481 LRESULT CMainWindow::OnMouseWheel(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 482 { 483 INT zDelta = (SHORT)HIWORD(wParam); 484 485 if (::GetKeyState(VK_CONTROL) < 0) // Ctrl+Wheel 486 { 487 if (zDelta < 0) 488 { 489 if (toolsModel.GetZoom() > MIN_ZOOM) 490 canvasWindow.zoomTo(toolsModel.GetZoom() / 2); 491 } 492 else if (zDelta > 0) 493 { 494 if (toolsModel.GetZoom() < MAX_ZOOM) 495 canvasWindow.zoomTo(toolsModel.GetZoom() * 2); 496 } 497 } 498 else // Wheel only 499 { 500 UINT nCount = 3; 501 if (::GetAsyncKeyState(VK_SHIFT) < 0) 502 { 503 #ifndef SPI_GETWHEELSCROLLCHARS 504 #define SPI_GETWHEELSCROLLCHARS 0x006C // Needed for pre-NT6 PSDK 505 #endif 506 SystemParametersInfoW(SPI_GETWHEELSCROLLCHARS, 0, &nCount, 0); 507 for (UINT i = 0; i < nCount; ++i) 508 { 509 if (zDelta < 0) 510 ::PostMessageW(canvasWindow, WM_HSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0); 511 else if (zDelta > 0) 512 ::PostMessageW(canvasWindow, WM_HSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0); 513 } 514 } 515 else 516 { 517 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES, 0, &nCount, 0); 518 for (UINT i = 0; i < nCount; ++i) 519 { 520 if (zDelta < 0) 521 ::PostMessageW(canvasWindow, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0); 522 else if (zDelta > 0) 523 ::PostMessageW(canvasWindow, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0); 524 } 525 } 526 } 527 528 return 0; 529 } 530 531 LRESULT CMainWindow::OnDropFiles(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 532 { 533 WCHAR droppedfile[MAX_PATH]; 534 535 HDROP hDrop = (HDROP)wParam; 536 DragQueryFile(hDrop, 0, droppedfile, _countof(droppedfile)); 537 DragFinish(hDrop); 538 539 ConfirmSave() && DoLoadImageFile(m_hWnd, droppedfile, TRUE); 540 541 return 0; 542 } 543 544 LRESULT CMainWindow::OnCreate(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 545 { 546 // Loading and setting the window menu from resource 547 m_hMenu = ::LoadMenuW(g_hinstExe, MAKEINTRESOURCEW(ID_MENU)); 548 SetMenu(m_hMenu); 549 550 // Create the status bar 551 DWORD style = SBARS_SIZEGRIP | WS_CHILD | (registrySettings.ShowStatusBar ? WS_VISIBLE : 0); 552 g_hStatusBar = ::CreateWindowExW(0, STATUSCLASSNAME, NULL, style, 0, 0, 0, 0, m_hWnd, 553 NULL, g_hinstExe, NULL); 554 ::SendMessageW(g_hStatusBar, SB_SETMINHEIGHT, 21, 0); 555 556 // Create the tool box 557 toolBoxContainer.DoCreate(m_hWnd); 558 559 // Create the palette window 560 RECT rcEmpty = { 0, 0, 0, 0 }; // Rely on WM_SIZE 561 style = WS_CHILD | (registrySettings.ShowPalette ? WS_VISIBLE : 0); 562 paletteWindow.Create(m_hWnd, rcEmpty, NULL, style, WS_EX_STATICEDGE); 563 564 // Create the canvas 565 style = WS_CHILD | WS_GROUP | WS_HSCROLL | WS_VSCROLL | WS_VISIBLE; 566 canvasWindow.Create(m_hWnd, rcEmpty, NULL, style, WS_EX_CLIENTEDGE); 567 568 // Create and show the miniature if necessary 569 if (registrySettings.ShowThumbnail) 570 { 571 miniature.DoCreate(m_hWnd); 572 miniature.ShowWindow(SW_SHOWNOACTIVATE); 573 } 574 575 // Set icon 576 SendMessage(WM_SETICON, ICON_BIG, (LPARAM)::LoadIconW(g_hinstExe, MAKEINTRESOURCEW(IDI_APPICON))); 577 SendMessage(WM_SETICON, ICON_SMALL, (LPARAM)::LoadIconW(g_hinstExe, MAKEINTRESOURCEW(IDI_APPICON))); 578 579 return 0; 580 } 581 582 LRESULT CMainWindow::OnDestroy(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 583 { 584 registrySettings.WindowPlacement.length = sizeof(WINDOWPLACEMENT); 585 GetWindowPlacement(&(registrySettings.WindowPlacement)); 586 587 DoHtmlHelpW(NULL, NULL, HH_CLOSE_ALL, 0); 588 589 if (s_hHHCTRL_OCX) 590 { 591 FreeLibrary(s_hHHCTRL_OCX); 592 s_hHHCTRL_OCX = NULL; 593 s_pHtmlHelpW = NULL; 594 } 595 596 SetMenu(NULL); 597 if (m_hMenu) 598 { 599 ::DestroyMenu(m_hMenu); 600 m_hMenu = NULL; 601 } 602 603 PostQuitMessage(0); /* send a WM_QUIT to the message queue */ 604 return 0; 605 } 606 607 BOOL CMainWindow::ConfirmSave() 608 { 609 canvasWindow.OnEndDraw(FALSE); 610 611 if (imageModel.IsImageSaved()) 612 return TRUE; 613 614 CStringW strProgramName; 615 strProgramName.LoadString(IDS_PROGRAMNAME); 616 617 CStringW strSavePromptText; 618 strSavePromptText.Format(IDS_SAVEPROMPTTEXT, PathFindFileName(g_szFileName)); 619 620 switch (MessageBox(strSavePromptText, strProgramName, MB_YESNOCANCEL | MB_ICONQUESTION)) 621 { 622 case IDYES: 623 saveImage(TRUE); 624 return imageModel.IsImageSaved(); 625 case IDNO: 626 return TRUE; 627 case IDCANCEL: 628 return FALSE; 629 } 630 631 return TRUE; 632 } 633 634 LRESULT CMainWindow::OnClose(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 635 { 636 if (ConfirmSave()) 637 { 638 DestroyWindow(); 639 } 640 return 0; 641 } 642 643 void CMainWindow::ProcessFileMenu(HMENU hPopupMenu) 644 { 645 LPCWSTR dotext = PathFindExtensionW(g_szFileName); 646 BOOL isBMP = FALSE; 647 if (_wcsicmp(dotext, L".bmp") == 0 || 648 _wcsicmp(dotext, L".dib") == 0 || 649 _wcsicmp(dotext, L".rle") == 0) 650 { 651 isBMP = TRUE; 652 } 653 654 UINT uWallpaperEnabled = ENABLED_IF(g_isAFile && isBMP && g_fileSize > 0); 655 ::EnableMenuItem(hPopupMenu, IDM_FILEASWALLPAPERPLANE, uWallpaperEnabled); 656 ::EnableMenuItem(hPopupMenu, IDM_FILEASWALLPAPERCENTERED, uWallpaperEnabled); 657 ::EnableMenuItem(hPopupMenu, IDM_FILEASWALLPAPERSTRETCHED, uWallpaperEnabled); 658 659 for (INT iItem = 0; iItem < MAX_RECENT_FILES; ++iItem) 660 RemoveMenu(hPopupMenu, IDM_FILE1 + iItem, MF_BYCOMMAND); 661 662 if (registrySettings.strFiles[0].IsEmpty()) 663 return; 664 665 RemoveMenu(hPopupMenu, IDM_FILEMOSTRECENTLYUSEDFILE, MF_BYCOMMAND); 666 667 INT cMenuItems = GetMenuItemCount(hPopupMenu); 668 669 for (INT iItem = 0; iItem < MAX_RECENT_FILES; ++iItem) 670 { 671 CStringW& strFile = registrySettings.strFiles[iItem]; 672 if (strFile.IsEmpty()) 673 break; 674 675 // Condense the lengthy pathname by using '...' 676 #define MAX_RECENT_PATHNAME_DISPLAY 30 677 CPath pathFile(strFile); 678 pathFile.CompactPathEx(MAX_RECENT_PATHNAME_DISPLAY); 679 assert(wcslen((LPCWSTR)pathFile) <= MAX_RECENT_PATHNAME_DISPLAY); 680 681 // Add an accelerator (by '&') to the item number for quick access 682 WCHAR szText[4 + MAX_RECENT_PATHNAME_DISPLAY + 1]; 683 StringCchPrintfW(szText, _countof(szText), L"&%u %s", iItem + 1, (LPCWSTR)pathFile); 684 685 INT iMenuItem = (cMenuItems - 2) + iItem; 686 InsertMenu(hPopupMenu, iMenuItem, MF_BYPOSITION | MF_STRING, IDM_FILE1 + iItem, szText); 687 } 688 } 689 690 BOOL CMainWindow::CanUndo() const 691 { 692 if (toolsModel.GetActiveTool() == TOOL_TEXT && ::IsWindowVisible(textEditWindow)) 693 return (BOOL)textEditWindow.SendMessage(EM_CANUNDO); 694 if (selectionModel.m_bShow && toolsModel.IsSelection()) 695 return TRUE; 696 return imageModel.CanUndo(); 697 } 698 699 BOOL CMainWindow::CanRedo() const 700 { 701 if (toolsModel.GetActiveTool() == TOOL_TEXT && ::IsWindowVisible(textEditWindow)) 702 return FALSE; // There is no "WM_REDO" in EDIT control 703 return imageModel.CanRedo(); 704 } 705 706 BOOL CMainWindow::CanPaste() const 707 { 708 if (toolsModel.GetActiveTool() == TOOL_TEXT && ::IsWindowVisible(textEditWindow)) 709 return ::IsClipboardFormatAvailable(CF_UNICODETEXT); 710 711 return (::IsClipboardFormatAvailable(CF_ENHMETAFILE) || 712 ::IsClipboardFormatAvailable(CF_DIB) || 713 ::IsClipboardFormatAvailable(CF_BITMAP)); 714 } 715 716 LRESULT CMainWindow::OnInitMenuPopup(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 717 { 718 HMENU menu = (HMENU)wParam; 719 BOOL trueSelection = (selectionModel.m_bShow && toolsModel.IsSelection()); 720 BOOL textShown = (toolsModel.GetActiveTool() == TOOL_TEXT && ::IsWindowVisible(textEditWindow)); 721 DWORD dwStart = 0, dwEnd = 0; 722 if (textShown) 723 textEditWindow.SendMessage(EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd); 724 BOOL hasTextSel = (dwStart < dwEnd); 725 726 // 727 // File menu 728 // 729 if (::GetSubMenu(GetMenu(), 0) == menu) 730 { 731 ProcessFileMenu(menu); 732 } 733 734 // 735 // Edit menu 736 // 737 EnableMenuItem(menu, IDM_EDITUNDO, ENABLED_IF(CanUndo())); 738 EnableMenuItem(menu, IDM_EDITREDO, ENABLED_IF(CanRedo())); 739 EnableMenuItem(menu, IDM_EDITCUT, ENABLED_IF(textShown ? hasTextSel : trueSelection)); 740 EnableMenuItem(menu, IDM_EDITCOPY, ENABLED_IF(textShown ? hasTextSel : trueSelection)); 741 EnableMenuItem(menu, IDM_EDITDELETESELECTION, 742 ENABLED_IF(textShown ? hasTextSel : trueSelection)); 743 EnableMenuItem(menu, IDM_EDITINVERTSELECTION, ENABLED_IF(trueSelection)); 744 EnableMenuItem(menu, IDM_EDITCOPYTO, ENABLED_IF(trueSelection)); 745 EnableMenuItem(menu, IDM_EDITPASTE, ENABLED_IF(CanPaste())); 746 EnableMenuItem(menu, IDM_CROPSELECTION, ENABLED_IF(trueSelection)); 747 748 // 749 // View menu 750 // 751 CheckMenuItem(menu, IDM_VIEWTOOLBOX, CHECKED_IF(::IsWindowVisible(toolBoxContainer))); 752 CheckMenuItem(menu, IDM_VIEWCOLORPALETTE, CHECKED_IF(::IsWindowVisible(paletteWindow))); 753 CheckMenuItem(menu, IDM_VIEWSTATUSBAR, CHECKED_IF(::IsWindowVisible(g_hStatusBar))); 754 CheckMenuItem(menu, IDM_FORMATICONBAR, CHECKED_IF(::IsWindowVisible(fontsDialog))); 755 EnableMenuItem(menu, IDM_FORMATICONBAR, ENABLED_IF(toolsModel.GetActiveTool() == TOOL_TEXT)); 756 CheckMenuItem(menu, IDM_VIEWZOOM125, CHECKED_IF(toolsModel.GetZoom() == 125)); 757 CheckMenuItem(menu, IDM_VIEWZOOM25, CHECKED_IF(toolsModel.GetZoom() == 250)); 758 CheckMenuItem(menu, IDM_VIEWZOOM50, CHECKED_IF(toolsModel.GetZoom() == 500)); 759 CheckMenuItem(menu, IDM_VIEWZOOM100, CHECKED_IF(toolsModel.GetZoom() == 1000)); 760 CheckMenuItem(menu, IDM_VIEWZOOM200, CHECKED_IF(toolsModel.GetZoom() == 2000)); 761 CheckMenuItem(menu, IDM_VIEWZOOM400, CHECKED_IF(toolsModel.GetZoom() == 4000)); 762 CheckMenuItem(menu, IDM_VIEWZOOM800, CHECKED_IF(toolsModel.GetZoom() == 8000)); 763 CheckMenuItem(menu, IDM_VIEWSHOWGRID, CHECKED_IF(g_showGrid)); 764 CheckMenuItem(menu, IDM_VIEWSHOWMINIATURE, CHECKED_IF(registrySettings.ShowThumbnail)); 765 766 // 767 // Image menu 768 // 769 EnableMenuItem(menu, IDM_IMAGECROP, ENABLED_IF(selectionModel.m_bShow)); 770 EnableMenuItem(menu, IDM_IMAGEDELETEIMAGE, ENABLED_IF(!selectionModel.m_bShow)); 771 CheckMenuItem(menu, IDM_IMAGEDRAWOPAQUE, CHECKED_IF(!toolsModel.IsBackgroundTransparent())); 772 773 // 774 // Palette menu 775 // 776 CheckMenuItem(menu, IDM_COLORSMODERNPALETTE, CHECKED_IF(paletteModel.SelectedPalette() == PAL_MODERN)); 777 CheckMenuItem(menu, IDM_COLORSOLDPALETTE, CHECKED_IF(paletteModel.SelectedPalette() == PAL_OLDTYPE)); 778 return 0; 779 } 780 781 LRESULT CMainWindow::OnSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 782 { 783 int test[] = { LOWORD(lParam) - 260, LOWORD(lParam) - 140, LOWORD(lParam) - 20 }; 784 if (::IsWindow(g_hStatusBar)) 785 { 786 ::SendMessageW(g_hStatusBar, WM_SIZE, 0, 0); 787 ::SendMessageW(g_hStatusBar, SB_SETPARTS, 3, (LPARAM)&test); 788 } 789 alignChildrenToMainWindow(); 790 return 0; 791 } 792 793 LRESULT CMainWindow::OnGetMinMaxInfo(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 794 { 795 MINMAXINFO *mm = (MINMAXINFO*)lParam; 796 mm->ptMinTrackSize = { 330, 360 }; 797 return 0; 798 } 799 800 LRESULT CMainWindow::OnKeyDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 801 { 802 switch (wParam) 803 { 804 case VK_ESCAPE: 805 canvasWindow.PostMessage(nMsg, wParam, lParam); 806 break; 807 case VK_LEFT: 808 selectionModel.moveSelection(-1, 0); 809 break; 810 case VK_RIGHT: 811 selectionModel.moveSelection(+1, 0); 812 break; 813 case VK_UP: 814 selectionModel.moveSelection(0, -1); 815 break; 816 case VK_DOWN: 817 selectionModel.moveSelection(0, +1); 818 break; 819 default: 820 break; 821 } 822 return 0; 823 } 824 825 LRESULT CMainWindow::OnSysColorChange(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 826 { 827 /* Redirect message to common controls */ 828 HWND hToolbar = FindWindowEx(toolBoxContainer.m_hWnd, NULL, TOOLBARCLASSNAME, NULL); 829 ::SendMessageW(hToolbar, WM_SYSCOLORCHANGE, 0, 0); 830 return 0; 831 } 832 833 LRESULT CMainWindow::OnCommand(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 834 { 835 // Disable commands while dragging mouse 836 if (canvasWindow.m_drawing && ::GetCapture()) 837 { 838 ATLTRACE("locking!\n"); 839 return 0; 840 } 841 842 BOOL textShown = (toolsModel.GetActiveTool() == TOOL_TEXT && ::IsWindowVisible(textEditWindow)); 843 switch (LOWORD(wParam)) 844 { 845 case IDM_HELPINFO: 846 { 847 WCHAR infotitle[100], infotext[200]; 848 ::LoadStringW(g_hinstExe, IDS_INFOTITLE, infotitle, _countof(infotitle)); 849 ::LoadStringW(g_hinstExe, IDS_INFOTEXT, infotext, _countof(infotext)); 850 ::ShellAboutW(m_hWnd, infotitle, infotext, 851 LoadIconW(g_hinstExe, MAKEINTRESOURCEW(IDI_APPICON))); 852 break; 853 } 854 case IDM_HELPHELPTOPICS: 855 DoHtmlHelpW(m_hWnd, L"%SystemRoot%\\Help\\mspaint.chm", HH_DISPLAY_TOPIC, 0); 856 break; 857 case IDM_FILEEXIT: 858 SendMessage(WM_CLOSE, wParam, lParam); 859 break; 860 case IDM_FILENEW: 861 if (ConfirmSave()) 862 { 863 InitializeImage(NULL, NULL, FALSE); 864 } 865 break; 866 case IDM_FILEOPEN: 867 { 868 WCHAR szFileName[MAX_LONG_PATH] = L""; 869 if (ConfirmSave() && GetOpenFileName(szFileName, _countof(szFileName))) 870 { 871 DoLoadImageFile(m_hWnd, szFileName, TRUE); 872 } 873 break; 874 } 875 case IDM_FILESAVE: 876 saveImage(TRUE); 877 break; 878 case IDM_FILESAVEAS: 879 saveImage(FALSE); 880 break; 881 case IDM_FILEPAGESETUP: 882 // DUMMY: Shows the dialog only, no functionality 883 PAGESETUPDLG psd; 884 ZeroMemory(&psd, sizeof(psd)); 885 psd.lStructSize = sizeof(psd); 886 psd.hwndOwner = m_hWnd; 887 PageSetupDlg(&psd); 888 break; 889 case IDM_FILEPRINT: 890 // TODO: Test whether it actually works 891 PRINTDLG pd; 892 ZeroMemory(&pd, sizeof(pd)); 893 pd.lStructSize = sizeof(pd); 894 pd.hwndOwner = m_hWnd; 895 pd.hDevMode = NULL; // freed by user 896 pd.hDevNames = NULL; // freed by user 897 pd.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC; 898 pd.nCopies = 1; 899 pd.nFromPage = 0xffff; 900 pd.nToPage = 0xffff; 901 pd.nMinPage = 1; 902 pd.nMaxPage = 0xffff; 903 if (PrintDlg(&pd) == TRUE) 904 { 905 ::BitBlt(pd.hDC, 0, 0, imageModel.GetWidth(), imageModel.GetHeight(), imageModel.GetDC(), 0, 0, SRCCOPY); 906 DeleteDC(pd.hDC); 907 } 908 if (pd.hDevMode) 909 GlobalFree(pd.hDevMode); 910 if (pd.hDevNames) 911 GlobalFree(pd.hDevNames); 912 break; 913 case IDM_FILESEND: 914 canvasWindow.OnEndDraw(FALSE); 915 if (!OpenMailer(m_hWnd, g_szFileName)) 916 { 917 ShowError(IDS_CANTSENDMAIL); 918 } 919 break; 920 case IDM_FILEASWALLPAPERPLANE: 921 RegistrySettings::SetWallpaper(g_szFileName, RegistrySettings::TILED); 922 break; 923 case IDM_FILEASWALLPAPERCENTERED: 924 RegistrySettings::SetWallpaper(g_szFileName, RegistrySettings::CENTERED); 925 break; 926 case IDM_FILEASWALLPAPERSTRETCHED: 927 RegistrySettings::SetWallpaper(g_szFileName, RegistrySettings::STRETCHED); 928 break; 929 case IDM_FILE1: 930 case IDM_FILE2: 931 case IDM_FILE3: 932 case IDM_FILE4: 933 { 934 INT iFile = LOWORD(wParam) - IDM_FILE1; 935 if (ConfirmSave()) 936 DoLoadImageFile(m_hWnd, registrySettings.strFiles[iFile], TRUE); 937 break; 938 } 939 case IDM_EDITUNDO: 940 if (textShown) 941 { 942 textEditWindow.PostMessage(WM_UNDO, 0, 0); 943 break; 944 } 945 canvasWindow.OnEndDraw(FALSE); 946 imageModel.Undo(); 947 break; 948 case IDM_EDITREDO: 949 if (textShown) 950 { 951 // There is no "WM_REDO" in EDIT control 952 break; 953 } 954 canvasWindow.OnEndDraw(FALSE); 955 imageModel.Redo(); 956 break; 957 case IDM_EDITCOPY: 958 if (textShown) 959 { 960 textEditWindow.SendMessage(WM_COPY); 961 break; 962 } 963 if (!selectionModel.m_bShow || !OpenClipboard()) 964 break; 965 966 EmptyClipboard(); 967 968 selectionModel.TakeOff(); 969 970 { 971 HBITMAP hbmCopy = selectionModel.GetSelectionContents(); 972 HGLOBAL hGlobal = BitmapToClipboardDIB(hbmCopy); 973 if (hGlobal) 974 ::SetClipboardData(CF_DIB, hGlobal); 975 else 976 ShowOutOfMemory(); 977 ::DeleteObject(hbmCopy); 978 } 979 980 CloseClipboard(); 981 break; 982 case IDM_EDITCUT: 983 if (textShown) 984 { 985 textEditWindow.SendMessage(WM_CUT); 986 break; 987 } 988 /* Copy */ 989 SendMessage(WM_COMMAND, IDM_EDITCOPY, 0); 990 /* Delete selection */ 991 SendMessage(WM_COMMAND, IDM_EDITDELETESELECTION, 0); 992 break; 993 case IDM_EDITPASTE: 994 if (textShown) 995 { 996 textEditWindow.SendMessage(WM_PASTE); 997 break; 998 } 999 1000 if (!OpenClipboard()) 1001 break; 1002 1003 // In many cases, CF_ENHMETAFILE provides a better image than CF_DIB 1004 if (::IsClipboardFormatAvailable(CF_ENHMETAFILE)) 1005 { 1006 HENHMETAFILE hEMF = (HENHMETAFILE)::GetClipboardData(CF_ENHMETAFILE); 1007 if (hEMF) 1008 { 1009 HBITMAP hbm = BitmapFromHEMF(hEMF); 1010 ::DeleteEnhMetaFile(hEMF); 1011 if (hbm) 1012 { 1013 InsertSelectionFromHBITMAP(hbm, m_hWnd); 1014 CloseClipboard(); 1015 break; 1016 } 1017 } 1018 } 1019 1020 // In many cases, CF_DIB provides a better image than CF_BITMAP 1021 if (::IsClipboardFormatAvailable(CF_DIB)) 1022 { 1023 HBITMAP hbm = BitmapFromClipboardDIB(::GetClipboardData(CF_DIB)); 1024 if (hbm) 1025 { 1026 InsertSelectionFromHBITMAP(hbm, m_hWnd); 1027 CloseClipboard(); 1028 break; 1029 } 1030 } 1031 1032 // The last resort 1033 if (::IsClipboardFormatAvailable(CF_BITMAP)) 1034 { 1035 HBITMAP hbm = (HBITMAP)::GetClipboardData(CF_BITMAP); 1036 if (hbm) 1037 { 1038 InsertSelectionFromHBITMAP(hbm, m_hWnd); 1039 CloseClipboard(); 1040 break; 1041 } 1042 } 1043 1044 // Failed to paste 1045 { 1046 CStringW strText, strTitle; 1047 strText.LoadString(IDS_CANTPASTE); 1048 strTitle.LoadString(IDS_PROGRAMNAME); 1049 MessageBox(strText, strTitle, MB_ICONINFORMATION); 1050 } 1051 1052 CloseClipboard(); 1053 break; 1054 case IDM_CROPSELECTION: 1055 { 1056 HBITMAP hbmSelection = selectionModel.GetSelectionContents(); 1057 if (hbmSelection) 1058 { 1059 imageModel.PushImageForUndo(hbmSelection); 1060 selectionModel.HideSelection(); 1061 imageModel.NotifyImageChanged(); 1062 } 1063 break; 1064 } 1065 case IDM_EDITDELETESELECTION: 1066 { 1067 if (textShown) 1068 { 1069 textEditWindow.SendMessage(WM_CLEAR); 1070 break; 1071 } 1072 switch (toolsModel.GetActiveTool()) 1073 { 1074 case TOOL_FREESEL: 1075 case TOOL_RECTSEL: 1076 selectionModel.DeleteSelection(); 1077 break; 1078 1079 case TOOL_TEXT: 1080 canvasWindow.OnEndDraw(TRUE); 1081 break; 1082 default: 1083 break; 1084 } 1085 break; 1086 } 1087 case IDM_EDITSELECTALL: 1088 { 1089 if (textShown) 1090 { 1091 textEditWindow.SendMessage(EM_SETSEL, 0, -1); 1092 break; 1093 } 1094 HWND hToolbar = FindWindowEx(toolBoxContainer.m_hWnd, NULL, TOOLBARCLASSNAME, NULL); 1095 ::SendMessageW(hToolbar, TB_CHECKBUTTON, ID_RECTSEL, MAKELPARAM(TRUE, 0)); 1096 toolsModel.selectAll(); 1097 canvasWindow.Invalidate(TRUE); 1098 break; 1099 } 1100 case IDM_EDITCOPYTO: 1101 { 1102 WCHAR szFileName[MAX_LONG_PATH]; 1103 ::LoadStringW(g_hinstExe, IDS_DEFAULTFILENAME, szFileName, _countof(szFileName)); 1104 if (GetSaveFileName(szFileName, _countof(szFileName))) 1105 { 1106 HBITMAP hbmSelection = selectionModel.GetSelectionContents(); 1107 if (!hbmSelection) 1108 { 1109 ShowOutOfMemory(); 1110 break; 1111 } 1112 SaveDIBToFile(hbmSelection, szFileName, FALSE); 1113 DeleteObject(hbmSelection); 1114 } 1115 break; 1116 } 1117 case IDM_EDITPASTEFROM: 1118 { 1119 WCHAR szFileName[MAX_LONG_PATH] = L""; 1120 if (GetOpenFileName(szFileName, _countof(szFileName))) 1121 { 1122 HBITMAP hbmNew = DoLoadImageFile(m_hWnd, szFileName, FALSE); 1123 if (hbmNew) 1124 InsertSelectionFromHBITMAP(hbmNew, m_hWnd); 1125 } 1126 break; 1127 } 1128 case IDM_COLORSEDITPALETTE: 1129 { 1130 COLORREF rgbColor = paletteModel.GetFgColor(); 1131 if (ChooseColor(&rgbColor)) 1132 paletteModel.SetFgColor(rgbColor); 1133 break; 1134 } 1135 case IDM_COLORSMODERNPALETTE: 1136 paletteModel.SelectPalette(PAL_MODERN); 1137 break; 1138 case IDM_COLORSOLDPALETTE: 1139 paletteModel.SelectPalette(PAL_OLDTYPE); 1140 break; 1141 case IDM_IMAGEINVERTCOLORS: 1142 { 1143 if (selectionModel.m_bShow) 1144 selectionModel.InvertSelection(); 1145 else 1146 imageModel.InvertColors(); 1147 break; 1148 } 1149 case IDM_IMAGEDELETEIMAGE: 1150 imageModel.PushImageForUndo(); 1151 Rect(imageModel.GetDC(), 0, 0, imageModel.GetWidth(), imageModel.GetHeight(), paletteModel.GetBgColor(), paletteModel.GetBgColor(), 0, TRUE); 1152 imageModel.NotifyImageChanged(); 1153 break; 1154 case IDM_IMAGEROTATEMIRROR: 1155 { 1156 CWaitCursor waitCursor; 1157 canvasWindow.updateScrollPos(); 1158 switch (mirrorRotateDialog.DoModal(mainWindow.m_hWnd)) 1159 { 1160 case 1: /* flip horizontally */ 1161 { 1162 if (selectionModel.m_bShow) 1163 selectionModel.FlipHorizontally(); 1164 else 1165 imageModel.FlipHorizontally(); 1166 break; 1167 } 1168 case 2: /* flip vertically */ 1169 { 1170 if (selectionModel.m_bShow) 1171 selectionModel.FlipVertically(); 1172 else 1173 imageModel.FlipVertically(); 1174 break; 1175 } 1176 case 3: /* rotate 90 degrees */ 1177 { 1178 if (selectionModel.m_bShow) 1179 selectionModel.RotateNTimes90Degrees(1); 1180 else 1181 imageModel.RotateNTimes90Degrees(1); 1182 break; 1183 } 1184 case 4: /* rotate 180 degrees */ 1185 { 1186 if (selectionModel.m_bShow) 1187 selectionModel.RotateNTimes90Degrees(2); 1188 else 1189 imageModel.RotateNTimes90Degrees(2); 1190 break; 1191 } 1192 case 5: /* rotate 270 degrees */ 1193 { 1194 if (selectionModel.m_bShow) 1195 selectionModel.RotateNTimes90Degrees(3); 1196 else 1197 imageModel.RotateNTimes90Degrees(3); 1198 break; 1199 } 1200 } 1201 } 1202 break; 1203 case IDM_IMAGEATTRIBUTES: 1204 { 1205 if (attributesDialog.DoModal(mainWindow.m_hWnd)) 1206 { 1207 CWaitCursor waitCursor; 1208 if (attributesDialog.m_bBlackAndWhite && !imageModel.IsBlackAndWhite()) 1209 { 1210 CStringW strText(MAKEINTRESOURCEW(IDS_LOSECOLOR)); 1211 CStringW strTitle(MAKEINTRESOURCEW(IDS_PROGRAMNAME)); 1212 INT id = MessageBox(strText, strTitle, MB_ICONINFORMATION | MB_YESNOCANCEL); 1213 if (id != IDYES) 1214 break; 1215 1216 imageModel.PushBlackAndWhite(); 1217 } 1218 1219 if (imageModel.GetWidth() != attributesDialog.newWidth || 1220 imageModel.GetHeight() != attributesDialog.newHeight) 1221 { 1222 imageModel.Crop(attributesDialog.newWidth, attributesDialog.newHeight); 1223 } 1224 } 1225 break; 1226 } 1227 case IDM_IMAGESTRETCHSKEW: 1228 { 1229 if (stretchSkewDialog.DoModal(mainWindow.m_hWnd)) 1230 { 1231 CWaitCursor waitCursor; 1232 if (selectionModel.m_bShow) 1233 { 1234 selectionModel.StretchSkew(stretchSkewDialog.percentage.x, stretchSkewDialog.percentage.y, 1235 stretchSkewDialog.angle.x, stretchSkewDialog.angle.y); 1236 } 1237 else 1238 { 1239 imageModel.StretchSkew(stretchSkewDialog.percentage.x, stretchSkewDialog.percentage.y, 1240 stretchSkewDialog.angle.x, stretchSkewDialog.angle.y); 1241 } 1242 } 1243 break; 1244 } 1245 case IDM_IMAGEDRAWOPAQUE: 1246 toolsModel.SetBackgroundTransparent(!toolsModel.IsBackgroundTransparent()); 1247 break; 1248 case IDM_IMAGECROP: 1249 { 1250 HBITMAP hbmCopy = selectionModel.GetSelectionContents(); 1251 imageModel.PushImageForUndo(hbmCopy); 1252 selectionModel.HideSelection(); 1253 break; 1254 } 1255 case IDM_VIEWTOOLBOX: 1256 registrySettings.ShowToolBox = !toolBoxContainer.IsWindowVisible(); 1257 toolBoxContainer.ShowWindow(registrySettings.ShowToolBox ? SW_SHOWNOACTIVATE : SW_HIDE); 1258 alignChildrenToMainWindow(); 1259 break; 1260 case IDM_VIEWCOLORPALETTE: 1261 registrySettings.ShowPalette = !paletteWindow.IsWindowVisible(); 1262 paletteWindow.ShowWindow(registrySettings.ShowPalette ? SW_SHOWNOACTIVATE : SW_HIDE); 1263 alignChildrenToMainWindow(); 1264 break; 1265 case IDM_VIEWSTATUSBAR: 1266 registrySettings.ShowStatusBar = !::IsWindowVisible(g_hStatusBar); 1267 ::ShowWindow(g_hStatusBar, (registrySettings.ShowStatusBar ? SW_SHOWNOACTIVATE : SW_HIDE)); 1268 alignChildrenToMainWindow(); 1269 break; 1270 case IDM_FORMATICONBAR: 1271 if (toolsModel.GetActiveTool() == TOOL_TEXT) 1272 { 1273 if (!fontsDialog.IsWindow()) 1274 { 1275 fontsDialog.Create(mainWindow); 1276 } 1277 registrySettings.ShowTextTool = !::IsWindowVisible(fontsDialog); 1278 fontsDialog.ShowWindow(registrySettings.ShowTextTool ? SW_SHOW : SW_HIDE); 1279 fontsDialog.SendMessage(DM_REPOSITION, 0, 0); 1280 } 1281 break; 1282 case IDM_VIEWSHOWGRID: 1283 g_showGrid = !g_showGrid; 1284 canvasWindow.Invalidate(FALSE); 1285 break; 1286 case IDM_VIEWSHOWMINIATURE: 1287 registrySettings.ShowThumbnail = !::IsWindowVisible(miniature); 1288 miniature.DoCreate(m_hWnd); 1289 miniature.ShowWindow(registrySettings.ShowThumbnail ? SW_SHOWNOACTIVATE : SW_HIDE); 1290 break; 1291 1292 case IDM_VIEWZOOM125: 1293 canvasWindow.zoomTo(125); 1294 break; 1295 case IDM_VIEWZOOM25: 1296 canvasWindow.zoomTo(250); 1297 break; 1298 case IDM_VIEWZOOM50: 1299 canvasWindow.zoomTo(500); 1300 break; 1301 case IDM_VIEWZOOM100: 1302 canvasWindow.zoomTo(1000); 1303 break; 1304 case IDM_VIEWZOOM200: 1305 canvasWindow.zoomTo(2000); 1306 break; 1307 case IDM_VIEWZOOM400: 1308 canvasWindow.zoomTo(4000); 1309 break; 1310 case IDM_VIEWZOOM800: 1311 canvasWindow.zoomTo(8000); 1312 break; 1313 1314 case IDM_VIEWFULLSCREEN: 1315 // Create and show the fullscreen window 1316 fullscreenWindow.DoCreate(); 1317 fullscreenWindow.ShowWindow(SW_SHOWMAXIMIZED); 1318 break; 1319 1320 case IDM_CTRL_PLUS: 1321 toolsModel.SpecialTweak(FALSE); 1322 break; 1323 case IDM_CTRL_MINUS: 1324 toolsModel.SpecialTweak(TRUE); 1325 break; 1326 } 1327 return 0; 1328 } 1329 1330 VOID CMainWindow::TrackPopupMenu(POINT ptScreen, INT iSubMenu) 1331 { 1332 HMENU hMenu = ::LoadMenuW(g_hinstExe, MAKEINTRESOURCEW(ID_POPUPMENU)); 1333 HMENU hSubMenu = ::GetSubMenu(hMenu, iSubMenu); 1334 1335 ::SetForegroundWindow(m_hWnd); 1336 INT_PTR id = ::TrackPopupMenu(hSubMenu, TPM_RIGHTBUTTON | TPM_RETURNCMD, 1337 ptScreen.x, ptScreen.y, 0, m_hWnd, NULL); 1338 PostMessage(WM_NULL); 1339 if (id != 0) 1340 PostMessage(WM_COMMAND, id); 1341 1342 ::DestroyMenu(hMenu); 1343 } 1344 1345 // entry point 1346 INT WINAPI 1347 wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, INT nCmdShow) 1348 { 1349 g_hinstExe = hInstance; 1350 1351 // Initialize common controls library 1352 INITCOMMONCONTROLSEX iccx; 1353 iccx.dwSize = sizeof(iccx); 1354 iccx.dwICC = ICC_STANDARD_CLASSES | ICC_USEREX_CLASSES | ICC_BAR_CLASSES; 1355 InitCommonControlsEx(&iccx); 1356 1357 // Load settings from registry 1358 registrySettings.Load(nCmdShow); 1359 1360 // Create the main window 1361 if (!mainWindow.DoCreate()) 1362 { 1363 MessageBox(NULL, L"Failed to create main window.", NULL, MB_ICONERROR); 1364 return 1; 1365 } 1366 1367 // Initialize imageModel 1368 if (__argc < 2 || !DoLoadImageFile(mainWindow, __targv[1], TRUE)) 1369 InitializeImage(NULL, NULL, FALSE); 1370 1371 // Make the window visible on the screen 1372 mainWindow.ShowWindow(registrySettings.WindowPlacement.showCmd); 1373 1374 // Load the access keys 1375 HACCEL hAccel = ::LoadAcceleratorsW(hInstance, MAKEINTRESOURCEW(800)); 1376 1377 // The message loop 1378 MSG msg; 1379 while (::GetMessage(&msg, NULL, 0, 0)) 1380 { 1381 if (fontsDialog.IsWindow() && fontsDialog.IsDialogMessage(&msg)) 1382 continue; 1383 1384 if (::TranslateAcceleratorW(mainWindow, hAccel, &msg)) 1385 continue; 1386 1387 ::TranslateMessage(&msg); 1388 ::DispatchMessage(&msg); 1389 } 1390 1391 // Unload the access keys 1392 ::DestroyAcceleratorTable(hAccel); 1393 1394 // Write back settings to registry 1395 registrySettings.Store(); 1396 1397 if (g_szMailTempFile[0]) 1398 ::DeleteFileW(g_szMailTempFile); 1399 1400 // Return the value that PostQuitMessage() gave 1401 return (INT)msg.wParam; 1402 } 1403