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 747 // 748 // View menu 749 // 750 CheckMenuItem(menu, IDM_VIEWTOOLBOX, CHECKED_IF(::IsWindowVisible(toolBoxContainer))); 751 CheckMenuItem(menu, IDM_VIEWCOLORPALETTE, CHECKED_IF(::IsWindowVisible(paletteWindow))); 752 CheckMenuItem(menu, IDM_VIEWSTATUSBAR, CHECKED_IF(::IsWindowVisible(g_hStatusBar))); 753 CheckMenuItem(menu, IDM_FORMATICONBAR, CHECKED_IF(::IsWindowVisible(fontsDialog))); 754 EnableMenuItem(menu, IDM_FORMATICONBAR, ENABLED_IF(toolsModel.GetActiveTool() == TOOL_TEXT)); 755 CheckMenuItem(menu, IDM_VIEWZOOM125, CHECKED_IF(toolsModel.GetZoom() == 125)); 756 CheckMenuItem(menu, IDM_VIEWZOOM25, CHECKED_IF(toolsModel.GetZoom() == 250)); 757 CheckMenuItem(menu, IDM_VIEWZOOM50, CHECKED_IF(toolsModel.GetZoom() == 500)); 758 CheckMenuItem(menu, IDM_VIEWZOOM100, CHECKED_IF(toolsModel.GetZoom() == 1000)); 759 CheckMenuItem(menu, IDM_VIEWZOOM200, CHECKED_IF(toolsModel.GetZoom() == 2000)); 760 CheckMenuItem(menu, IDM_VIEWZOOM400, CHECKED_IF(toolsModel.GetZoom() == 4000)); 761 CheckMenuItem(menu, IDM_VIEWZOOM800, CHECKED_IF(toolsModel.GetZoom() == 8000)); 762 CheckMenuItem(menu, IDM_VIEWSHOWGRID, CHECKED_IF(g_showGrid)); 763 CheckMenuItem(menu, IDM_VIEWSHOWMINIATURE, CHECKED_IF(registrySettings.ShowThumbnail)); 764 765 // 766 // Image menu 767 // 768 EnableMenuItem(menu, IDM_IMAGECROP, ENABLED_IF(selectionModel.m_bShow)); 769 EnableMenuItem(menu, IDM_IMAGEDELETEIMAGE, ENABLED_IF(!selectionModel.m_bShow)); 770 CheckMenuItem(menu, IDM_IMAGEDRAWOPAQUE, CHECKED_IF(!toolsModel.IsBackgroundTransparent())); 771 772 // 773 // Palette menu 774 // 775 CheckMenuItem(menu, IDM_COLORSMODERNPALETTE, CHECKED_IF(paletteModel.SelectedPalette() == PAL_MODERN)); 776 CheckMenuItem(menu, IDM_COLORSOLDPALETTE, CHECKED_IF(paletteModel.SelectedPalette() == PAL_OLDTYPE)); 777 return 0; 778 } 779 780 LRESULT CMainWindow::OnSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 781 { 782 int test[] = { LOWORD(lParam) - 260, LOWORD(lParam) - 140, LOWORD(lParam) - 20 }; 783 if (::IsWindow(g_hStatusBar)) 784 { 785 ::SendMessageW(g_hStatusBar, WM_SIZE, 0, 0); 786 ::SendMessageW(g_hStatusBar, SB_SETPARTS, 3, (LPARAM)&test); 787 } 788 alignChildrenToMainWindow(); 789 return 0; 790 } 791 792 LRESULT CMainWindow::OnGetMinMaxInfo(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 793 { 794 MINMAXINFO *mm = (MINMAXINFO*)lParam; 795 mm->ptMinTrackSize = { 330, 360 }; 796 return 0; 797 } 798 799 LRESULT CMainWindow::OnKeyDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 800 { 801 switch (wParam) 802 { 803 case VK_ESCAPE: 804 canvasWindow.PostMessage(nMsg, wParam, lParam); 805 break; 806 case VK_LEFT: 807 selectionModel.moveSelection(-1, 0); 808 break; 809 case VK_RIGHT: 810 selectionModel.moveSelection(+1, 0); 811 break; 812 case VK_UP: 813 selectionModel.moveSelection(0, -1); 814 break; 815 case VK_DOWN: 816 selectionModel.moveSelection(0, +1); 817 break; 818 default: 819 break; 820 } 821 return 0; 822 } 823 824 LRESULT CMainWindow::OnSysColorChange(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 825 { 826 /* Redirect message to common controls */ 827 HWND hToolbar = FindWindowEx(toolBoxContainer.m_hWnd, NULL, TOOLBARCLASSNAME, NULL); 828 ::SendMessageW(hToolbar, WM_SYSCOLORCHANGE, 0, 0); 829 return 0; 830 } 831 832 LRESULT CMainWindow::OnCommand(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 833 { 834 // Disable commands while dragging mouse 835 if (canvasWindow.m_drawing && ::GetCapture()) 836 { 837 ATLTRACE("locking!\n"); 838 return 0; 839 } 840 841 BOOL textShown = (toolsModel.GetActiveTool() == TOOL_TEXT && ::IsWindowVisible(textEditWindow)); 842 switch (LOWORD(wParam)) 843 { 844 case IDM_HELPINFO: 845 { 846 WCHAR infotitle[100], infotext[200]; 847 ::LoadStringW(g_hinstExe, IDS_INFOTITLE, infotitle, _countof(infotitle)); 848 ::LoadStringW(g_hinstExe, IDS_INFOTEXT, infotext, _countof(infotext)); 849 ::ShellAboutW(m_hWnd, infotitle, infotext, 850 LoadIconW(g_hinstExe, MAKEINTRESOURCEW(IDI_APPICON))); 851 break; 852 } 853 case IDM_HELPHELPTOPICS: 854 DoHtmlHelpW(m_hWnd, L"%SystemRoot%\\Help\\mspaint.chm", HH_DISPLAY_TOPIC, 0); 855 break; 856 case IDM_FILEEXIT: 857 SendMessage(WM_CLOSE, wParam, lParam); 858 break; 859 case IDM_FILENEW: 860 if (ConfirmSave()) 861 { 862 InitializeImage(NULL, NULL, FALSE); 863 } 864 break; 865 case IDM_FILEOPEN: 866 { 867 WCHAR szFileName[MAX_LONG_PATH] = L""; 868 if (ConfirmSave() && GetOpenFileName(szFileName, _countof(szFileName))) 869 { 870 DoLoadImageFile(m_hWnd, szFileName, TRUE); 871 } 872 break; 873 } 874 case IDM_FILESAVE: 875 saveImage(TRUE); 876 break; 877 case IDM_FILESAVEAS: 878 saveImage(FALSE); 879 break; 880 case IDM_FILEPAGESETUP: 881 // DUMMY: Shows the dialog only, no functionality 882 PAGESETUPDLG psd; 883 ZeroMemory(&psd, sizeof(psd)); 884 psd.lStructSize = sizeof(psd); 885 psd.hwndOwner = m_hWnd; 886 PageSetupDlg(&psd); 887 break; 888 case IDM_FILEPRINT: 889 // TODO: Test whether it actually works 890 PRINTDLG pd; 891 ZeroMemory(&pd, sizeof(pd)); 892 pd.lStructSize = sizeof(pd); 893 pd.hwndOwner = m_hWnd; 894 pd.hDevMode = NULL; // freed by user 895 pd.hDevNames = NULL; // freed by user 896 pd.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC; 897 pd.nCopies = 1; 898 pd.nFromPage = 0xffff; 899 pd.nToPage = 0xffff; 900 pd.nMinPage = 1; 901 pd.nMaxPage = 0xffff; 902 if (PrintDlg(&pd) == TRUE) 903 { 904 ::BitBlt(pd.hDC, 0, 0, imageModel.GetWidth(), imageModel.GetHeight(), imageModel.GetDC(), 0, 0, SRCCOPY); 905 DeleteDC(pd.hDC); 906 } 907 if (pd.hDevMode) 908 GlobalFree(pd.hDevMode); 909 if (pd.hDevNames) 910 GlobalFree(pd.hDevNames); 911 break; 912 case IDM_FILESEND: 913 canvasWindow.OnEndDraw(FALSE); 914 if (!OpenMailer(m_hWnd, g_szFileName)) 915 { 916 ShowError(IDS_CANTSENDMAIL); 917 } 918 break; 919 case IDM_FILEASWALLPAPERPLANE: 920 RegistrySettings::SetWallpaper(g_szFileName, RegistrySettings::TILED); 921 break; 922 case IDM_FILEASWALLPAPERCENTERED: 923 RegistrySettings::SetWallpaper(g_szFileName, RegistrySettings::CENTERED); 924 break; 925 case IDM_FILEASWALLPAPERSTRETCHED: 926 RegistrySettings::SetWallpaper(g_szFileName, RegistrySettings::STRETCHED); 927 break; 928 case IDM_FILE1: 929 case IDM_FILE2: 930 case IDM_FILE3: 931 case IDM_FILE4: 932 { 933 INT iFile = LOWORD(wParam) - IDM_FILE1; 934 if (ConfirmSave()) 935 DoLoadImageFile(m_hWnd, registrySettings.strFiles[iFile], TRUE); 936 break; 937 } 938 case IDM_EDITUNDO: 939 if (textShown) 940 { 941 textEditWindow.PostMessage(WM_UNDO, 0, 0); 942 break; 943 } 944 canvasWindow.OnEndDraw(FALSE); 945 imageModel.Undo(); 946 break; 947 case IDM_EDITREDO: 948 if (textShown) 949 { 950 // There is no "WM_REDO" in EDIT control 951 break; 952 } 953 canvasWindow.OnEndDraw(FALSE); 954 imageModel.Redo(); 955 break; 956 case IDM_EDITCOPY: 957 if (textShown) 958 { 959 textEditWindow.SendMessage(WM_COPY); 960 break; 961 } 962 if (!selectionModel.m_bShow || !OpenClipboard()) 963 break; 964 965 EmptyClipboard(); 966 967 selectionModel.TakeOff(); 968 969 { 970 HBITMAP hbmCopy = selectionModel.GetSelectionContents(); 971 HGLOBAL hGlobal = BitmapToClipboardDIB(hbmCopy); 972 if (hGlobal) 973 ::SetClipboardData(CF_DIB, hGlobal); 974 else 975 ShowOutOfMemory(); 976 ::DeleteObject(hbmCopy); 977 } 978 979 CloseClipboard(); 980 break; 981 case IDM_EDITCUT: 982 if (textShown) 983 { 984 textEditWindow.SendMessage(WM_CUT); 985 break; 986 } 987 /* Copy */ 988 SendMessage(WM_COMMAND, IDM_EDITCOPY, 0); 989 /* Delete selection */ 990 SendMessage(WM_COMMAND, IDM_EDITDELETESELECTION, 0); 991 break; 992 case IDM_EDITPASTE: 993 if (textShown) 994 { 995 textEditWindow.SendMessage(WM_PASTE); 996 break; 997 } 998 999 if (!OpenClipboard()) 1000 break; 1001 1002 // In many cases, CF_ENHMETAFILE provides a better image than CF_DIB 1003 if (::IsClipboardFormatAvailable(CF_ENHMETAFILE)) 1004 { 1005 HENHMETAFILE hEMF = (HENHMETAFILE)::GetClipboardData(CF_ENHMETAFILE); 1006 if (hEMF) 1007 { 1008 HBITMAP hbm = BitmapFromHEMF(hEMF); 1009 ::DeleteEnhMetaFile(hEMF); 1010 if (hbm) 1011 { 1012 InsertSelectionFromHBITMAP(hbm, m_hWnd); 1013 CloseClipboard(); 1014 break; 1015 } 1016 } 1017 } 1018 1019 // In many cases, CF_DIB provides a better image than CF_BITMAP 1020 if (::IsClipboardFormatAvailable(CF_DIB)) 1021 { 1022 HBITMAP hbm = BitmapFromClipboardDIB(::GetClipboardData(CF_DIB)); 1023 if (hbm) 1024 { 1025 InsertSelectionFromHBITMAP(hbm, m_hWnd); 1026 CloseClipboard(); 1027 break; 1028 } 1029 } 1030 1031 // The last resort 1032 if (::IsClipboardFormatAvailable(CF_BITMAP)) 1033 { 1034 HBITMAP hbm = (HBITMAP)::GetClipboardData(CF_BITMAP); 1035 if (hbm) 1036 { 1037 InsertSelectionFromHBITMAP(hbm, m_hWnd); 1038 CloseClipboard(); 1039 break; 1040 } 1041 } 1042 1043 // Failed to paste 1044 { 1045 CStringW strText, strTitle; 1046 strText.LoadString(IDS_CANTPASTE); 1047 strTitle.LoadString(IDS_PROGRAMNAME); 1048 MessageBox(strText, strTitle, MB_ICONINFORMATION); 1049 } 1050 1051 CloseClipboard(); 1052 break; 1053 case IDM_EDITDELETESELECTION: 1054 { 1055 if (textShown) 1056 { 1057 textEditWindow.SendMessage(WM_CLEAR); 1058 break; 1059 } 1060 switch (toolsModel.GetActiveTool()) 1061 { 1062 case TOOL_FREESEL: 1063 case TOOL_RECTSEL: 1064 selectionModel.DeleteSelection(); 1065 break; 1066 1067 case TOOL_TEXT: 1068 canvasWindow.OnEndDraw(TRUE); 1069 break; 1070 default: 1071 break; 1072 } 1073 break; 1074 } 1075 case IDM_EDITSELECTALL: 1076 { 1077 if (textShown) 1078 { 1079 textEditWindow.SendMessage(EM_SETSEL, 0, -1); 1080 break; 1081 } 1082 HWND hToolbar = FindWindowEx(toolBoxContainer.m_hWnd, NULL, TOOLBARCLASSNAME, NULL); 1083 ::SendMessageW(hToolbar, TB_CHECKBUTTON, ID_RECTSEL, MAKELPARAM(TRUE, 0)); 1084 toolsModel.selectAll(); 1085 canvasWindow.Invalidate(TRUE); 1086 break; 1087 } 1088 case IDM_EDITCOPYTO: 1089 { 1090 WCHAR szFileName[MAX_LONG_PATH]; 1091 ::LoadStringW(g_hinstExe, IDS_DEFAULTFILENAME, szFileName, _countof(szFileName)); 1092 if (GetSaveFileName(szFileName, _countof(szFileName))) 1093 { 1094 HBITMAP hbmSelection = selectionModel.GetSelectionContents(); 1095 if (!hbmSelection) 1096 { 1097 ShowOutOfMemory(); 1098 break; 1099 } 1100 SaveDIBToFile(hbmSelection, szFileName, FALSE); 1101 DeleteObject(hbmSelection); 1102 } 1103 break; 1104 } 1105 case IDM_EDITPASTEFROM: 1106 { 1107 WCHAR szFileName[MAX_LONG_PATH] = L""; 1108 if (GetOpenFileName(szFileName, _countof(szFileName))) 1109 { 1110 HBITMAP hbmNew = DoLoadImageFile(m_hWnd, szFileName, FALSE); 1111 if (hbmNew) 1112 InsertSelectionFromHBITMAP(hbmNew, m_hWnd); 1113 } 1114 break; 1115 } 1116 case IDM_COLORSEDITPALETTE: 1117 { 1118 COLORREF rgbColor = paletteModel.GetFgColor(); 1119 if (ChooseColor(&rgbColor)) 1120 paletteModel.SetFgColor(rgbColor); 1121 break; 1122 } 1123 case IDM_COLORSMODERNPALETTE: 1124 paletteModel.SelectPalette(PAL_MODERN); 1125 break; 1126 case IDM_COLORSOLDPALETTE: 1127 paletteModel.SelectPalette(PAL_OLDTYPE); 1128 break; 1129 case IDM_IMAGEINVERTCOLORS: 1130 { 1131 if (selectionModel.m_bShow) 1132 selectionModel.InvertSelection(); 1133 else 1134 imageModel.InvertColors(); 1135 break; 1136 } 1137 case IDM_IMAGEDELETEIMAGE: 1138 imageModel.PushImageForUndo(); 1139 Rect(imageModel.GetDC(), 0, 0, imageModel.GetWidth(), imageModel.GetHeight(), paletteModel.GetBgColor(), paletteModel.GetBgColor(), 0, TRUE); 1140 imageModel.NotifyImageChanged(); 1141 break; 1142 case IDM_IMAGEROTATEMIRROR: 1143 { 1144 CWaitCursor waitCursor; 1145 canvasWindow.updateScrollPos(); 1146 switch (mirrorRotateDialog.DoModal(mainWindow.m_hWnd)) 1147 { 1148 case 1: /* flip horizontally */ 1149 { 1150 if (selectionModel.m_bShow) 1151 selectionModel.FlipHorizontally(); 1152 else 1153 imageModel.FlipHorizontally(); 1154 break; 1155 } 1156 case 2: /* flip vertically */ 1157 { 1158 if (selectionModel.m_bShow) 1159 selectionModel.FlipVertically(); 1160 else 1161 imageModel.FlipVertically(); 1162 break; 1163 } 1164 case 3: /* rotate 90 degrees */ 1165 { 1166 if (selectionModel.m_bShow) 1167 selectionModel.RotateNTimes90Degrees(1); 1168 else 1169 imageModel.RotateNTimes90Degrees(1); 1170 break; 1171 } 1172 case 4: /* rotate 180 degrees */ 1173 { 1174 if (selectionModel.m_bShow) 1175 selectionModel.RotateNTimes90Degrees(2); 1176 else 1177 imageModel.RotateNTimes90Degrees(2); 1178 break; 1179 } 1180 case 5: /* rotate 270 degrees */ 1181 { 1182 if (selectionModel.m_bShow) 1183 selectionModel.RotateNTimes90Degrees(3); 1184 else 1185 imageModel.RotateNTimes90Degrees(3); 1186 break; 1187 } 1188 } 1189 } 1190 break; 1191 case IDM_IMAGEATTRIBUTES: 1192 { 1193 if (attributesDialog.DoModal(mainWindow.m_hWnd)) 1194 { 1195 CWaitCursor waitCursor; 1196 if (attributesDialog.m_bBlackAndWhite && !imageModel.IsBlackAndWhite()) 1197 { 1198 CStringW strText(MAKEINTRESOURCEW(IDS_LOSECOLOR)); 1199 CStringW strTitle(MAKEINTRESOURCEW(IDS_PROGRAMNAME)); 1200 INT id = MessageBox(strText, strTitle, MB_ICONINFORMATION | MB_YESNOCANCEL); 1201 if (id != IDYES) 1202 break; 1203 1204 imageModel.PushBlackAndWhite(); 1205 } 1206 1207 if (imageModel.GetWidth() != attributesDialog.newWidth || 1208 imageModel.GetHeight() != attributesDialog.newHeight) 1209 { 1210 imageModel.Crop(attributesDialog.newWidth, attributesDialog.newHeight); 1211 } 1212 } 1213 break; 1214 } 1215 case IDM_IMAGESTRETCHSKEW: 1216 { 1217 if (stretchSkewDialog.DoModal(mainWindow.m_hWnd)) 1218 { 1219 CWaitCursor waitCursor; 1220 if (selectionModel.m_bShow) 1221 { 1222 selectionModel.StretchSkew(stretchSkewDialog.percentage.x, stretchSkewDialog.percentage.y, 1223 stretchSkewDialog.angle.x, stretchSkewDialog.angle.y); 1224 } 1225 else 1226 { 1227 imageModel.StretchSkew(stretchSkewDialog.percentage.x, stretchSkewDialog.percentage.y, 1228 stretchSkewDialog.angle.x, stretchSkewDialog.angle.y); 1229 } 1230 } 1231 break; 1232 } 1233 case IDM_IMAGEDRAWOPAQUE: 1234 toolsModel.SetBackgroundTransparent(!toolsModel.IsBackgroundTransparent()); 1235 break; 1236 case IDM_IMAGECROP: 1237 { 1238 HBITMAP hbmCopy = selectionModel.GetSelectionContents(); 1239 imageModel.PushImageForUndo(hbmCopy); 1240 selectionModel.HideSelection(); 1241 break; 1242 } 1243 case IDM_VIEWTOOLBOX: 1244 registrySettings.ShowToolBox = !toolBoxContainer.IsWindowVisible(); 1245 toolBoxContainer.ShowWindow(registrySettings.ShowToolBox ? SW_SHOWNOACTIVATE : SW_HIDE); 1246 alignChildrenToMainWindow(); 1247 break; 1248 case IDM_VIEWCOLORPALETTE: 1249 registrySettings.ShowPalette = !paletteWindow.IsWindowVisible(); 1250 paletteWindow.ShowWindow(registrySettings.ShowPalette ? SW_SHOWNOACTIVATE : SW_HIDE); 1251 alignChildrenToMainWindow(); 1252 break; 1253 case IDM_VIEWSTATUSBAR: 1254 registrySettings.ShowStatusBar = !::IsWindowVisible(g_hStatusBar); 1255 ::ShowWindow(g_hStatusBar, (registrySettings.ShowStatusBar ? SW_SHOWNOACTIVATE : SW_HIDE)); 1256 alignChildrenToMainWindow(); 1257 break; 1258 case IDM_FORMATICONBAR: 1259 if (toolsModel.GetActiveTool() == TOOL_TEXT) 1260 { 1261 if (!fontsDialog.IsWindow()) 1262 { 1263 fontsDialog.Create(mainWindow); 1264 } 1265 registrySettings.ShowTextTool = !::IsWindowVisible(fontsDialog); 1266 fontsDialog.ShowWindow(registrySettings.ShowTextTool ? SW_SHOW : SW_HIDE); 1267 fontsDialog.SendMessage(DM_REPOSITION, 0, 0); 1268 } 1269 break; 1270 case IDM_VIEWSHOWGRID: 1271 g_showGrid = !g_showGrid; 1272 canvasWindow.Invalidate(FALSE); 1273 break; 1274 case IDM_VIEWSHOWMINIATURE: 1275 registrySettings.ShowThumbnail = !::IsWindowVisible(miniature); 1276 miniature.DoCreate(m_hWnd); 1277 miniature.ShowWindow(registrySettings.ShowThumbnail ? SW_SHOWNOACTIVATE : SW_HIDE); 1278 break; 1279 1280 case IDM_VIEWZOOM125: 1281 canvasWindow.zoomTo(125); 1282 break; 1283 case IDM_VIEWZOOM25: 1284 canvasWindow.zoomTo(250); 1285 break; 1286 case IDM_VIEWZOOM50: 1287 canvasWindow.zoomTo(500); 1288 break; 1289 case IDM_VIEWZOOM100: 1290 canvasWindow.zoomTo(1000); 1291 break; 1292 case IDM_VIEWZOOM200: 1293 canvasWindow.zoomTo(2000); 1294 break; 1295 case IDM_VIEWZOOM400: 1296 canvasWindow.zoomTo(4000); 1297 break; 1298 case IDM_VIEWZOOM800: 1299 canvasWindow.zoomTo(8000); 1300 break; 1301 1302 case IDM_VIEWFULLSCREEN: 1303 // Create and show the fullscreen window 1304 fullscreenWindow.DoCreate(); 1305 fullscreenWindow.ShowWindow(SW_SHOWMAXIMIZED); 1306 break; 1307 1308 case IDM_CTRL_PLUS: 1309 toolsModel.SpecialTweak(FALSE); 1310 break; 1311 case IDM_CTRL_MINUS: 1312 toolsModel.SpecialTweak(TRUE); 1313 break; 1314 } 1315 return 0; 1316 } 1317 1318 VOID CMainWindow::TrackPopupMenu(POINT ptScreen, INT iSubMenu) 1319 { 1320 HMENU hMenu = ::LoadMenuW(g_hinstExe, MAKEINTRESOURCEW(ID_POPUPMENU)); 1321 HMENU hSubMenu = ::GetSubMenu(hMenu, iSubMenu); 1322 1323 ::SetForegroundWindow(m_hWnd); 1324 INT_PTR id = ::TrackPopupMenu(hSubMenu, TPM_RIGHTBUTTON | TPM_RETURNCMD, 1325 ptScreen.x, ptScreen.y, 0, m_hWnd, NULL); 1326 PostMessage(WM_NULL); 1327 if (id != 0) 1328 PostMessage(WM_COMMAND, id); 1329 1330 ::DestroyMenu(hMenu); 1331 } 1332 1333 // entry point 1334 INT WINAPI 1335 wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, INT nCmdShow) 1336 { 1337 g_hinstExe = hInstance; 1338 1339 // Initialize common controls library 1340 INITCOMMONCONTROLSEX iccx; 1341 iccx.dwSize = sizeof(iccx); 1342 iccx.dwICC = ICC_STANDARD_CLASSES | ICC_USEREX_CLASSES | ICC_BAR_CLASSES; 1343 InitCommonControlsEx(&iccx); 1344 1345 // Load settings from registry 1346 registrySettings.Load(nCmdShow); 1347 1348 // Create the main window 1349 if (!mainWindow.DoCreate()) 1350 { 1351 MessageBox(NULL, L"Failed to create main window.", NULL, MB_ICONERROR); 1352 return 1; 1353 } 1354 1355 // Initialize imageModel 1356 if (__argc < 2 || !DoLoadImageFile(mainWindow, __targv[1], TRUE)) 1357 InitializeImage(NULL, NULL, FALSE); 1358 1359 // Make the window visible on the screen 1360 mainWindow.ShowWindow(registrySettings.WindowPlacement.showCmd); 1361 1362 // Load the access keys 1363 HACCEL hAccel = ::LoadAcceleratorsW(hInstance, MAKEINTRESOURCEW(800)); 1364 1365 // The message loop 1366 MSG msg; 1367 while (::GetMessage(&msg, NULL, 0, 0)) 1368 { 1369 if (fontsDialog.IsWindow() && fontsDialog.IsDialogMessage(&msg)) 1370 continue; 1371 1372 if (::TranslateAcceleratorW(mainWindow, hAccel, &msg)) 1373 continue; 1374 1375 ::TranslateMessage(&msg); 1376 ::DispatchMessage(&msg); 1377 } 1378 1379 // Unload the access keys 1380 ::DestroyAcceleratorTable(hAccel); 1381 1382 // Write back settings to registry 1383 registrySettings.Store(); 1384 1385 if (g_szMailTempFile[0]) 1386 ::DeleteFileW(g_szMailTempFile); 1387 1388 // Return the value that PostQuitMessage() gave 1389 return (INT)msg.wParam; 1390 } 1391