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