1 /*
2 * PROJECT: PAINT for ReactOS
3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
4 * PURPOSE: Things which should not be in the mouse event handler itself
5 * COPYRIGHT: Copyright 2015 Benedikt Freisen <b.freisen@gmx.net>
6 * Copyright 2021-2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
7 */
8
9 #include "precomp.h"
10 #include <atlalloc.h>
11
12 static SIZE_T s_cPoints = 0;
13 static CHeapPtr<POINT, CLocalAllocator> s_dynamicPoints;
14 static POINT s_staticPoints[512]; // 512 is enough
15 static SIZE_T s_maxPoints = _countof(s_staticPoints);
16 static LPPOINT s_pPoints = s_staticPoints;
17 static POINT g_ptStart, g_ptEnd;
18
19 /* FUNCTIONS ********************************************************/
20
21 void
regularize(LONG x0,LONG y0,LONG & x1,LONG & y1)22 regularize(LONG x0, LONG y0, LONG& x1, LONG& y1)
23 {
24 if (labs(x1 - x0) >= labs(y1 - y0))
25 y1 = y0 + (y1 > y0 ? labs(x1 - x0) : -labs(x1 - x0));
26 else
27 x1 = x0 + (x1 > x0 ? labs(y1 - y0) : -labs(y1 - y0));
28 }
29
30 void
roundTo8Directions(LONG x0,LONG y0,LONG & x1,LONG & y1)31 roundTo8Directions(LONG x0, LONG y0, LONG& x1, LONG& y1)
32 {
33 if (labs(x1 - x0) >= labs(y1 - y0))
34 {
35 if (labs(y1 - y0) * 5 < labs(x1 - x0) * 2)
36 y1 = y0;
37 else
38 y1 = y0 + (y1 > y0 ? labs(x1 - x0) : -labs(x1 - x0));
39 }
40 else
41 {
42 if (labs(x1 - x0) * 5 < labs(y1 - y0) * 2)
43 x1 = x0;
44 else
45 x1 = x0 + (x1 > x0 ? labs(y1 - y0) : -labs(y1 - y0));
46 }
47 }
48
nearlyEqualPoints(INT x0,INT y0,INT x1,INT y1)49 BOOL nearlyEqualPoints(INT x0, INT y0, INT x1, INT y1)
50 {
51 INT cxThreshold = toolsModel.GetLineWidth() + UnZoomed(GetSystemMetrics(SM_CXDRAG));
52 INT cyThreshold = toolsModel.GetLineWidth() + UnZoomed(GetSystemMetrics(SM_CYDRAG));
53 return (abs(x1 - x0) <= cxThreshold) && (abs(y1 - y0) <= cyThreshold);
54 }
55
getBoundaryOfPoints(RECT & rcBoundary,SIZE_T cPoints,const POINT * pPoints)56 void getBoundaryOfPoints(RECT& rcBoundary, SIZE_T cPoints, const POINT *pPoints)
57 {
58 POINT ptMin = { MAXLONG, MAXLONG }, ptMax = { (LONG)MINLONG, (LONG)MINLONG };
59 while (cPoints-- > 0)
60 {
61 LONG x = pPoints->x, y = pPoints->y;
62 ptMin = { min(x, ptMin.x), min(y, ptMin.y) };
63 ptMax = { max(x, ptMax.x), max(y, ptMax.y) };
64 ++pPoints;
65 }
66
67 ptMax.x += 1;
68 ptMax.y += 1;
69
70 CRect rc(ptMin, ptMax);
71 rcBoundary = rc;
72 }
73
ShiftPoints(INT dx,INT dy)74 void ShiftPoints(INT dx, INT dy)
75 {
76 for (SIZE_T i = 0; i < s_cPoints; ++i)
77 {
78 POINT& pt = s_pPoints[i];
79 pt.x += dx;
80 pt.y += dy;
81 }
82 }
83
BuildMaskFromPoints()84 void BuildMaskFromPoints()
85 {
86 CRect rc;
87 getBoundaryOfPoints(rc, s_cPoints, s_pPoints);
88
89 ShiftPoints(-rc.left, -rc.top);
90
91 HDC hdcMem = ::CreateCompatibleDC(NULL);
92 HBITMAP hbmMask = ::CreateBitmap(rc.Width(), rc.Height(), 1, 1, NULL);
93 HGDIOBJ hbmOld = ::SelectObject(hdcMem, hbmMask);
94 ::FillRect(hdcMem, &rc, (HBRUSH)::GetStockObject(BLACK_BRUSH));
95 HGDIOBJ hPenOld = ::SelectObject(hdcMem, GetStockObject(NULL_PEN));
96 HGDIOBJ hbrOld = ::SelectObject(hdcMem, GetStockObject(WHITE_BRUSH));
97 ::Polygon(hdcMem, s_pPoints, (INT)s_cPoints);
98 ::SelectObject(hdcMem, hbrOld);
99 ::SelectObject(hdcMem, hPenOld);
100 ::SelectObject(hdcMem, hbmOld);
101 ::DeleteDC(hdcMem);
102
103 selectionModel.setMask(rc, hbmMask);
104 }
105
reset()106 void ToolBase::reset()
107 {
108 if (s_pPoints != s_staticPoints)
109 {
110 s_dynamicPoints.Free();
111 s_pPoints = s_staticPoints;
112 s_maxPoints = _countof(s_staticPoints);
113 }
114
115 s_cPoints = 0;
116 g_ptEnd = g_ptStart = { -1, -1 };
117
118 if (selectionModel.m_bShow)
119 {
120 selectionModel.Landing();
121 selectionModel.HideSelection();
122 }
123 }
124
OnEndDraw(BOOL bCancel)125 void ToolBase::OnEndDraw(BOOL bCancel)
126 {
127 reset();
128 imageModel.NotifyImageChanged();
129 }
130
beginEvent()131 void ToolBase::beginEvent()
132 {
133 m_hdc = imageModel.GetDC();
134 m_fg = paletteModel.GetFgColor();
135 m_bg = paletteModel.GetBgColor();
136 }
137
endEvent()138 void ToolBase::endEvent()
139 {
140 m_hdc = NULL;
141 }
142
pushToPoints(LONG x,LONG y)143 static void pushToPoints(LONG x, LONG y)
144 {
145 if (s_cPoints + 1 >= s_maxPoints)
146 {
147 SIZE_T newMax = s_maxPoints + 512;
148 SIZE_T cbNew = newMax * sizeof(POINT);
149 if (!s_dynamicPoints.ReallocateBytes(cbNew))
150 {
151 ATLTRACE("%d, %d, %d\n", (INT)s_cPoints, (INT)s_maxPoints, (INT)cbNew);
152 return;
153 }
154
155 if (s_pPoints == s_staticPoints)
156 CopyMemory(s_dynamicPoints, s_staticPoints, s_cPoints * sizeof(POINT));
157
158 s_pPoints = s_dynamicPoints;
159 s_maxPoints = newMax;
160 }
161
162 s_pPoints[s_cPoints++] = { x, y };
163 }
164
165 /* TOOLS ********************************************************/
166
167 struct TwoPointDrawTool : ToolBase
168 {
169 BOOL m_bLeftButton = FALSE;
170 BOOL m_bDrawing = FALSE;
171
OnButtonDownTwoPointDrawTool172 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
173 {
174 m_bLeftButton = bLeftButton;
175 m_bDrawing = TRUE;
176 imageModel.NotifyImageChanged();
177 }
178
OnMouseMoveTwoPointDrawTool179 BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override
180 {
181 imageModel.NotifyImageChanged();
182 return TRUE;
183 }
184
OnButtonUpTwoPointDrawTool185 BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override
186 {
187 CRect rcPartial(g_ptStart, g_ptEnd);
188 rcPartial.NormalizeRect();
189 SIZE size = toolsModel.GetToolSize();
190 rcPartial.InflateRect((size.cx + 1) / 2, (size.cy + 1) / 2);
191 imageModel.PushImageForUndo(rcPartial);
192
193 OnDrawOverlayOnImage(m_hdc);
194 m_bDrawing = FALSE;
195 imageModel.NotifyImageChanged();
196 return TRUE;
197 }
198
OnEndDrawTwoPointDrawTool199 void OnEndDraw(BOOL bCancel) override
200 {
201 m_bDrawing = FALSE;
202 ToolBase::OnEndDraw(bCancel);
203 }
204
OnSpecialTweakTwoPointDrawTool205 void OnSpecialTweak(BOOL bMinus) override
206 {
207 toolsModel.MakeLineThickerOrThinner(bMinus);
208 }
209 };
210
211 typedef enum DIRECTION
212 {
213 NO_DIRECTION = -1,
214 DIRECTION_HORIZONTAL,
215 DIRECTION_VERTICAL,
216 DIRECTION_DIAGONAL_RIGHT_DOWN,
217 DIRECTION_DIAGONAL_RIGHT_UP,
218 } DIRECTION;
219
220 #define THRESHOULD_DEG 15
221
222 static DIRECTION
GetDirection(LONG x0,LONG y0,LONG x1,LONG y1)223 GetDirection(LONG x0, LONG y0, LONG x1, LONG y1)
224 {
225 LONG dx = x1 - x0, dy = y1 - y0;
226
227 if (labs(dx) <= 8 && labs(dy) <= 8)
228 return NO_DIRECTION;
229
230 double radian = atan2((double)dy, (double)dx);
231 if (radian < DEG2RAD(-180 + THRESHOULD_DEG))
232 {
233 ATLTRACE("DIRECTION_HORIZONTAL: %ld\n", RAD2DEG(radian));
234 return DIRECTION_HORIZONTAL;
235 }
236 if (radian < DEG2RAD(-90 - THRESHOULD_DEG))
237 {
238 ATLTRACE("DIRECTION_DIAGONAL_RIGHT_DOWN: %ld\n", RAD2DEG(radian));
239 return DIRECTION_DIAGONAL_RIGHT_DOWN;
240 }
241 if (radian < DEG2RAD(-90 + THRESHOULD_DEG))
242 {
243 ATLTRACE("DIRECTION_VERTICAL: %ld\n", RAD2DEG(radian));
244 return DIRECTION_VERTICAL;
245 }
246 if (radian < DEG2RAD(-THRESHOULD_DEG))
247 {
248 ATLTRACE("DIRECTION_DIAGONAL_RIGHT_UP: %ld\n", RAD2DEG(radian));
249 return DIRECTION_DIAGONAL_RIGHT_UP;
250 }
251 if (radian < DEG2RAD(+THRESHOULD_DEG))
252 {
253 ATLTRACE("DIRECTION_HORIZONTAL: %ld\n", RAD2DEG(radian));
254 return DIRECTION_HORIZONTAL;
255 }
256 if (radian < DEG2RAD(+90 - THRESHOULD_DEG))
257 {
258 ATLTRACE("DIRECTION_DIAGONAL_RIGHT_DOWN: %ld\n", RAD2DEG(radian));
259 return DIRECTION_DIAGONAL_RIGHT_DOWN;
260 }
261 if (radian < DEG2RAD(+90 + THRESHOULD_DEG))
262 {
263 ATLTRACE("DIRECTION_VERTICAL: %ld\n", RAD2DEG(radian));
264 return DIRECTION_VERTICAL;
265 }
266 if (radian < DEG2RAD(+180 - THRESHOULD_DEG))
267 {
268 ATLTRACE("DIRECTION_DIAGONAL_RIGHT_UP: %ld\n", RAD2DEG(radian));
269 return DIRECTION_DIAGONAL_RIGHT_UP;
270 }
271 ATLTRACE("DIRECTION_HORIZONTAL: %ld\n", RAD2DEG(radian));
272 return DIRECTION_HORIZONTAL;
273 }
274
275 static void
RestrictDrawDirection(DIRECTION dir,LONG x0,LONG y0,LONG & x1,LONG & y1)276 RestrictDrawDirection(DIRECTION dir, LONG x0, LONG y0, LONG& x1, LONG& y1)
277 {
278 switch (dir)
279 {
280 case NO_DIRECTION:
281 default:
282 return;
283
284 case DIRECTION_HORIZONTAL:
285 y1 = y0;
286 break;
287
288 case DIRECTION_VERTICAL:
289 x1 = x0;
290 break;
291
292 case DIRECTION_DIAGONAL_RIGHT_DOWN:
293 y1 = y0 + (x1 - x0);
294 break;
295
296 case DIRECTION_DIAGONAL_RIGHT_UP:
297 x1 = x0 - (y1 - y0);
298 break;
299 }
300 }
301
302 struct SmoothDrawTool : ToolBase
303 {
304 DIRECTION m_direction = NO_DIRECTION;
305 BOOL m_bShiftDown = FALSE;
306 BOOL m_bLeftButton = FALSE;
307
308 virtual void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) = 0;
309
OnButtonDownSmoothDrawTool310 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
311 {
312 m_direction = NO_DIRECTION;
313 m_bShiftDown = (::GetKeyState(VK_SHIFT) & 0x8000); // Is Shift key pressed?
314 m_bLeftButton = bLeftButton;
315 s_cPoints = 0;
316 pushToPoints(x, y);
317 pushToPoints(x, y); // We have to draw the first point
318 imageModel.NotifyImageChanged();
319 }
320
OnMouseMoveSmoothDrawTool321 BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override
322 {
323 if (!m_bShiftDown)
324 {
325 pushToPoints(x, y);
326 imageModel.NotifyImageChanged();
327 return TRUE;
328 }
329
330 if (m_direction == NO_DIRECTION)
331 {
332 m_direction = GetDirection(g_ptStart.x, g_ptStart.y, x, y);
333 if (m_direction == NO_DIRECTION)
334 return FALSE;
335 }
336
337 RestrictDrawDirection(m_direction, g_ptStart.x, g_ptStart.y, x, y);
338 pushToPoints(x, y);
339 imageModel.NotifyImageChanged();
340 return TRUE;
341 }
342
OnButtonUpSmoothDrawTool343 BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override
344 {
345 if (m_bShiftDown && m_direction != NO_DIRECTION)
346 RestrictDrawDirection(m_direction, g_ptStart.x, g_ptStart.y, x, y);
347
348 pushToPoints(x, y);
349
350 CRect rcPartial;
351 getBoundaryOfPoints(rcPartial, s_cPoints, s_pPoints);
352
353 SIZE size = toolsModel.GetToolSize();
354 rcPartial.InflateRect((size.cx + 1) / 2, (size.cy + 1) / 2);
355
356 imageModel.PushImageForUndo(rcPartial);
357
358 OnDrawOverlayOnImage(m_hdc);
359 imageModel.NotifyImageChanged();
360 OnEndDraw(FALSE);
361 return TRUE;
362 }
363
OnDrawOverlayOnImageSmoothDrawTool364 void OnDrawOverlayOnImage(HDC hdc) override
365 {
366 for (SIZE_T i = 1; i < s_cPoints; ++i)
367 {
368 OnDraw(hdc, m_bLeftButton, s_pPoints[i - 1], s_pPoints[i]);
369 }
370 }
371 };
372
373 struct SelectionBaseTool : ToolBase
374 {
375 BOOL m_bLeftButton = FALSE;
376 BOOL m_bCtrlKey = FALSE;
377 BOOL m_bShiftKey = FALSE;
378 BOOL m_bDrawing = FALSE;
379 BOOL m_bNoDrawBack = FALSE;
380 HITTEST m_hitSelection = HIT_NONE;
381
isRectSelectSelectionBaseTool382 BOOL isRectSelect() const
383 {
384 return (toolsModel.GetActiveTool() == TOOL_RECTSEL);
385 }
386
OnDrawOverlayOnImageSelectionBaseTool387 void OnDrawOverlayOnImage(HDC hdc) override
388 {
389 if (selectionModel.IsLanded() || !selectionModel.m_bShow)
390 return;
391
392 if (!m_bNoDrawBack)
393 selectionModel.DrawBackground(hdc, selectionModel.m_rgbBack);
394
395 selectionModel.DrawSelection(hdc, paletteModel.GetBgColor(), toolsModel.IsBackgroundTransparent());
396 }
397
OnDrawOverlayOnCanvasSelectionBaseTool398 void OnDrawOverlayOnCanvas(HDC hdc) override
399 {
400 if (m_bDrawing || selectionModel.m_bShow)
401 selectionModel.drawFrameOnCanvas(hdc);
402 }
403
OnButtonDownSelectionBaseTool404 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
405 {
406 m_bLeftButton = bLeftButton;
407 m_bCtrlKey = (::GetKeyState(VK_CONTROL) < 0);
408 m_bShiftKey = (::GetKeyState(VK_SHIFT) < 0);
409 m_bDrawing = FALSE;
410 m_hitSelection = HIT_NONE;
411
412 POINT pt = { x, y };
413 if (!m_bLeftButton) // Show context menu on Right-click
414 {
415 canvasWindow.ImageToCanvas(pt);
416 canvasWindow.ClientToScreen(&pt);
417 mainWindow.TrackPopupMenu(pt, 0);
418 return;
419 }
420
421 POINT ptCanvas = pt;
422 canvasWindow.ImageToCanvas(ptCanvas);
423 HITTEST hit = selectionModel.hitTest(ptCanvas);
424 if (hit != HIT_NONE) // Dragging of selection started?
425 {
426 if (m_bCtrlKey || m_bShiftKey)
427 {
428 imageModel.PushImageForUndo();
429 toolsModel.OnDrawOverlayOnImage(imageModel.GetDC());
430 }
431 m_hitSelection = hit;
432 selectionModel.m_ptHit = pt;
433 selectionModel.TakeOff();
434 m_bNoDrawBack |= (m_bCtrlKey || m_bShiftKey);
435 imageModel.NotifyImageChanged();
436 return;
437 }
438
439 selectionModel.Landing();
440 m_bDrawing = TRUE;
441
442 imageModel.Clamp(pt);
443 if (isRectSelect())
444 {
445 selectionModel.SetRectFromPoints(g_ptStart, pt);
446 }
447 else
448 {
449 s_cPoints = 0;
450 pushToPoints(pt.x, pt.y);
451 }
452
453 imageModel.NotifyImageChanged();
454 }
455
OnMouseMoveSelectionBaseTool456 BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override
457 {
458 POINT pt = { x, y };
459
460 if (!m_bLeftButton)
461 return TRUE;
462
463 if (m_hitSelection != HIT_NONE) // Now dragging selection?
464 {
465 if (m_bShiftKey)
466 toolsModel.OnDrawOverlayOnImage(imageModel.GetDC());
467
468 selectionModel.Dragging(m_hitSelection, pt);
469 imageModel.NotifyImageChanged();
470 return TRUE;
471 }
472
473 if (isRectSelect() && ::GetKeyState(VK_SHIFT) < 0)
474 regularize(g_ptStart.x, g_ptStart.y, pt.x, pt.y);
475
476 imageModel.Clamp(pt);
477
478 if (isRectSelect())
479 selectionModel.SetRectFromPoints(g_ptStart, pt);
480 else
481 pushToPoints(pt.x, pt.y);
482
483 imageModel.NotifyImageChanged();
484 return TRUE;
485 }
486
OnButtonUpSelectionBaseTool487 BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override
488 {
489 POINT pt = { x, y };
490 m_bDrawing = FALSE;
491
492 if (!m_bLeftButton)
493 return TRUE;
494
495 if (m_hitSelection != HIT_NONE) // Dragging of selection ended?
496 {
497 if (m_bShiftKey)
498 toolsModel.OnDrawOverlayOnImage(imageModel.GetDC());
499
500 selectionModel.Dragging(m_hitSelection, pt);
501 m_hitSelection = HIT_NONE;
502 imageModel.NotifyImageChanged();
503 return TRUE;
504 }
505
506 if (isRectSelect() && ::GetKeyState(VK_SHIFT) < 0)
507 regularize(g_ptStart.x, g_ptStart.y, pt.x, pt.y);
508
509 imageModel.Clamp(pt);
510
511 if (isRectSelect())
512 {
513 selectionModel.SetRectFromPoints(g_ptStart, pt);
514 selectionModel.m_bShow = !selectionModel.m_rc.IsRectEmpty();
515 }
516 else
517 {
518 if (s_cPoints > 2)
519 {
520 BuildMaskFromPoints();
521 selectionModel.m_bShow = TRUE;
522 }
523 else
524 {
525 s_cPoints = 0;
526 selectionModel.m_bShow = FALSE;
527 }
528 }
529
530 m_bNoDrawBack = FALSE;
531 imageModel.NotifyImageChanged();
532 return TRUE;
533 }
534
OnEndDrawSelectionBaseTool535 void OnEndDraw(BOOL bCancel) override
536 {
537 if (bCancel)
538 selectionModel.HideSelection();
539 else
540 selectionModel.Landing();
541
542 m_bDrawing = FALSE;
543 m_hitSelection = HIT_NONE;
544 ToolBase::OnEndDraw(bCancel);
545 }
546
OnSpecialTweakSelectionBaseTool547 void OnSpecialTweak(BOOL bMinus) override
548 {
549 selectionModel.StretchSelection(bMinus);
550 }
551 };
552
553 // TOOL_FREESEL
554 struct FreeSelTool : SelectionBaseTool
555 {
OnDrawOverlayOnImageFreeSelTool556 void OnDrawOverlayOnImage(HDC hdc) override
557 {
558 SelectionBaseTool::OnDrawOverlayOnImage(hdc);
559
560 if (!selectionModel.m_bShow && m_bDrawing)
561 {
562 /* Draw the freehand selection inverted/xored */
563 Poly(hdc, s_pPoints, (INT)s_cPoints, 0, 0, 2, 0, FALSE, TRUE);
564 }
565 }
566 };
567
568 // TOOL_RECTSEL
569 struct RectSelTool : SelectionBaseTool
570 {
OnDrawOverlayOnImageRectSelTool571 void OnDrawOverlayOnImage(HDC hdc) override
572 {
573 SelectionBaseTool::OnDrawOverlayOnImage(hdc);
574
575 if (!selectionModel.m_bShow && m_bDrawing)
576 {
577 CRect& rc = selectionModel.m_rc;
578 if (!rc.IsRectEmpty())
579 RectSel(hdc, rc.left, rc.top, rc.right, rc.bottom);
580 }
581 }
582 };
583
584 // TOOL_RUBBER
585 struct RubberTool : SmoothDrawTool
586 {
OnDrawRubberTool587 void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) override
588 {
589 if (bLeftButton)
590 Erase(hdc, pt0.x, pt0.y, pt1.x, pt1.y, m_bg, toolsModel.GetRubberRadius());
591 else
592 Replace(hdc, pt0.x, pt0.y, pt1.x, pt1.y, m_fg, m_bg, toolsModel.GetRubberRadius());
593 }
594
OnSpecialTweakRubberTool595 void OnSpecialTweak(BOOL bMinus) override
596 {
597 toolsModel.MakeRubberThickerOrThinner(bMinus);
598 }
599 };
600
601 // TOOL_FILL
602 struct FillTool : ToolBase
603 {
OnButtonDownFillTool604 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
605 {
606 imageModel.PushImageForUndo();
607 Fill(m_hdc, x, y, bLeftButton ? m_fg : m_bg);
608 }
609 };
610
611 // TOOL_COLOR
612 struct ColorTool : ToolBase
613 {
fetchColorColorTool614 void fetchColor(BOOL bLeftButton, LONG x, LONG y)
615 {
616 COLORREF rgbColor;
617
618 if (0 <= x && x < imageModel.GetWidth() && 0 <= y && y < imageModel.GetHeight())
619 rgbColor = GetPixel(m_hdc, x, y);
620 else
621 rgbColor = RGB(255, 255, 255); // Outside is white
622
623 if (bLeftButton)
624 paletteModel.SetFgColor(rgbColor);
625 else
626 paletteModel.SetBgColor(rgbColor);
627 }
628
OnMouseMoveColorTool629 BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override
630 {
631 fetchColor(bLeftButton, x, y);
632 return TRUE;
633 }
634
OnButtonUpColorTool635 BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override
636 {
637 fetchColor(bLeftButton, x, y);
638 toolsModel.SetActiveTool(toolsModel.GetOldActiveTool());
639 return TRUE;
640 }
641 };
642
643 // TOOL_ZOOM
644 struct ZoomTool : ToolBase
645 {
646 BOOL m_bZoomed = FALSE;
647
648 BOOL getNewZoomRect(CRect& rcView, INT newZoom);
649
OnDrawOverlayOnCanvasZoomTool650 void OnDrawOverlayOnCanvas(HDC hdc) override
651 {
652 CRect rcView;
653 INT oldZoom = toolsModel.GetZoom();
654 if (oldZoom < MAX_ZOOM && getNewZoomRect(rcView, oldZoom * 2))
655 DrawXorRect(hdc, &rcView);
656 }
657
OnButtonDownZoomTool658 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
659 {
660 INT newZoom, oldZoom = toolsModel.GetZoom();
661 if (bLeftButton)
662 newZoom = (oldZoom < MAX_ZOOM) ? (oldZoom * 2) : MIN_ZOOM;
663 else
664 newZoom = (oldZoom > MIN_ZOOM) ? (oldZoom / 2) : MAX_ZOOM;
665
666 m_bZoomed = FALSE;
667
668 if (oldZoom != newZoom)
669 {
670 CRect rcView;
671 if (getNewZoomRect(rcView, newZoom))
672 {
673 canvasWindow.zoomTo(newZoom, rcView.left, rcView.top);
674 m_bZoomed = TRUE;
675 }
676 }
677 }
678
OnButtonUpZoomTool679 BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override
680 {
681 if (m_bZoomed)
682 toolsModel.SetActiveTool(toolsModel.GetOldActiveTool());
683
684 return TRUE;
685 }
686 };
687
getNewZoomRect(CRect & rcView,INT newZoom)688 BOOL ZoomTool::getNewZoomRect(CRect& rcView, INT newZoom)
689 {
690 CPoint pt;
691 ::GetCursorPos(&pt);
692 canvasWindow.ScreenToClient(&pt);
693
694 canvasWindow.getNewZoomRect(rcView, newZoom, pt);
695
696 CRect rc;
697 canvasWindow.GetImageRect(rc);
698 canvasWindow.ImageToCanvas(rc);
699
700 return rc.PtInRect(pt);
701 }
702
703 // TOOL_PEN
704 struct PenTool : SmoothDrawTool
705 {
OnDrawPenTool706 void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) override
707 {
708 COLORREF rgb = bLeftButton ? m_fg : m_bg;
709 Line(hdc, pt0.x, pt0.y, pt1.x, pt1.y, rgb, toolsModel.GetPenWidth());
710 }
711
OnSpecialTweakPenTool712 void OnSpecialTweak(BOOL bMinus) override
713 {
714 toolsModel.MakePenThickerOrThinner(bMinus);
715 }
716 };
717
718 // TOOL_BRUSH
719 struct BrushTool : SmoothDrawTool
720 {
OnDrawBrushTool721 void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) override
722 {
723 COLORREF rgb = bLeftButton ? m_fg : m_bg;
724 Brush(hdc, pt0.x, pt0.y, pt1.x, pt1.y, rgb, toolsModel.GetBrushStyle(),
725 toolsModel.GetBrushWidth());
726 }
727
OnSpecialTweakBrushTool728 void OnSpecialTweak(BOOL bMinus) override
729 {
730 toolsModel.MakeBrushThickerOrThinner(bMinus);
731 }
732 };
733
734 // TOOL_AIRBRUSH
735 struct AirBrushTool : SmoothDrawTool
736 {
737 DWORD m_dwTick = 0;
738
OnButtonDownAirBrushTool739 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
740 {
741 m_dwTick = GetTickCount();
742 SmoothDrawTool::OnButtonDown(bLeftButton, x, y, bDoubleClick);
743 }
744
OnDrawOverlayOnImageAirBrushTool745 void OnDrawOverlayOnImage(HDC hdc) override
746 {
747 srand(m_dwTick);
748 SmoothDrawTool::OnDrawOverlayOnImage(hdc);
749 }
750
OnDrawAirBrushTool751 void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) override
752 {
753 COLORREF rgb = bLeftButton ? m_fg : m_bg;
754 Airbrush(hdc, pt1.x, pt1.y, rgb, toolsModel.GetAirBrushRadius());
755 }
756
OnSpecialTweakAirBrushTool757 void OnSpecialTweak(BOOL bMinus) override
758 {
759 toolsModel.MakeAirBrushThickerOrThinner(bMinus);
760 }
761 };
762
763 // TOOL_TEXT
764 struct TextTool : ToolBase
765 {
OnDrawOverlayOnImageTextTool766 void OnDrawOverlayOnImage(HDC hdc) override
767 {
768 if (canvasWindow.m_drawing)
769 {
770 CRect& rc = selectionModel.m_rc;
771 if (!rc.IsRectEmpty())
772 RectSel(hdc, rc.left, rc.top, rc.right, rc.bottom);
773 }
774 }
775
UpdatePointTextTool776 void UpdatePoint(LONG x, LONG y)
777 {
778 POINT pt = { x, y };
779 imageModel.Clamp(pt);
780 selectionModel.SetRectFromPoints(g_ptStart, pt);
781 imageModel.NotifyImageChanged();
782 }
783
OnButtonDownTextTool784 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
785 {
786 if (!textEditWindow.IsWindow())
787 textEditWindow.Create(canvasWindow);
788
789 UpdatePoint(x, y);
790 }
791
OnMouseMoveTextTool792 BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override
793 {
794 UpdatePoint(x, y);
795 return TRUE;
796 }
797
drawTextTool798 void draw(HDC hdc)
799 {
800 CStringW szText;
801 textEditWindow.GetWindowText(szText);
802
803 CRect rc;
804 textEditWindow.InvalidateEditRect();
805 textEditWindow.GetEditRect(&rc);
806 rc.InflateRect(-GRIP_SIZE / 2, -GRIP_SIZE / 2);
807
808 // Draw the text
809 INT style = (toolsModel.IsBackgroundTransparent() ? 0 : 1);
810 Text(hdc, rc.left, rc.top, rc.right, rc.bottom, m_fg, m_bg, szText,
811 textEditWindow.GetFont(), style);
812 }
813
quitTextTool814 void quit()
815 {
816 if (textEditWindow.IsWindow())
817 textEditWindow.ShowWindow(SW_HIDE);
818 selectionModel.HideSelection();
819 }
820
OnButtonUpTextTool821 BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override
822 {
823 POINT pt = { x, y };
824 imageModel.Clamp(pt);
825 selectionModel.SetRectFromPoints(g_ptStart, pt);
826
827 BOOL bTextBoxShown = ::IsWindowVisible(textEditWindow);
828 if (bTextBoxShown)
829 {
830 if (textEditWindow.GetWindowTextLength() > 0)
831 {
832 imageModel.PushImageForUndo();
833 draw(m_hdc);
834 }
835 if (selectionModel.m_rc.IsRectEmpty())
836 {
837 quit();
838 return TRUE;
839 }
840 }
841
842 if (registrySettings.ShowTextTool)
843 {
844 if (!fontsDialog.IsWindow())
845 fontsDialog.Create(mainWindow);
846
847 fontsDialog.ShowWindow(SW_SHOWNOACTIVATE);
848 }
849
850 CRect rc = selectionModel.m_rc;
851
852 // Enlarge if tool small
853 INT cxMin = CX_MINTEXTEDIT, cyMin = CY_MINTEXTEDIT;
854 if (selectionModel.m_rc.IsRectEmpty())
855 {
856 rc.SetRect(x, y, x + cxMin, y + cyMin);
857 }
858 else
859 {
860 if (rc.right - rc.left < cxMin)
861 rc.right = rc.left + cxMin;
862 if (rc.bottom - rc.top < cyMin)
863 rc.bottom = rc.top + cyMin;
864 }
865
866 if (!textEditWindow.IsWindow())
867 textEditWindow.Create(canvasWindow);
868
869 textEditWindow.SetWindowText(NULL);
870 textEditWindow.ValidateEditRect(&rc);
871 textEditWindow.ShowWindow(SW_SHOWNOACTIVATE);
872 textEditWindow.SetFocus();
873 return TRUE;
874 }
875
OnEndDrawTextTool876 void OnEndDraw(BOOL bCancel) override
877 {
878 if (!bCancel)
879 {
880 if (::IsWindowVisible(textEditWindow) &&
881 textEditWindow.GetWindowTextLength() > 0)
882 {
883 imageModel.PushImageForUndo();
884 draw(m_hdc);
885 }
886 }
887 quit();
888 ToolBase::OnEndDraw(bCancel);
889 }
890 };
891
892 // TOOL_LINE
893 struct LineTool : TwoPointDrawTool
894 {
OnDrawOverlayOnImageLineTool895 void OnDrawOverlayOnImage(HDC hdc) override
896 {
897 if (!m_bDrawing)
898 return;
899 if (GetAsyncKeyState(VK_SHIFT) < 0)
900 roundTo8Directions(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y);
901 COLORREF rgb = m_bLeftButton ? m_fg : m_bg;
902 Line(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, rgb, toolsModel.GetLineWidth());
903 }
904 };
905
906 // TOOL_BEZIER
907 struct BezierTool : ToolBase
908 {
909 BOOL m_bLeftButton = FALSE;
910
OnDrawOverlayOnImageBezierTool911 void OnDrawOverlayOnImage(HDC hdc)
912 {
913 COLORREF rgb = (m_bLeftButton ? m_fg : m_bg);
914 switch (s_cPoints)
915 {
916 case 2:
917 Line(hdc, s_pPoints[0].x, s_pPoints[0].y, s_pPoints[1].x, s_pPoints[1].y, rgb,
918 toolsModel.GetLineWidth());
919 break;
920 case 3:
921 Bezier(hdc, s_pPoints[0], s_pPoints[2], s_pPoints[2], s_pPoints[1], rgb, toolsModel.GetLineWidth());
922 break;
923 case 4:
924 Bezier(hdc, s_pPoints[0], s_pPoints[2], s_pPoints[3], s_pPoints[1], rgb, toolsModel.GetLineWidth());
925 break;
926 }
927 }
928
OnButtonDownBezierTool929 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
930 {
931 m_bLeftButton = bLeftButton;
932
933 if (s_cPoints == 0)
934 {
935 pushToPoints(x, y);
936 pushToPoints(x, y);
937 }
938 else
939 {
940 s_pPoints[s_cPoints - 1] = { x, y };
941 }
942
943 imageModel.NotifyImageChanged();
944 }
945
OnMouseMoveBezierTool946 BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override
947 {
948 if (s_cPoints > 0)
949 s_pPoints[s_cPoints - 1] = { x, y };
950 imageModel.NotifyImageChanged();
951 return TRUE;
952 }
953
OnButtonUpBezierTool954 BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override
955 {
956 if (s_cPoints >= 4)
957 {
958 OnEndDraw(FALSE);
959 return TRUE;
960 }
961 pushToPoints(x, y);
962 imageModel.NotifyImageChanged();
963 return TRUE;
964 }
965
OnEndDrawBezierTool966 void OnEndDraw(BOOL bCancel) override
967 {
968 if (!bCancel && s_cPoints > 1)
969 {
970 // FIXME: I couldn't calculate boundary rectangle from Bezier curve
971 imageModel.PushImageForUndo();
972 OnDrawOverlayOnImage(m_hdc);
973 }
974 ToolBase::OnEndDraw(bCancel);
975 }
976
OnSpecialTweakBezierTool977 void OnSpecialTweak(BOOL bMinus) override
978 {
979 toolsModel.MakeLineThickerOrThinner(bMinus);
980 }
981 };
982
983 // TOOL_RECT
984 struct RectTool : TwoPointDrawTool
985 {
OnDrawOverlayOnImageRectTool986 void OnDrawOverlayOnImage(HDC hdc) override
987 {
988 if (!m_bDrawing)
989 return;
990 if (GetAsyncKeyState(VK_SHIFT) < 0)
991 regularize(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y);
992 if (m_bLeftButton)
993 Rect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
994 else
995 Rect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
996 }
997 };
998
999 // TOOL_SHAPE
1000 struct ShapeTool : ToolBase
1001 {
1002 BOOL m_bLeftButton = FALSE;
1003 BOOL m_bClosed = FALSE;
1004
OnDrawOverlayOnImageShapeTool1005 void OnDrawOverlayOnImage(HDC hdc)
1006 {
1007 if (s_cPoints <= 0)
1008 return;
1009
1010 if (m_bLeftButton)
1011 Poly(hdc, s_pPoints, (INT)s_cPoints, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), m_bClosed, FALSE);
1012 else
1013 Poly(hdc, s_pPoints, (INT)s_cPoints, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), m_bClosed, FALSE);
1014 }
1015
OnButtonDownShapeTool1016 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
1017 {
1018 m_bLeftButton = bLeftButton;
1019 m_bClosed = FALSE;
1020
1021 if ((s_cPoints > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
1022 roundTo8Directions(s_pPoints[s_cPoints - 1].x, s_pPoints[s_cPoints - 1].y, x, y);
1023
1024 pushToPoints(x, y);
1025
1026 if (s_cPoints > 1 && bDoubleClick)
1027 {
1028 OnEndDraw(FALSE);
1029 return;
1030 }
1031
1032 if (s_cPoints == 1)
1033 pushToPoints(x, y); // We have to draw the first point
1034
1035 imageModel.NotifyImageChanged();
1036 }
1037
OnMouseMoveShapeTool1038 BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override
1039 {
1040 if (s_cPoints > 1)
1041 {
1042 if (GetAsyncKeyState(VK_SHIFT) < 0)
1043 roundTo8Directions(s_pPoints[s_cPoints - 2].x, s_pPoints[s_cPoints - 2].y, x, y);
1044
1045 s_pPoints[s_cPoints - 1] = { x, y };
1046 }
1047
1048 imageModel.NotifyImageChanged();
1049 return TRUE;
1050 }
1051
OnButtonUpShapeTool1052 BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override
1053 {
1054 if ((s_cPoints > 1) && (GetAsyncKeyState(VK_SHIFT) < 0))
1055 roundTo8Directions(s_pPoints[s_cPoints - 2].x, s_pPoints[s_cPoints - 2].y, x, y);
1056
1057 m_bClosed = FALSE;
1058 if (nearlyEqualPoints(x, y, s_pPoints[0].x, s_pPoints[0].y))
1059 {
1060 OnEndDraw(FALSE);
1061 return TRUE;
1062 }
1063
1064 pushToPoints(x, y);
1065 imageModel.NotifyImageChanged();
1066 return TRUE;
1067 }
1068
OnEndDrawShapeTool1069 void OnEndDraw(BOOL bCancel) override
1070 {
1071 if (!bCancel && s_cPoints > 1)
1072 {
1073 CRect rcPartial;
1074 getBoundaryOfPoints(rcPartial, s_cPoints, s_pPoints);
1075
1076 SIZE size = toolsModel.GetToolSize();
1077 rcPartial.InflateRect((size.cx + 1) / 2, (size.cy + 1) / 2);
1078
1079 imageModel.PushImageForUndo(rcPartial);
1080
1081 m_bClosed = TRUE;
1082 OnDrawOverlayOnImage(m_hdc);
1083 }
1084 m_bClosed = FALSE;
1085 ToolBase::OnEndDraw(bCancel);
1086 }
1087
OnSpecialTweakShapeTool1088 void OnSpecialTweak(BOOL bMinus) override
1089 {
1090 toolsModel.MakeLineThickerOrThinner(bMinus);
1091 }
1092 };
1093
1094 // TOOL_ELLIPSE
1095 struct EllipseTool : TwoPointDrawTool
1096 {
OnDrawOverlayOnImageEllipseTool1097 void OnDrawOverlayOnImage(HDC hdc) override
1098 {
1099 if (!m_bDrawing)
1100 return;
1101 if (GetAsyncKeyState(VK_SHIFT) < 0)
1102 regularize(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y);
1103 if (m_bLeftButton)
1104 Ellp(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
1105 else
1106 Ellp(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
1107 }
1108 };
1109
1110 // TOOL_RRECT
1111 struct RRectTool : TwoPointDrawTool
1112 {
OnDrawOverlayOnImageRRectTool1113 void OnDrawOverlayOnImage(HDC hdc) override
1114 {
1115 if (!m_bDrawing)
1116 return;
1117 if (GetAsyncKeyState(VK_SHIFT) < 0)
1118 regularize(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y);
1119 if (m_bLeftButton)
1120 RRect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
1121 else
1122 RRect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
1123 }
1124 };
1125
1126 /*static*/ ToolBase*
createToolObject(TOOLTYPE type)1127 ToolBase::createToolObject(TOOLTYPE type)
1128 {
1129 switch (type)
1130 {
1131 case TOOL_FREESEL: return new FreeSelTool();
1132 case TOOL_RECTSEL: return new RectSelTool();
1133 case TOOL_RUBBER: return new RubberTool();
1134 case TOOL_FILL: return new FillTool();
1135 case TOOL_COLOR: return new ColorTool();
1136 case TOOL_ZOOM: return new ZoomTool();
1137 case TOOL_PEN: return new PenTool();
1138 case TOOL_BRUSH: return new BrushTool();
1139 case TOOL_AIRBRUSH: return new AirBrushTool();
1140 case TOOL_TEXT: return new TextTool();
1141 case TOOL_LINE: return new LineTool();
1142 case TOOL_BEZIER: return new BezierTool();
1143 case TOOL_RECT: return new RectTool();
1144 case TOOL_SHAPE: return new ShapeTool();
1145 case TOOL_ELLIPSE: return new EllipseTool();
1146 case TOOL_RRECT: return new RRectTool();
1147 }
1148 UNREACHABLE;
1149 return NULL;
1150 }
1151
OnButtonDown(BOOL bLeftButton,LONG x,LONG y,BOOL bDoubleClick)1152 void ToolsModel::OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick)
1153 {
1154 m_pToolObject->beginEvent();
1155 g_ptEnd = g_ptStart = { x, y };
1156 m_pToolObject->OnButtonDown(bLeftButton, x, y, bDoubleClick);
1157 m_pToolObject->endEvent();
1158 }
1159
OnMouseMove(BOOL bLeftButton,LONG x,LONG y)1160 void ToolsModel::OnMouseMove(BOOL bLeftButton, LONG x, LONG y)
1161 {
1162 m_pToolObject->beginEvent();
1163 if (m_pToolObject->OnMouseMove(bLeftButton, x, y))
1164 g_ptEnd = { x, y };
1165
1166 m_pToolObject->endEvent();
1167 }
1168
OnButtonUp(BOOL bLeftButton,LONG x,LONG y)1169 void ToolsModel::OnButtonUp(BOOL bLeftButton, LONG x, LONG y)
1170 {
1171 m_pToolObject->beginEvent();
1172 if (m_pToolObject->OnButtonUp(bLeftButton, x, y))
1173 g_ptEnd = { x, y };
1174
1175 m_pToolObject->endEvent();
1176 }
1177
OnEndDraw(BOOL bCancel)1178 void ToolsModel::OnEndDraw(BOOL bCancel)
1179 {
1180 ATLTRACE("ToolsModel::OnEndDraw(%d)\n", bCancel);
1181 m_pToolObject->beginEvent();
1182 m_pToolObject->OnEndDraw(bCancel);
1183 m_pToolObject->endEvent();
1184 }
1185
OnDrawOverlayOnImage(HDC hdc)1186 void ToolsModel::OnDrawOverlayOnImage(HDC hdc)
1187 {
1188 m_pToolObject->OnDrawOverlayOnImage(hdc);
1189 }
1190
OnDrawOverlayOnCanvas(HDC hdc)1191 void ToolsModel::OnDrawOverlayOnCanvas(HDC hdc)
1192 {
1193 m_pToolObject->OnDrawOverlayOnCanvas(hdc);
1194 }
1195
SpecialTweak(BOOL bMinus)1196 void ToolsModel::SpecialTweak(BOOL bMinus)
1197 {
1198 m_pToolObject->OnSpecialTweak(bMinus);
1199 }
1200
DrawWithMouseTool(POINT pt,WPARAM wParam)1201 void ToolsModel::DrawWithMouseTool(POINT pt, WPARAM wParam)
1202 {
1203 LONG xRel = pt.x - g_ptStart.x, yRel = pt.y - g_ptStart.y;
1204
1205 switch (m_activeTool)
1206 {
1207 // freesel, rectsel and text tools always show numbers limited to fit into image area
1208 case TOOL_FREESEL:
1209 case TOOL_RECTSEL:
1210 case TOOL_TEXT:
1211 if (xRel < 0)
1212 xRel = (pt.x < 0) ? -g_ptStart.x : xRel;
1213 else if (pt.x > imageModel.GetWidth())
1214 xRel = imageModel.GetWidth() - g_ptStart.x;
1215 if (yRel < 0)
1216 yRel = (pt.y < 0) ? -g_ptStart.y : yRel;
1217 else if (pt.y > imageModel.GetHeight())
1218 yRel = imageModel.GetHeight() - g_ptStart.y;
1219 break;
1220
1221 // while drawing, update cursor coordinates only for tools 3, 7, 8, 9, 14
1222 case TOOL_RUBBER:
1223 case TOOL_PEN:
1224 case TOOL_BRUSH:
1225 case TOOL_AIRBRUSH:
1226 case TOOL_SHAPE:
1227 {
1228 CStringW strCoord;
1229 strCoord.Format(L"%ld, %ld", pt.x, pt.y);
1230 ::SendMessageW(g_hStatusBar, SB_SETTEXT, 1, (LPARAM)(LPCWSTR)strCoord);
1231 break;
1232 }
1233 default:
1234 break;
1235 }
1236
1237 // rectsel and shape tools always show non-negative numbers when drawing
1238 if (m_activeTool == TOOL_RECTSEL || m_activeTool == TOOL_SHAPE)
1239 {
1240 xRel = labs(xRel);
1241 yRel = labs(yRel);
1242 }
1243
1244 if (wParam & MK_LBUTTON)
1245 {
1246 OnMouseMove(TRUE, pt.x, pt.y);
1247 canvasWindow.Invalidate(FALSE);
1248 if ((m_activeTool >= TOOL_TEXT) || IsSelection())
1249 {
1250 CStringW strSize;
1251 if ((m_activeTool >= TOOL_LINE) && (GetAsyncKeyState(VK_SHIFT) < 0))
1252 yRel = xRel;
1253 strSize.Format(L"%ld x %ld", xRel, yRel);
1254 ::SendMessageW(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)(LPCWSTR)strSize);
1255 }
1256 }
1257
1258 if (wParam & MK_RBUTTON)
1259 {
1260 OnMouseMove(FALSE, pt.x, pt.y);
1261 canvasWindow.Invalidate(FALSE);
1262 if (m_activeTool >= TOOL_TEXT)
1263 {
1264 CStringW strSize;
1265 if ((m_activeTool >= TOOL_LINE) && (GetAsyncKeyState(VK_SHIFT) < 0))
1266 yRel = xRel;
1267 strSize.Format(L"%ld x %ld", xRel, yRel);
1268 ::SendMessageW(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)(LPCWSTR)strSize);
1269 }
1270 }
1271 }
1272