Reactos
at master 1394 lines 46 kB view raw
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 16BOOL g_askBeforeEnlarging = FALSE; // TODO: initialize from registry 17HINSTANCE g_hinstExe = NULL; 18WCHAR g_szFileName[MAX_LONG_PATH] = { 0 }; 19WCHAR g_szMailTempFile[MAX_LONG_PATH] = { 0 }; 20BOOL g_isAFile = FALSE; 21BOOL g_imageSaved = FALSE; 22BOOL g_showGrid = FALSE; 23HWND g_hStatusBar = NULL; 24 25CMainWindow mainWindow; 26 27typedef HWND (WINAPI *FN_HtmlHelpW)(HWND, LPCWSTR, UINT, DWORD_PTR); 28 29static HINSTANCE s_hHHCTRL_OCX = NULL; // HtmlHelpW needs "hhctrl.ocx" 30static FN_HtmlHelpW s_pHtmlHelpW = NULL; 31 32/* FUNCTIONS ********************************************************/ 33 34void 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 47static BOOL 48FileExtFromFilter(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 73static UINT_PTR APIENTRY 74OFNHookProc(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 96typedef ULONG (WINAPI *FN_MAPISendMail)(LHANDLE, ULONG_PTR, lpMapiMessage, FLAGS, ULONG); 97typedef ULONG (WINAPI *FN_MAPISendMailW)(LHANDLE, ULONG_PTR, lpMapiMessageW, FLAGS, ULONG); 98 99BOOL 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 206BOOL 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 238BOOL 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 282BOOL 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 309HWND 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 321static 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 341void 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 407void 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 431void 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 481LRESULT 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 531LRESULT 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 544LRESULT 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 582LRESULT 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 607BOOL 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 634LRESULT CMainWindow::OnClose(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 635{ 636 if (ConfirmSave()) 637 { 638 DestroyWindow(); 639 } 640 return 0; 641} 642 643void 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 676BOOL 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 685BOOL 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 692BOOL 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 702LRESULT 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 767LRESULT 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 779LRESULT 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 786LRESULT 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 811LRESULT 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 819LRESULT 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 { 961 ::SetClipboardData(CF_DIB, hGlobal); 962 } 963 else 964 { 965 ShowOutOfMemory(); 966 imageModel.ClearHistory(); 967 } 968 ::DeleteObject(hbmCopy); 969 } 970 971 CloseClipboard(); 972 break; 973 case IDM_EDITCUT: 974 if (textShown) 975 { 976 textEditWindow.SendMessage(WM_CUT); 977 break; 978 } 979 /* Copy */ 980 SendMessage(WM_COMMAND, IDM_EDITCOPY, 0); 981 /* Delete selection */ 982 SendMessage(WM_COMMAND, IDM_EDITDELETESELECTION, 0); 983 break; 984 case IDM_EDITPASTE: 985 if (textShown) 986 { 987 textEditWindow.SendMessage(WM_PASTE); 988 break; 989 } 990 991 if (!OpenClipboard()) 992 break; 993 994 // In many cases, CF_ENHMETAFILE provides a better image than CF_DIB 995 if (::IsClipboardFormatAvailable(CF_ENHMETAFILE)) 996 { 997 HENHMETAFILE hEMF = (HENHMETAFILE)::GetClipboardData(CF_ENHMETAFILE); 998 if (hEMF) 999 { 1000 HBITMAP hbm = BitmapFromHEMF(hEMF); 1001 ::DeleteEnhMetaFile(hEMF); 1002 if (hbm) 1003 { 1004 InsertSelectionFromHBITMAP(hbm, m_hWnd); 1005 CloseClipboard(); 1006 break; 1007 } 1008 } 1009 } 1010 1011 // In many cases, CF_DIB provides a better image than CF_BITMAP 1012 if (::IsClipboardFormatAvailable(CF_DIB)) 1013 { 1014 HBITMAP hbm = BitmapFromClipboardDIB(::GetClipboardData(CF_DIB)); 1015 if (hbm) 1016 { 1017 InsertSelectionFromHBITMAP(hbm, m_hWnd); 1018 CloseClipboard(); 1019 break; 1020 } 1021 } 1022 1023 // The last resort 1024 if (::IsClipboardFormatAvailable(CF_BITMAP)) 1025 { 1026 HBITMAP hbm = (HBITMAP)::GetClipboardData(CF_BITMAP); 1027 if (hbm) 1028 { 1029 InsertSelectionFromHBITMAP(hbm, m_hWnd); 1030 CloseClipboard(); 1031 break; 1032 } 1033 } 1034 1035 // Failed to paste 1036 { 1037 CStringW strText, strTitle; 1038 strText.LoadString(IDS_CANTPASTE); 1039 strTitle.LoadString(IDS_PROGRAMNAME); 1040 MessageBox(strText, strTitle, MB_ICONINFORMATION); 1041 } 1042 1043 CloseClipboard(); 1044 break; 1045 case IDM_CROPSELECTION: 1046 { 1047 HBITMAP hbmSelection = selectionModel.GetSelectionContents(); 1048 if (hbmSelection) 1049 { 1050 imageModel.PushImageForUndo(hbmSelection); 1051 selectionModel.HideSelection(); 1052 imageModel.NotifyImageChanged(); 1053 } 1054 break; 1055 } 1056 case IDM_EDITDELETESELECTION: 1057 { 1058 if (textShown) 1059 { 1060 textEditWindow.SendMessage(WM_CLEAR); 1061 break; 1062 } 1063 switch (toolsModel.GetActiveTool()) 1064 { 1065 case TOOL_FREESEL: 1066 case TOOL_RECTSEL: 1067 selectionModel.DeleteSelection(); 1068 break; 1069 1070 case TOOL_TEXT: 1071 canvasWindow.OnEndDraw(TRUE); 1072 break; 1073 default: 1074 break; 1075 } 1076 break; 1077 } 1078 case IDM_EDITSELECTALL: 1079 { 1080 if (textShown) 1081 { 1082 textEditWindow.SendMessage(EM_SETSEL, 0, -1); 1083 break; 1084 } 1085 HWND hToolbar = FindWindowEx(toolBoxContainer.m_hWnd, NULL, TOOLBARCLASSNAME, NULL); 1086 ::SendMessageW(hToolbar, TB_CHECKBUTTON, ID_RECTSEL, MAKELPARAM(TRUE, 0)); 1087 toolsModel.selectAll(); 1088 canvasWindow.Invalidate(TRUE); 1089 break; 1090 } 1091 case IDM_EDITCOPYTO: 1092 { 1093 WCHAR szFileName[MAX_LONG_PATH]; 1094 ::LoadStringW(g_hinstExe, IDS_DEFAULTFILENAME, szFileName, _countof(szFileName)); 1095 if (GetSaveFileName(szFileName, _countof(szFileName))) 1096 { 1097 HBITMAP hbmSelection = selectionModel.GetSelectionContents(); 1098 if (!hbmSelection) 1099 { 1100 ShowOutOfMemory(); 1101 imageModel.ClearHistory(); 1102 break; 1103 } 1104 SaveDIBToFile(hbmSelection, szFileName, FALSE); 1105 DeleteObject(hbmSelection); 1106 } 1107 break; 1108 } 1109 case IDM_EDITPASTEFROM: 1110 { 1111 WCHAR szFileName[MAX_LONG_PATH] = L""; 1112 if (GetOpenFileName(szFileName, _countof(szFileName))) 1113 { 1114 HBITMAP hbmNew = DoLoadImageFile(m_hWnd, szFileName, FALSE); 1115 if (hbmNew) 1116 InsertSelectionFromHBITMAP(hbmNew, m_hWnd); 1117 } 1118 break; 1119 } 1120 case IDM_COLORSEDITPALETTE: 1121 { 1122 COLORREF rgbColor = paletteModel.GetFgColor(); 1123 if (ChooseColor(&rgbColor)) 1124 paletteModel.SetFgColor(rgbColor); 1125 break; 1126 } 1127 case IDM_COLORSMODERNPALETTE: 1128 paletteModel.SelectPalette(PAL_MODERN); 1129 break; 1130 case IDM_COLORSOLDPALETTE: 1131 paletteModel.SelectPalette(PAL_OLDTYPE); 1132 break; 1133 case IDM_IMAGEINVERTCOLORS: 1134 { 1135 if (selectionModel.m_bShow) 1136 selectionModel.InvertSelection(); 1137 else 1138 imageModel.InvertColors(); 1139 break; 1140 } 1141 case IDM_IMAGEDELETEIMAGE: 1142 imageModel.PushImageForUndo(); 1143 Rect(imageModel.GetDC(), 0, 0, imageModel.GetWidth(), imageModel.GetHeight(), paletteModel.GetBgColor(), paletteModel.GetBgColor(), 0, TRUE); 1144 imageModel.NotifyImageChanged(); 1145 break; 1146 case IDM_IMAGEROTATEMIRROR: 1147 { 1148 CWaitCursor waitCursor; 1149 canvasWindow.updateScrollPos(); 1150 switch (mirrorRotateDialog.DoModal(mainWindow.m_hWnd)) 1151 { 1152 case 1: /* flip horizontally */ 1153 { 1154 if (selectionModel.m_bShow) 1155 selectionModel.FlipHorizontally(); 1156 else 1157 imageModel.FlipHorizontally(); 1158 break; 1159 } 1160 case 2: /* flip vertically */ 1161 { 1162 if (selectionModel.m_bShow) 1163 selectionModel.FlipVertically(); 1164 else 1165 imageModel.FlipVertically(); 1166 break; 1167 } 1168 case 3: /* rotate 90 degrees */ 1169 { 1170 if (selectionModel.m_bShow) 1171 selectionModel.RotateNTimes90Degrees(1); 1172 else 1173 imageModel.RotateNTimes90Degrees(1); 1174 break; 1175 } 1176 case 4: /* rotate 180 degrees */ 1177 { 1178 if (selectionModel.m_bShow) 1179 selectionModel.RotateNTimes90Degrees(2); 1180 else 1181 imageModel.RotateNTimes90Degrees(2); 1182 break; 1183 } 1184 case 5: /* rotate 270 degrees */ 1185 { 1186 if (selectionModel.m_bShow) 1187 selectionModel.RotateNTimes90Degrees(3); 1188 else 1189 imageModel.RotateNTimes90Degrees(3); 1190 break; 1191 } 1192 } 1193 } 1194 break; 1195 case IDM_IMAGEATTRIBUTES: 1196 { 1197 if (attributesDialog.DoModal(mainWindow.m_hWnd)) 1198 { 1199 CWaitCursor waitCursor; 1200 if (attributesDialog.m_bBlackAndWhite && !imageModel.IsBlackAndWhite()) 1201 { 1202 CStringW strText(MAKEINTRESOURCEW(IDS_LOSECOLOR)); 1203 CStringW strTitle(MAKEINTRESOURCEW(IDS_PROGRAMNAME)); 1204 INT id = MessageBox(strText, strTitle, MB_ICONINFORMATION | MB_YESNOCANCEL); 1205 if (id != IDYES) 1206 break; 1207 1208 imageModel.PushBlackAndWhite(); 1209 } 1210 1211 if (imageModel.GetWidth() != attributesDialog.newWidth || 1212 imageModel.GetHeight() != attributesDialog.newHeight) 1213 { 1214 imageModel.Crop(attributesDialog.newWidth, attributesDialog.newHeight); 1215 } 1216 } 1217 break; 1218 } 1219 case IDM_IMAGESTRETCHSKEW: 1220 { 1221 if (stretchSkewDialog.DoModal(mainWindow.m_hWnd)) 1222 { 1223 CWaitCursor waitCursor; 1224 if (selectionModel.m_bShow) 1225 { 1226 selectionModel.StretchSkew(stretchSkewDialog.percentage.x, stretchSkewDialog.percentage.y, 1227 stretchSkewDialog.angle.x, stretchSkewDialog.angle.y); 1228 } 1229 else 1230 { 1231 imageModel.StretchSkew(stretchSkewDialog.percentage.x, stretchSkewDialog.percentage.y, 1232 stretchSkewDialog.angle.x, stretchSkewDialog.angle.y); 1233 } 1234 } 1235 break; 1236 } 1237 case IDM_IMAGEDRAWOPAQUE: 1238 toolsModel.SetBackgroundTransparent(!toolsModel.IsBackgroundTransparent()); 1239 break; 1240 case IDM_IMAGECROP: 1241 { 1242 HBITMAP hbmCopy = selectionModel.GetSelectionContents(); 1243 imageModel.PushImageForUndo(hbmCopy); 1244 selectionModel.HideSelection(); 1245 break; 1246 } 1247 case IDM_VIEWTOOLBOX: 1248 registrySettings.ShowToolBox = !toolBoxContainer.IsWindowVisible(); 1249 toolBoxContainer.ShowWindow(registrySettings.ShowToolBox ? SW_SHOWNOACTIVATE : SW_HIDE); 1250 alignChildrenToMainWindow(); 1251 break; 1252 case IDM_VIEWCOLORPALETTE: 1253 registrySettings.ShowPalette = !paletteWindow.IsWindowVisible(); 1254 paletteWindow.ShowWindow(registrySettings.ShowPalette ? SW_SHOWNOACTIVATE : SW_HIDE); 1255 alignChildrenToMainWindow(); 1256 break; 1257 case IDM_VIEWSTATUSBAR: 1258 registrySettings.ShowStatusBar = !::IsWindowVisible(g_hStatusBar); 1259 ::ShowWindow(g_hStatusBar, (registrySettings.ShowStatusBar ? SW_SHOWNOACTIVATE : SW_HIDE)); 1260 alignChildrenToMainWindow(); 1261 break; 1262 case IDM_FORMATICONBAR: 1263 if (toolsModel.GetActiveTool() == TOOL_TEXT) 1264 { 1265 if (!fontsDialog.IsWindow()) 1266 { 1267 fontsDialog.Create(mainWindow); 1268 } 1269 registrySettings.ShowTextTool = !::IsWindowVisible(fontsDialog); 1270 fontsDialog.ShowWindow(registrySettings.ShowTextTool ? SW_SHOW : SW_HIDE); 1271 fontsDialog.SendMessage(DM_REPOSITION, 0, 0); 1272 } 1273 break; 1274 case IDM_VIEWSHOWGRID: 1275 g_showGrid = !g_showGrid; 1276 canvasWindow.Invalidate(FALSE); 1277 break; 1278 case IDM_VIEWSHOWMINIATURE: 1279 registrySettings.ShowThumbnail = !::IsWindowVisible(miniature); 1280 miniature.DoCreate(m_hWnd); 1281 miniature.ShowWindow(registrySettings.ShowThumbnail ? SW_SHOWNOACTIVATE : SW_HIDE); 1282 break; 1283 1284 case IDM_VIEWZOOM125: 1285 canvasWindow.zoomTo(125); 1286 break; 1287 case IDM_VIEWZOOM25: 1288 canvasWindow.zoomTo(250); 1289 break; 1290 case IDM_VIEWZOOM50: 1291 canvasWindow.zoomTo(500); 1292 break; 1293 case IDM_VIEWZOOM100: 1294 canvasWindow.zoomTo(1000); 1295 break; 1296 case IDM_VIEWZOOM200: 1297 canvasWindow.zoomTo(2000); 1298 break; 1299 case IDM_VIEWZOOM400: 1300 canvasWindow.zoomTo(4000); 1301 break; 1302 case IDM_VIEWZOOM800: 1303 canvasWindow.zoomTo(8000); 1304 break; 1305 1306 case IDM_VIEWFULLSCREEN: 1307 // Create and show the fullscreen window 1308 fullscreenWindow.DoCreate(); 1309 fullscreenWindow.ShowWindow(SW_SHOWMAXIMIZED); 1310 break; 1311 1312 case IDM_CTRL_PLUS: 1313 toolsModel.SpecialTweak(FALSE); 1314 break; 1315 case IDM_CTRL_MINUS: 1316 toolsModel.SpecialTweak(TRUE); 1317 break; 1318 } 1319 return 0; 1320} 1321 1322VOID CMainWindow::TrackPopupMenu(POINT ptScreen, INT iSubMenu) 1323{ 1324 HMENU hMenu = ::LoadMenuW(g_hinstExe, MAKEINTRESOURCEW(ID_POPUPMENU)); 1325 HMENU hSubMenu = ::GetSubMenu(hMenu, iSubMenu); 1326 1327 ::SetForegroundWindow(m_hWnd); 1328 INT_PTR id = ::TrackPopupMenu(hSubMenu, TPM_RIGHTBUTTON | TPM_RETURNCMD, 1329 ptScreen.x, ptScreen.y, 0, m_hWnd, NULL); 1330 PostMessage(WM_NULL); 1331 if (id != 0) 1332 PostMessage(WM_COMMAND, id); 1333 1334 ::DestroyMenu(hMenu); 1335} 1336 1337// entry point 1338INT WINAPI 1339wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, INT nCmdShow) 1340{ 1341 g_hinstExe = hInstance; 1342 1343 // Initialize common controls library 1344 INITCOMMONCONTROLSEX iccx; 1345 iccx.dwSize = sizeof(iccx); 1346 iccx.dwICC = ICC_STANDARD_CLASSES | ICC_USEREX_CLASSES | ICC_BAR_CLASSES; 1347 InitCommonControlsEx(&iccx); 1348 1349 // Load settings from registry 1350 registrySettings.Load(nCmdShow); 1351 1352 // Create the main window 1353 if (!mainWindow.DoCreate()) 1354 { 1355 MessageBox(NULL, L"Failed to create main window.", NULL, MB_ICONERROR); 1356 return 1; 1357 } 1358 1359 // Initialize imageModel 1360 if (__argc < 2 || !DoLoadImageFile(mainWindow, __targv[1], TRUE)) 1361 InitializeImage(NULL, NULL, FALSE); 1362 1363 // Make the window visible on the screen 1364 mainWindow.ShowWindow(registrySettings.WindowPlacement.showCmd); 1365 1366 // Load the access keys 1367 HACCEL hAccel = ::LoadAcceleratorsW(hInstance, MAKEINTRESOURCEW(800)); 1368 1369 // The message loop 1370 MSG msg; 1371 while (::GetMessage(&msg, NULL, 0, 0)) 1372 { 1373 if (fontsDialog.IsWindow() && fontsDialog.IsDialogMessage(&msg)) 1374 continue; 1375 1376 if (::TranslateAcceleratorW(mainWindow, hAccel, &msg)) 1377 continue; 1378 1379 ::TranslateMessage(&msg); 1380 ::DispatchMessage(&msg); 1381 } 1382 1383 // Unload the access keys 1384 ::DestroyAcceleratorTable(hAccel); 1385 1386 // Write back settings to registry 1387 registrySettings.Store(); 1388 1389 if (g_szMailTempFile[0]) 1390 ::DeleteFileW(g_szMailTempFile); 1391 1392 // Return the value that PostQuitMessage() gave 1393 return (INT)msg.wParam; 1394}