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
ShowOutOfMemory(void)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
FileExtFromFilter(LPWSTR pExt,OPENFILENAME * pOFN)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
OFNHookProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)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
OpenMailer(HWND hWnd,LPCWSTR pszPathName)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
GetOpenFileName(IN OUT LPWSTR pszFile,INT cchMaxFile)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
GetSaveFileName(IN OUT LPWSTR pszFile,INT cchMaxFile)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
ChooseColor(IN OUT COLORREF * prgbColor)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
DoCreate()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
DoHtmlHelpW(HWND hwndCaller,LPCWSTR pszFile,UINT uCommand,DWORD_PTR dwData)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
alignChildrenToMainWindow()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
saveImage(BOOL overwrite)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
InsertSelectionFromHBITMAP(HBITMAP bitmap,HWND window)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
OnMouseWheel(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
OnDropFiles(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
OnCreate(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
OnDestroy(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
ConfirmSave()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
OnClose(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
ProcessFileMenu(HMENU hPopupMenu)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
CanUndo() const676 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
CanRedo() const685 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
CanPaste() const692 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
OnInitMenuPopup(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
OnSize(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
OnGetMinMaxInfo(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
OnKeyDown(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
OnSysColorChange(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
OnCommand(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)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
TrackPopupMenu(POINT ptScreen,INT iSubMenu)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
wWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPWSTR lpCmdLine,INT nCmdShow)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