Reactos
1/*
2 * PROJECT: PAINT for ReactOS
3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
4 * PURPOSE: Providing the canvas window class
5 * COPYRIGHT: Copyright 2015 Benedikt Freisen <b.freisen@gmx.net>
6 */
7
8#include "precomp.h"
9
10CCanvasWindow canvasWindow;
11
12/* FUNCTIONS ********************************************************/
13
14CCanvasWindow::CCanvasWindow()
15 : m_drawing(FALSE)
16 , m_hitCanvasSizeBox(HIT_NONE)
17 , m_ptOrig { -1, -1 }
18{
19 m_rcResizing.SetRectEmpty();
20}
21
22CCanvasWindow::~CCanvasWindow()
23{
24}
25
26RECT CCanvasWindow::GetBaseRect()
27{
28 CRect rcBase;
29 GetImageRect(rcBase);
30 ImageToCanvas(rcBase);
31 rcBase.InflateRect(GRIP_SIZE, GRIP_SIZE);
32 return rcBase;
33}
34
35VOID CCanvasWindow::ImageToCanvas(POINT& pt)
36{
37 Zoomed(pt);
38 pt.x += GRIP_SIZE - GetScrollPos(SB_HORZ);
39 pt.y += GRIP_SIZE - GetScrollPos(SB_VERT);
40}
41
42VOID CCanvasWindow::ImageToCanvas(RECT& rc)
43{
44 Zoomed(rc);
45 ::OffsetRect(&rc, GRIP_SIZE - GetScrollPos(SB_HORZ), GRIP_SIZE - GetScrollPos(SB_VERT));
46}
47
48VOID CCanvasWindow::CanvasToImage(POINT& pt)
49{
50 pt.x -= GRIP_SIZE - GetScrollPos(SB_HORZ);
51 pt.y -= GRIP_SIZE - GetScrollPos(SB_VERT);
52 UnZoomed(pt);
53}
54
55VOID CCanvasWindow::CanvasToImage(RECT& rc)
56{
57 ::OffsetRect(&rc, GetScrollPos(SB_HORZ) - GRIP_SIZE, GetScrollPos(SB_VERT) - GRIP_SIZE);
58 UnZoomed(rc);
59}
60
61VOID CCanvasWindow::GetImageRect(RECT& rc)
62{
63 rc = { 0, 0, imageModel.GetWidth(), imageModel.GetHeight() };
64}
65
66HITTEST CCanvasWindow::CanvasHitTest(POINT pt)
67{
68 if (selectionModel.m_bShow || ::IsWindowVisible(textEditWindow))
69 return HIT_INNER;
70 RECT rcBase = GetBaseRect();
71 return getSizeBoxHitTest(pt, &rcBase);
72}
73
74VOID CCanvasWindow::getNewZoomRect(CRect& rcView, INT newZoom, CPoint ptTarget)
75{
76 CRect rcImage;
77 GetImageRect(rcImage);
78 ImageToCanvas(rcImage);
79
80 // Calculate the zoom rectangle
81 INT oldZoom = toolsModel.GetZoom();
82 GetClientRect(rcView);
83 LONG cxView = rcView.right * oldZoom / newZoom, cyView = rcView.bottom * oldZoom / newZoom;
84 rcView.SetRect(ptTarget.x - cxView / 2, ptTarget.y - cyView / 2,
85 ptTarget.x + cxView / 2, ptTarget.y + cyView / 2);
86
87 // Shift the rectangle if necessary
88 INT dx = 0, dy = 0;
89 if (rcView.left < rcImage.left)
90 dx = rcImage.left - rcView.left;
91 else if (rcImage.right < rcView.right)
92 dx = rcImage.right - rcView.right;
93 if (rcView.top < rcImage.top)
94 dy = rcImage.top - rcView.top;
95 else if (rcImage.bottom < rcView.bottom)
96 dy = rcImage.bottom - rcView.bottom;
97 rcView.OffsetRect(dx, dy);
98
99 rcView.IntersectRect(&rcView, &rcImage);
100}
101
102VOID CCanvasWindow::zoomTo(INT newZoom, LONG left, LONG top)
103{
104 POINT pt = { left, top };
105 CanvasToImage(pt);
106
107 toolsModel.SetZoom(newZoom);
108 ImageToCanvas(pt);
109 pt.x += GetScrollPos(SB_HORZ);
110 pt.y += GetScrollPos(SB_VERT);
111
112 updateScrollRange();
113 updateScrollPos(pt.x, pt.y);
114 Invalidate(TRUE);
115}
116
117BOOL CCanvasWindow::DoDraw(HDC hDC, RECT& rcClient, RECT& rcPaint)
118{
119 // This is the target area we have to draw on
120 CRect rcCanvasDraw;
121 rcCanvasDraw.IntersectRect(&rcClient, &rcPaint);
122
123 // Calculate image size
124 CRect rcImage;
125 GetImageRect(rcImage);
126 SIZE sizeImage = { imageModel.GetWidth(), imageModel.GetHeight() };
127
128 // We use a memory bitmap to reduce flickering
129 HBITMAP hbmCache1 = CreateDIBWithProperties(rcClient.right, rcClient.bottom);
130 if (!hbmCache1)
131 return FALSE; // Out of memory
132 HBITMAP hbmCache2 = CreateDIBWithProperties(sizeImage.cx, sizeImage.cy);
133 if (!hbmCache2)
134 {
135 ::DeleteObject(hbmCache1);
136 return FALSE; // Out of memory
137 }
138
139 HDC hdcMem0 = ::CreateCompatibleDC(hDC);
140 HGDIOBJ hbm0Old = ::SelectObject(hdcMem0, hbmCache1);
141
142 // Fill the background on hdcMem0
143 ::FillRect(hdcMem0, &rcCanvasDraw, (HBRUSH)(COLOR_APPWORKSPACE + 1));
144
145 // Draw the sizeboxes if necessary
146 RECT rcBase = GetBaseRect();
147 if (!selectionModel.m_bShow && !::IsWindowVisible(textEditWindow))
148 drawSizeBoxes(hdcMem0, &rcBase, FALSE, &rcCanvasDraw);
149
150 // Calculate the target area on the image
151 CRect rcImageDraw = rcCanvasDraw;
152 CanvasToImage(rcImageDraw);
153 rcImageDraw.IntersectRect(&rcImageDraw, &rcImage);
154
155 // Consider rounding down by zooming
156 rcImageDraw.right += 1;
157 rcImageDraw.bottom += 1;
158
159 // hdcMem1 <-- imageModel
160 HDC hdcMem1 = ::CreateCompatibleDC(hDC);
161 HGDIOBJ hbm1Old = ::SelectObject(hdcMem1, hbmCache2);
162 ::BitBlt(hdcMem1, rcImageDraw.left, rcImageDraw.top, rcImageDraw.Width(), rcImageDraw.Height(),
163 imageModel.GetDC(), rcImageDraw.left, rcImageDraw.top, SRCCOPY);
164
165 // Draw overlay #1 on hdcMem1
166 toolsModel.OnDrawOverlayOnImage(hdcMem1);
167
168 // Transfer the bits with stretch (hdcMem0 <-- hdcMem1)
169 ImageToCanvas(rcImage);
170 ::StretchBlt(hdcMem0, rcImage.left, rcImage.top, rcImage.Width(), rcImage.Height(),
171 hdcMem1, 0, 0, sizeImage.cx, sizeImage.cy, SRCCOPY);
172
173 // Clean up hdcMem1
174 ::SelectObject(hdcMem1, hbm1Old);
175 ::DeleteDC(hdcMem1);
176
177 // Draw the grid on hdcMem0
178 if (g_showGrid && toolsModel.GetZoom() >= 4000)
179 {
180 HPEN oldPen = (HPEN) ::SelectObject(hdcMem0, ::CreatePen(PS_SOLID, 1, RGB(160, 160, 160)));
181 for (INT counter = 0; counter < sizeImage.cy; counter++)
182 {
183 POINT pt0 = { 0, counter }, pt1 = { sizeImage.cx, counter };
184 ImageToCanvas(pt0);
185 ImageToCanvas(pt1);
186 ::MoveToEx(hdcMem0, pt0.x, pt0.y, NULL);
187 ::LineTo(hdcMem0, pt1.x, pt1.y);
188 }
189 for (INT counter = 0; counter < sizeImage.cx; counter++)
190 {
191 POINT pt0 = { counter, 0 }, pt1 = { counter, sizeImage.cy };
192 ImageToCanvas(pt0);
193 ImageToCanvas(pt1);
194 ::MoveToEx(hdcMem0, pt0.x, pt0.y, NULL);
195 ::LineTo(hdcMem0, pt1.x, pt1.y);
196 }
197 ::DeleteObject(::SelectObject(hdcMem0, oldPen));
198 }
199
200 // Draw overlay #2 on hdcMem0
201 toolsModel.OnDrawOverlayOnCanvas(hdcMem0);
202
203 // Draw new frame on hdcMem0 if any
204 if (m_hitCanvasSizeBox != HIT_NONE && !m_rcResizing.IsRectEmpty())
205 DrawXorRect(hdcMem0, &m_rcResizing);
206
207 // Transfer the bits (hDC <-- hdcMem0)
208 ::BitBlt(hDC, rcCanvasDraw.left, rcCanvasDraw.top, rcCanvasDraw.Width(), rcCanvasDraw.Height(),
209 hdcMem0, rcCanvasDraw.left, rcCanvasDraw.top, SRCCOPY);
210
211 // Clean up hdcMem0
212 ::SelectObject(hdcMem0, hbm0Old);
213 ::DeleteDC(hdcMem0);
214 ::DeleteObject(hbmCache2);
215 ::DeleteObject(hbmCache1);
216
217 return TRUE;
218}
219
220VOID CCanvasWindow::updateScrollRange()
221{
222 CRect rcClient;
223 GetClientRect(&rcClient);
224
225 CSize sizePage(rcClient.right, rcClient.bottom);
226 CSize sizeZoomed = { Zoomed(imageModel.GetWidth()), Zoomed(imageModel.GetHeight()) };
227 CSize sizeWhole = { sizeZoomed.cx + (GRIP_SIZE * 2), sizeZoomed.cy + (GRIP_SIZE * 2) };
228
229 // show/hide the scrollbars
230 ShowScrollBar(SB_HORZ, sizePage.cx < sizeWhole.cx);
231 ShowScrollBar(SB_VERT, sizePage.cy < sizeWhole.cy);
232
233 if (sizePage.cx < sizeWhole.cx || sizePage.cy < sizeWhole.cy)
234 {
235 GetClientRect(&rcClient); // Scrollbars might change, get client rectangle again
236 sizePage = CSize(rcClient.right, rcClient.bottom);
237 }
238
239 SCROLLINFO si = { sizeof(si), SIF_PAGE | SIF_RANGE };
240 si.nMin = 0;
241
242 si.nMax = sizeWhole.cx;
243 si.nPage = sizePage.cx;
244 SetScrollInfo(SB_HORZ, &si);
245
246 si.nMax = sizeWhole.cy;
247 si.nPage = sizePage.cy;
248 SetScrollInfo(SB_VERT, &si);
249}
250
251VOID CCanvasWindow::updateScrollPos(INT x, INT y)
252{
253 SetScrollPos(SB_HORZ, x);
254 SetScrollPos(SB_VERT, y);
255}
256
257LRESULT CCanvasWindow::OnSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
258{
259 if (m_hWnd)
260 updateScrollRange();
261
262 return 0;
263}
264
265VOID CCanvasWindow::OnHVScroll(WPARAM wParam, INT fnBar)
266{
267 SCROLLINFO si;
268 si.cbSize = sizeof(SCROLLINFO);
269 si.fMask = SIF_ALL;
270 GetScrollInfo(fnBar, &si);
271 switch (LOWORD(wParam))
272 {
273 case SB_THUMBTRACK:
274 case SB_THUMBPOSITION:
275 si.nPos = (SHORT)HIWORD(wParam);
276 break;
277 case SB_LINELEFT:
278 si.nPos -= 5;
279 break;
280 case SB_LINERIGHT:
281 si.nPos += 5;
282 break;
283 case SB_PAGELEFT:
284 si.nPos -= si.nPage;
285 break;
286 case SB_PAGERIGHT:
287 si.nPos += si.nPage;
288 break;
289 }
290 si.nPos = max(min(si.nPos, si.nMax), si.nMin);
291 SetScrollInfo(fnBar, &si);
292 Invalidate();
293}
294
295LRESULT CCanvasWindow::OnHScroll(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
296{
297 OnHVScroll(wParam, SB_HORZ);
298 return 0;
299}
300
301LRESULT CCanvasWindow::OnVScroll(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
302{
303 OnHVScroll(wParam, SB_VERT);
304 return 0;
305}
306
307LRESULT CCanvasWindow::OnButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
308{
309 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
310
311 m_nMouseDownMsg = nMsg;
312 BOOL bLeftButton = (nMsg == WM_LBUTTONDOWN);
313
314 if (nMsg == WM_MBUTTONDOWN)
315 {
316 m_ptOrig = pt;
317 SetCapture();
318 ::SetCursor(::LoadCursorW(g_hinstExe, MAKEINTRESOURCEW(IDC_HANDDRAG)));
319 return 0;
320 }
321
322 HITTEST hitSelection = selectionModel.hitTest(pt);
323 if (hitSelection != HIT_NONE)
324 {
325 m_drawing = TRUE;
326 CanvasToImage(pt);
327 SetCapture();
328 toolsModel.OnButtonDown(bLeftButton, pt.x, pt.y, FALSE);
329 Invalidate();
330 return 0;
331 }
332
333 HITTEST hit = CanvasHitTest(pt);
334 if (hit == HIT_NONE || hit == HIT_BORDER)
335 {
336 switch (toolsModel.GetActiveTool())
337 {
338 case TOOL_BEZIER:
339 case TOOL_SHAPE:
340 toolsModel.OnEndDraw(TRUE);
341 Invalidate();
342 break;
343
344 case TOOL_FREESEL:
345 case TOOL_RECTSEL:
346 toolsModel.OnEndDraw(FALSE);
347 Invalidate();
348 break;
349
350 default:
351 break;
352 }
353
354 toolsModel.resetTool(); // resets the point-buffer of the polygon and bezier functions
355 return 0;
356 }
357
358 CanvasToImage(pt);
359
360 if (hit == HIT_INNER)
361 {
362 m_drawing = TRUE;
363 SetCapture();
364 toolsModel.OnButtonDown(bLeftButton, pt.x, pt.y, FALSE);
365 Invalidate();
366 return 0;
367 }
368
369 if (bLeftButton)
370 {
371 m_hitCanvasSizeBox = hit;
372 m_ptOrig = pt;
373 SetCapture();
374 }
375
376 return 0;
377}
378
379LRESULT CCanvasWindow::OnButtonDblClk(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
380{
381 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
382 CanvasToImage(pt);
383
384 m_drawing = FALSE;
385 ::ReleaseCapture();
386 m_nMouseDownMsg = 0;
387
388 toolsModel.OnButtonDown(nMsg == WM_LBUTTONDBLCLK, pt.x, pt.y, TRUE);
389 toolsModel.resetTool();
390 Invalidate();
391 return 0;
392}
393
394LRESULT CCanvasWindow::OnMouseMove(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
395{
396 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
397
398 if (m_nMouseDownMsg == WM_MBUTTONDOWN)
399 {
400 INT x = GetScrollPos(SB_HORZ) - (pt.x - m_ptOrig.x);
401 INT y = GetScrollPos(SB_VERT) - (pt.y - m_ptOrig.y);
402 SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, x), 0);
403 SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBPOSITION, y), 0);
404 m_ptOrig = pt;
405 return 0;
406 }
407
408 CanvasToImage(pt);
409
410 if (toolsModel.GetActiveTool() == TOOL_ZOOM)
411 Invalidate();
412
413 if (!m_drawing || toolsModel.GetActiveTool() <= TOOL_AIRBRUSH)
414 {
415 TRACKMOUSEEVENT tme = { sizeof(tme) };
416 tme.dwFlags = TME_LEAVE;
417 tme.hwndTrack = m_hWnd;
418 tme.dwHoverTime = 0;
419 ::TrackMouseEvent(&tme);
420
421 if (!m_drawing)
422 {
423 CRect rcImage;
424 GetImageRect(rcImage);
425
426 CStringW strCoord;
427 if (rcImage.PtInRect(pt))
428 strCoord.Format(L"%ld, %ld", pt.x, pt.y);
429 ::SendMessageW(g_hStatusBar, SB_SETTEXT, 1, (LPARAM)(LPCWSTR)strCoord);
430 }
431 }
432
433 if (m_drawing || toolsModel.IsSelection())
434 {
435 toolsModel.DrawWithMouseTool(pt, wParam);
436 return 0;
437 }
438
439 if (m_hitCanvasSizeBox == HIT_NONE || ::GetCapture() != m_hWnd)
440 return 0;
441
442 // Dragging now... Calculate the new size
443 INT cxImage = imageModel.GetWidth(), cyImage = imageModel.GetHeight();
444 INT cxDelta = pt.x - m_ptOrig.x;
445 INT cyDelta = pt.y - m_ptOrig.y;
446 switch (m_hitCanvasSizeBox)
447 {
448 case HIT_UPPER_LEFT:
449 cxImage -= cxDelta;
450 cyImage -= cyDelta;
451 break;
452 case HIT_UPPER_CENTER:
453 cyImage -= cyDelta;
454 break;
455 case HIT_UPPER_RIGHT:
456 cxImage += cxDelta;
457 cyImage -= cyDelta;
458 break;
459 case HIT_MIDDLE_LEFT:
460 cxImage -= cxDelta;
461 break;
462 case HIT_MIDDLE_RIGHT:
463 cxImage += cxDelta;
464 break;
465 case HIT_LOWER_LEFT:
466 cxImage -= cxDelta;
467 cyImage += cyDelta;
468 break;
469 case HIT_LOWER_CENTER:
470 cyImage += cyDelta;
471 break;
472 case HIT_LOWER_RIGHT:
473 cxImage += cxDelta;
474 cyImage += cyDelta;
475 break;
476 default:
477 return 0;
478 }
479
480 // Limit bitmap size
481 cxImage = max(1, cxImage);
482 cyImage = max(1, cyImage);
483 cxImage = min(MAXWORD, cxImage);
484 cyImage = min(MAXWORD, cyImage);
485
486 // Display new size
487 CStringW strSize;
488 strSize.Format(L"%d x %d", cxImage, cyImage);
489 ::SendMessageW(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)(LPCWSTR)strSize);
490
491 // Dragging now... Fix the position...
492 CRect rcResizing = { 0, 0, cxImage, cyImage };
493 switch (m_hitCanvasSizeBox)
494 {
495 case HIT_UPPER_LEFT:
496 rcResizing.OffsetRect(cxDelta, cyDelta);
497 break;
498 case HIT_UPPER_CENTER:
499 rcResizing.OffsetRect(0, cyDelta);
500 break;
501 case HIT_UPPER_RIGHT:
502 rcResizing.OffsetRect(0, cyDelta);
503 break;
504 case HIT_MIDDLE_LEFT:
505 rcResizing.OffsetRect(cxDelta, 0);
506 break;
507 case HIT_LOWER_LEFT:
508 rcResizing.OffsetRect(cxDelta, 0);
509 break;
510 default:
511 break;
512 }
513 ImageToCanvas(rcResizing);
514 m_rcResizing = rcResizing;
515 Invalidate(TRUE);
516
517 return 0;
518}
519
520LRESULT CCanvasWindow::OnButtonUp(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
521{
522 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
523 CanvasToImage(pt);
524
525 ::ReleaseCapture();
526
527 BOOL bLeftButton = (m_nMouseDownMsg == WM_LBUTTONDOWN);
528 m_nMouseDownMsg = 0;
529
530 if (m_drawing)
531 {
532 m_drawing = FALSE;
533 toolsModel.OnButtonUp(bLeftButton, pt.x, pt.y);
534 Invalidate(FALSE);
535 ::SendMessageW(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)L"");
536 return 0;
537 }
538
539 if (m_hitCanvasSizeBox == HIT_NONE || !bLeftButton)
540 return 0;
541
542 // Resize the image
543 INT cxImage = imageModel.GetWidth(), cyImage = imageModel.GetHeight();
544 INT cxDelta = pt.x - m_ptOrig.x;
545 INT cyDelta = pt.y - m_ptOrig.y;
546 switch (m_hitCanvasSizeBox)
547 {
548 case HIT_UPPER_LEFT:
549 imageModel.Crop(cxImage - cxDelta, cyImage - cyDelta, cxDelta, cyDelta);
550 break;
551 case HIT_UPPER_CENTER:
552 imageModel.Crop(cxImage, cyImage - cyDelta, 0, cyDelta);
553 break;
554 case HIT_UPPER_RIGHT:
555 imageModel.Crop(cxImage + cxDelta, cyImage - cyDelta, 0, cyDelta);
556 break;
557 case HIT_MIDDLE_LEFT:
558 imageModel.Crop(cxImage - cxDelta, cyImage, cxDelta, 0);
559 break;
560 case HIT_MIDDLE_RIGHT:
561 imageModel.Crop(cxImage + cxDelta, cyImage, 0, 0);
562 break;
563 case HIT_LOWER_LEFT:
564 imageModel.Crop(cxImage - cxDelta, cyImage + cyDelta, cxDelta, 0);
565 break;
566 case HIT_LOWER_CENTER:
567 imageModel.Crop(cxImage, cyImage + cyDelta, 0, 0);
568 break;
569 case HIT_LOWER_RIGHT:
570 imageModel.Crop(cxImage + cxDelta, cyImage + cyDelta, 0, 0);
571 break;
572 default:
573 break;
574 }
575 m_rcResizing.SetRectEmpty();
576
577 g_imageSaved = FALSE;
578
579 m_hitCanvasSizeBox = HIT_NONE;
580 toolsModel.resetTool(); // resets the point-buffer of the polygon and bezier functions
581 updateScrollRange();
582 Invalidate(TRUE);
583 return 0;
584}
585
586LRESULT CCanvasWindow::OnSetCursor(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
587{
588 if (CWaitCursor::IsWaiting())
589 {
590 bHandled = FALSE;
591 return 0;
592 }
593
594 if (m_nMouseDownMsg == WM_MBUTTONDOWN)
595 {
596 ::SetCursor(::LoadCursorW(g_hinstExe, MAKEINTRESOURCEW(IDC_HANDDRAG)));
597 return 0;
598 }
599
600 POINT pt;
601 ::GetCursorPos(&pt);
602 ScreenToClient(&pt);
603
604 CRect rcClient;
605 GetClientRect(&rcClient);
606
607 if (!rcClient.PtInRect(pt))
608 {
609 bHandled = FALSE;
610 return 0;
611 }
612
613 HITTEST hitSelection = selectionModel.hitTest(pt);
614 if (hitSelection != HIT_NONE)
615 {
616 if (!setCursorOnSizeBox(hitSelection))
617 ::SetCursor(::LoadCursorW(NULL, (LPCWSTR)IDC_SIZEALL));
618 return 0;
619 }
620
621 CRect rcImage;
622 GetImageRect(rcImage);
623 ImageToCanvas(rcImage);
624
625 if (rcImage.PtInRect(pt))
626 {
627 switch (toolsModel.GetActiveTool())
628 {
629 case TOOL_FILL:
630 ::SetCursor(::LoadCursorW(g_hinstExe, MAKEINTRESOURCEW(IDC_FILL)));
631 break;
632 case TOOL_COLOR:
633 ::SetCursor(::LoadCursorW(g_hinstExe, MAKEINTRESOURCEW(IDC_COLOR)));
634 break;
635 case TOOL_ZOOM:
636 ::SetCursor(::LoadCursorW(g_hinstExe, MAKEINTRESOURCEW(IDC_ZOOM)));
637 break;
638 case TOOL_PEN:
639 ::SetCursor(::LoadCursorW(g_hinstExe, MAKEINTRESOURCEW(IDC_PEN)));
640 break;
641 case TOOL_AIRBRUSH:
642 ::SetCursor(::LoadCursorW(g_hinstExe, MAKEINTRESOURCEW(IDC_AIRBRUSH)));
643 break;
644 default:
645 ::SetCursor(::LoadCursorW(NULL, (LPCWSTR)IDC_CROSS));
646 }
647 return 0;
648 }
649
650 if (selectionModel.m_bShow || !setCursorOnSizeBox(CanvasHitTest(pt)))
651 bHandled = FALSE;
652
653 return 0;
654}
655
656LRESULT CCanvasWindow::OnKeyDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
657{
658 if (wParam == VK_ESCAPE)
659 {
660 OnEndDraw(TRUE);
661 ::ReleaseCapture();
662 m_nMouseDownMsg = 0;
663 m_hitCanvasSizeBox = HIT_NONE;
664 m_rcResizing.SetRectEmpty();
665 Invalidate(TRUE);
666 }
667
668 return 0;
669}
670
671LRESULT CCanvasWindow::OnCancelMode(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
672{
673 // Cancel dragging
674 m_hitCanvasSizeBox = HIT_NONE;
675 m_rcResizing.SetRectEmpty();
676 Invalidate(TRUE);
677 return 0;
678}
679
680LRESULT CCanvasWindow::OnMouseWheel(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
681{
682 return ::SendMessageW(GetParent(), nMsg, wParam, lParam);
683}
684
685LRESULT CCanvasWindow::OnCaptureChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
686{
687 ::SendMessageW(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)L"");
688 return 0;
689}
690
691LRESULT CCanvasWindow::OnEraseBkgnd(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
692{
693 return TRUE; // do nothing => transparent background
694}
695
696LRESULT CCanvasWindow::OnPaint(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
697{
698 RECT rcClient;
699 GetClientRect(&rcClient);
700
701 static BOOL s_bShowedOutOfMemory = FALSE; // Don't show "Out Of Memory" message multiple time
702
703 PAINTSTRUCT ps;
704 HDC hDC = BeginPaint(&ps);
705
706 if (DoDraw(hDC, rcClient, ps.rcPaint))
707 {
708 s_bShowedOutOfMemory = FALSE;
709 }
710 else if (!s_bShowedOutOfMemory)
711 {
712 ShowOutOfMemory();
713 s_bShowedOutOfMemory = TRUE;
714 imageModel.ClearHistory(); // Reduce memory usage
715 }
716
717 EndPaint(&ps);
718 return 0;
719}
720
721VOID CCanvasWindow::OnEndDraw(BOOL bCancel)
722{
723 m_drawing = FALSE;
724 toolsModel.OnEndDraw(bCancel);
725 Invalidate(FALSE);
726}
727
728LRESULT CCanvasWindow::OnCtlColorEdit(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
729{
730 SetTextColor((HDC)wParam, paletteModel.GetFgColor());
731 SetBkMode((HDC)wParam, TRANSPARENT);
732 return (LRESULT)GetStockObject(NULL_BRUSH);
733}
734
735LRESULT CCanvasWindow::OnPaletteModelColorChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
736{
737 imageModel.NotifyImageChanged();
738 return 0;
739}