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