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