xref: /reactos/base/applications/mspaint/mouse.cpp (revision 2ea03b5b)
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 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
7  */
8 
9 /* INCLUDES *********************************************************/
10 
11 #include "precomp.h"
12 
13 INT ToolBase::s_pointSP = 0;
14 POINT ToolBase::s_pointStack[256] = { { 0 } };
15 
16 /* FUNCTIONS ********************************************************/
17 
18 void
19 regularize(LONG x0, LONG y0, LONG& x1, LONG& y1)
20 {
21     if (abs(x1 - x0) >= abs(y1 - y0))
22         y1 = y0 + (y1 > y0 ? abs(x1 - x0) : -abs(x1 - x0));
23     else
24         x1 = x0 + (x1 > x0 ? abs(y1 - y0) : -abs(y1 - y0));
25 }
26 
27 void
28 roundTo8Directions(LONG x0, LONG y0, LONG& x1, LONG& y1)
29 {
30     if (abs(x1 - x0) >= abs(y1 - y0))
31     {
32         if (abs(y1 - y0) * 5 < abs(x1 - x0) * 2)
33             y1 = y0;
34         else
35             y1 = y0 + (y1 > y0 ? abs(x1 - x0) : -abs(x1 - x0));
36     }
37     else
38     {
39         if (abs(x1 - x0) * 5 < abs(y1 - y0) * 2)
40             x1 = x0;
41         else
42             x1 = x0 + (x1 > x0 ? abs(y1 - y0) : -abs(y1 - y0));
43     }
44 }
45 
46 BOOL nearlyEqualPoints(INT x0, INT y0, INT x1, INT y1)
47 {
48     INT cxThreshold = toolsModel.GetLineWidth() + UnZoomed(GetSystemMetrics(SM_CXDRAG));
49     INT cyThreshold = toolsModel.GetLineWidth() + UnZoomed(GetSystemMetrics(SM_CYDRAG));
50     return (abs(x1 - x0) <= cxThreshold) && (abs(y1 - y0) <= cyThreshold);
51 }
52 
53 void updateStartAndLast(LONG x, LONG y)
54 {
55     g_ptStart.x = g_ptEnd.x = x;
56     g_ptStart.y = g_ptEnd.y = y;
57 }
58 
59 void updateLast(LONG x, LONG y)
60 {
61     g_ptEnd.x = x;
62     g_ptEnd.y = y;
63 }
64 
65 void ToolBase::reset()
66 {
67     s_pointSP = 0;
68     g_ptStart.x = g_ptStart.y = g_ptEnd.x = g_ptEnd.y = -1;
69     selectionModel.ResetPtStack();
70     if (selectionModel.m_bShow)
71     {
72         selectionModel.Landing();
73         selectionModel.HideSelection();
74     }
75 }
76 
77 void ToolBase::OnCancelDraw()
78 {
79     reset();
80     imageModel.NotifyImageChanged();
81 }
82 
83 void ToolBase::OnFinishDraw()
84 {
85     reset();
86     imageModel.NotifyImageChanged();
87 }
88 
89 void ToolBase::beginEvent()
90 {
91     m_hdc = imageModel.GetDC();
92     m_fg = paletteModel.GetFgColor();
93     m_bg = paletteModel.GetBgColor();
94 }
95 
96 void ToolBase::endEvent()
97 {
98     m_hdc = NULL;
99 }
100 
101 void ToolBase::OnDrawSelectionOnCanvas(HDC hdc)
102 {
103     if (!selectionModel.m_bShow)
104         return;
105 
106     RECT rcSelection = selectionModel.m_rc;
107     canvasWindow.ImageToCanvas(rcSelection);
108 
109     ::InflateRect(&rcSelection, GRIP_SIZE, GRIP_SIZE);
110     drawSizeBoxes(hdc, &rcSelection, TRUE);
111 }
112 
113 /* TOOLS ********************************************************/
114 
115 // TOOL_FREESEL
116 struct FreeSelTool : ToolBase
117 {
118     BOOL m_bLeftButton = FALSE;
119 
120     FreeSelTool() : ToolBase(TOOL_FREESEL)
121     {
122     }
123 
124     void OnDrawOverlayOnImage(HDC hdc) override
125     {
126         if (!selectionModel.IsLanded())
127         {
128             selectionModel.DrawBackgroundPoly(hdc, selectionModel.m_rgbBack);
129             selectionModel.DrawSelection(hdc, paletteModel.GetBgColor(), toolsModel.IsBackgroundTransparent());
130         }
131 
132         if (canvasWindow.m_drawing)
133         {
134             selectionModel.DrawFramePoly(hdc);
135         }
136     }
137 
138     void OnDrawOverlayOnCanvas(HDC hdc) override
139     {
140         OnDrawSelectionOnCanvas(hdc);
141     }
142 
143     void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
144     {
145         selectionModel.Landing();
146         if (bLeftButton)
147         {
148             selectionModel.HideSelection();
149             selectionModel.ResetPtStack();
150             POINT pt = { x, y };
151             selectionModel.PushToPtStack(pt);
152         }
153         m_bLeftButton = bLeftButton;
154     }
155 
156     void OnMouseMove(BOOL bLeftButton, LONG x, LONG y) override
157     {
158         if (bLeftButton)
159         {
160             POINT pt = { x, y };
161             imageModel.Clamp(pt);
162             selectionModel.PushToPtStack(pt);
163             imageModel.NotifyImageChanged();
164         }
165     }
166 
167     void OnButtonUp(BOOL bLeftButton, LONG x, LONG y) override
168     {
169         if (bLeftButton)
170         {
171             if (selectionModel.PtStackSize() > 2)
172             {
173                 selectionModel.BuildMaskFromPtStack();
174                 selectionModel.m_bShow = TRUE;
175             }
176             else
177             {
178                 selectionModel.ResetPtStack();
179                 selectionModel.m_bShow = FALSE;
180             }
181             imageModel.NotifyImageChanged();
182         }
183         else
184         {
185             POINT pt = { x, y };
186             canvasWindow.ClientToScreen(&pt);
187             mainWindow.TrackPopupMenu(pt, 0);
188         }
189     }
190 
191     void OnFinishDraw() override
192     {
193         selectionModel.Landing();
194         ToolBase::OnFinishDraw();
195     }
196 
197     void OnCancelDraw() override
198     {
199         selectionModel.HideSelection();
200         ToolBase::OnCancelDraw();
201     }
202 };
203 
204 // TOOL_RECTSEL
205 struct RectSelTool : ToolBase
206 {
207     BOOL m_bLeftButton = FALSE;
208 
209     RectSelTool() : ToolBase(TOOL_RECTSEL)
210     {
211     }
212 
213     void OnDrawOverlayOnImage(HDC hdc) override
214     {
215         if (!selectionModel.IsLanded())
216         {
217             selectionModel.DrawBackgroundRect(hdc, selectionModel.m_rgbBack);
218             selectionModel.DrawSelection(hdc, paletteModel.GetBgColor(), toolsModel.IsBackgroundTransparent());
219         }
220 
221         if (canvasWindow.m_drawing)
222         {
223             RECT rc = selectionModel.m_rc;
224             if (!::IsRectEmpty(&rc))
225                 RectSel(hdc, rc.left, rc.top, rc.right, rc.bottom);
226         }
227     }
228 
229     void OnDrawOverlayOnCanvas(HDC hdc) override
230     {
231         OnDrawSelectionOnCanvas(hdc);
232     }
233 
234     void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
235     {
236         selectionModel.Landing();
237         if (bLeftButton)
238         {
239             selectionModel.HideSelection();
240         }
241         m_bLeftButton = bLeftButton;
242     }
243 
244     void OnMouseMove(BOOL bLeftButton, LONG x, LONG y) override
245     {
246         if (bLeftButton)
247         {
248             POINT pt = { x, y };
249             imageModel.Clamp(pt);
250             selectionModel.SetRectFromPoints(g_ptStart, pt);
251             imageModel.NotifyImageChanged();
252         }
253     }
254 
255     void OnButtonUp(BOOL bLeftButton, LONG x, LONG y) override
256     {
257         POINT pt = { x, y };
258         if (bLeftButton)
259         {
260             imageModel.Clamp(pt);
261             selectionModel.SetRectFromPoints(g_ptStart, pt);
262             selectionModel.m_bShow = !selectionModel.m_rc.IsRectEmpty();
263             imageModel.NotifyImageChanged();
264         }
265         else
266         {
267             canvasWindow.ClientToScreen(&pt);
268             mainWindow.TrackPopupMenu(pt, 0);
269         }
270     }
271 
272     void OnFinishDraw() override
273     {
274         selectionModel.Landing();
275         ToolBase::OnFinishDraw();
276     }
277 
278     void OnCancelDraw() override
279     {
280         selectionModel.HideSelection();
281         ToolBase::OnCancelDraw();
282     }
283 };
284 
285 struct TwoPointDrawTool : ToolBase
286 {
287     BOOL m_bLeftButton = FALSE;
288     BOOL m_bDrawing = FALSE;
289 
290     TwoPointDrawTool(TOOLTYPE type) : ToolBase(type)
291     {
292     }
293 
294     void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
295     {
296         m_bLeftButton = bLeftButton;
297         m_bDrawing = TRUE;
298         g_ptStart.x = g_ptEnd.x = x;
299         g_ptStart.y = g_ptEnd.y = y;
300         imageModel.NotifyImageChanged();
301     }
302 
303     void OnMouseMove(BOOL bLeftButton, LONG x, LONG y) override
304     {
305         g_ptEnd.x = x;
306         g_ptEnd.y = y;
307         imageModel.NotifyImageChanged();
308     }
309 
310     void OnButtonUp(BOOL bLeftButton, LONG x, LONG y) override
311     {
312         g_ptEnd.x = x;
313         g_ptEnd.y = y;
314         imageModel.PushImageForUndo();
315         OnDrawOverlayOnImage(m_hdc);
316         m_bDrawing = FALSE;
317         imageModel.NotifyImageChanged();
318     }
319 
320     void OnFinishDraw() override
321     {
322         m_bDrawing = FALSE;
323         ToolBase::OnFinishDraw();
324     }
325 
326     void OnCancelDraw() override
327     {
328         m_bDrawing = FALSE;
329         ToolBase::OnCancelDraw();
330     }
331 };
332 
333 struct SmoothDrawTool : ToolBase
334 {
335     SmoothDrawTool(TOOLTYPE type) : ToolBase(type)
336     {
337     }
338 
339     virtual void draw(BOOL bLeftButton, LONG x, LONG y) = 0;
340 
341     void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
342     {
343         imageModel.PushImageForUndo();
344         g_ptStart.x = g_ptEnd.x = x;
345         g_ptStart.y = g_ptEnd.y = y;
346         imageModel.NotifyImageChanged();
347     }
348 
349     void OnMouseMove(BOOL bLeftButton, LONG x, LONG y) override
350     {
351         draw(bLeftButton, x, y);
352         imageModel.NotifyImageChanged();
353     }
354 
355     void OnButtonUp(BOOL bLeftButton, LONG x, LONG y) override
356     {
357         draw(bLeftButton, x, y);
358         OnFinishDraw();
359     }
360 
361     void OnFinishDraw() override
362     {
363         ToolBase::OnFinishDraw();
364     }
365 
366     void OnCancelDraw() override
367     {
368         OnButtonUp(FALSE, 0, 0);
369         imageModel.Undo(TRUE);
370         ToolBase::OnCancelDraw();
371     }
372 };
373 
374 // TOOL_RUBBER
375 struct RubberTool : SmoothDrawTool
376 {
377     RubberTool() : SmoothDrawTool(TOOL_RUBBER)
378     {
379     }
380 
381     void draw(BOOL bLeftButton, LONG x, LONG y) override
382     {
383         if (bLeftButton)
384             Erase(m_hdc, g_ptEnd.x, g_ptEnd.y, x, y, m_bg, toolsModel.GetRubberRadius());
385         else
386             Replace(m_hdc, g_ptEnd.x, g_ptEnd.y, x, y, m_fg, m_bg, toolsModel.GetRubberRadius());
387         g_ptEnd.x = x;
388         g_ptEnd.y = y;
389     }
390 };
391 
392 // TOOL_FILL
393 struct FillTool : ToolBase
394 {
395     FillTool() : ToolBase(TOOL_FILL)
396     {
397     }
398 
399     void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
400     {
401         imageModel.PushImageForUndo();
402         Fill(m_hdc, x, y, bLeftButton ? m_fg : m_bg);
403     }
404 };
405 
406 // TOOL_COLOR
407 struct ColorTool : ToolBase
408 {
409     ColorTool() : ToolBase(TOOL_COLOR)
410     {
411     }
412 
413     void fetchColor(BOOL bLeftButton, LONG x, LONG y)
414     {
415         COLORREF rgbColor;
416 
417         if (0 <= x && x < imageModel.GetWidth() && 0 <= y && y < imageModel.GetHeight())
418             rgbColor = GetPixel(m_hdc, x, y);
419         else
420             rgbColor = RGB(255, 255, 255); // Outside is white
421 
422         if (bLeftButton)
423             paletteModel.SetFgColor(rgbColor);
424         else
425             paletteModel.SetBgColor(rgbColor);
426     }
427 
428     void OnMouseMove(BOOL bLeftButton, LONG x, LONG y) override
429     {
430         fetchColor(bLeftButton, x, y);
431     }
432 
433     void OnButtonUp(BOOL bLeftButton, LONG x, LONG y) override
434     {
435         fetchColor(bLeftButton, x, y);
436         toolsModel.SetActiveTool(toolsModel.GetOldActiveTool());
437     }
438 };
439 
440 // TOOL_ZOOM
441 struct ZoomTool : ToolBase
442 {
443     ZoomTool() : ToolBase(TOOL_ZOOM)
444     {
445     }
446 
447     void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
448     {
449         imageModel.PushImageForUndo();
450         if (bLeftButton)
451         {
452             if (toolsModel.GetZoom() < MAX_ZOOM)
453                 zoomTo(toolsModel.GetZoom() * 2, x, y);
454         }
455         else
456         {
457             if (toolsModel.GetZoom() > MIN_ZOOM)
458                 zoomTo(toolsModel.GetZoom() / 2, x, y);
459         }
460     }
461 };
462 
463 // TOOL_PEN
464 struct PenTool : SmoothDrawTool
465 {
466     PenTool() : SmoothDrawTool(TOOL_PEN)
467     {
468     }
469 
470     void draw(BOOL bLeftButton, LONG x, LONG y) override
471     {
472         COLORREF rgb = bLeftButton ? m_fg : m_bg;
473         Line(m_hdc, g_ptEnd.x, g_ptEnd.y, x, y, rgb, 1);
474         ::SetPixelV(m_hdc, x, y, rgb);
475         g_ptEnd.x = x;
476         g_ptEnd.y = y;
477     }
478 };
479 
480 // TOOL_BRUSH
481 struct BrushTool : SmoothDrawTool
482 {
483     BrushTool() : SmoothDrawTool(TOOL_BRUSH)
484     {
485     }
486 
487     void draw(BOOL bLeftButton, LONG x, LONG y) override
488     {
489         COLORREF rgb = bLeftButton ? m_fg : m_bg;
490         Brush(m_hdc, g_ptEnd.x, g_ptEnd.y, x, y, rgb, toolsModel.GetBrushStyle());
491         g_ptEnd.x = x;
492         g_ptEnd.y = y;
493     }
494 };
495 
496 // TOOL_AIRBRUSH
497 struct AirBrushTool : SmoothDrawTool
498 {
499     AirBrushTool() : SmoothDrawTool(TOOL_AIRBRUSH)
500     {
501     }
502 
503     void draw(BOOL bLeftButton, LONG x, LONG y) override
504     {
505         COLORREF rgb = bLeftButton ? m_fg : m_bg;
506         Airbrush(m_hdc, x, y, rgb, toolsModel.GetAirBrushWidth());
507     }
508 };
509 
510 // TOOL_TEXT
511 struct TextTool : ToolBase
512 {
513     TextTool() : ToolBase(TOOL_TEXT)
514     {
515     }
516 
517     void OnDrawOverlayOnImage(HDC hdc) override
518     {
519         if (canvasWindow.m_drawing)
520         {
521             RECT rc = selectionModel.m_rc;
522             if (!::IsRectEmpty(&rc))
523                 RectSel(hdc, rc.left, rc.top, rc.right, rc.bottom);
524         }
525     }
526 
527     void UpdatePoint(LONG x, LONG y)
528     {
529         POINT pt = { x, y };
530         imageModel.Clamp(pt);
531         selectionModel.SetRectFromPoints(g_ptStart, pt);
532         imageModel.NotifyImageChanged();
533     }
534 
535     void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
536     {
537         if (!textEditWindow.IsWindow())
538             textEditWindow.Create(canvasWindow);
539 
540         UpdatePoint(x, y);
541     }
542 
543     void OnMouseMove(BOOL bLeftButton, LONG x, LONG y) override
544     {
545         UpdatePoint(x, y);
546     }
547 
548     void draw(HDC hdc)
549     {
550         CString szText;
551         textEditWindow.GetWindowText(szText);
552 
553         RECT rc;
554         textEditWindow.InvalidateEditRect();
555         textEditWindow.GetEditRect(&rc);
556         ::InflateRect(&rc, -GRIP_SIZE / 2, -GRIP_SIZE / 2);
557 
558         // Draw the text
559         INT style = (toolsModel.IsBackgroundTransparent() ? 0 : 1);
560         Text(hdc, rc.left, rc.top, rc.right, rc.bottom, m_fg, m_bg, szText,
561              textEditWindow.GetFont(), style);
562     }
563 
564     void quit()
565     {
566         if (textEditWindow.IsWindow())
567             textEditWindow.ShowWindow(SW_HIDE);
568         selectionModel.HideSelection();
569     }
570 
571     void OnButtonUp(BOOL bLeftButton, LONG x, LONG y) override
572     {
573         POINT pt = { x, y };
574         imageModel.Clamp(pt);
575         selectionModel.SetRectFromPoints(g_ptStart, pt);
576 
577         BOOL bTextBoxShown = ::IsWindowVisible(textEditWindow);
578         if (bTextBoxShown)
579         {
580             if (textEditWindow.GetWindowTextLength() > 0)
581             {
582                 imageModel.PushImageForUndo();
583                 draw(m_hdc);
584             }
585             if (::IsRectEmpty(&selectionModel.m_rc))
586             {
587                 quit();
588                 return;
589             }
590         }
591 
592         if (registrySettings.ShowTextTool)
593         {
594             if (!fontsDialog.IsWindow())
595                 fontsDialog.Create(mainWindow);
596 
597             fontsDialog.ShowWindow(SW_SHOWNOACTIVATE);
598         }
599 
600         RECT rc = selectionModel.m_rc;
601 
602         // Enlarge if tool small
603         INT cxMin = CX_MINTEXTEDIT, cyMin = CY_MINTEXTEDIT;
604         if (selectionModel.m_rc.IsRectEmpty())
605         {
606             SetRect(&rc, x, y, x + cxMin, y + cyMin);
607         }
608         else
609         {
610             if (rc.right - rc.left < cxMin)
611                 rc.right = rc.left + cxMin;
612             if (rc.bottom - rc.top < cyMin)
613                 rc.bottom = rc.top + cyMin;
614         }
615 
616         if (!textEditWindow.IsWindow())
617             textEditWindow.Create(canvasWindow);
618 
619         textEditWindow.SetWindowText(NULL);
620         textEditWindow.ValidateEditRect(&rc);
621         textEditWindow.ShowWindow(SW_SHOWNOACTIVATE);
622         textEditWindow.SetFocus();
623     }
624 
625     void OnFinishDraw() override
626     {
627         if (textEditWindow.GetWindowTextLength() > 0)
628         {
629             imageModel.PushImageForUndo();
630             draw(m_hdc);
631         }
632         quit();
633         ToolBase::OnFinishDraw();
634     }
635 
636     void OnCancelDraw() override
637     {
638         quit();
639         ToolBase::OnCancelDraw();
640     }
641 };
642 
643 // TOOL_LINE
644 struct LineTool : TwoPointDrawTool
645 {
646     LineTool() : TwoPointDrawTool(TOOL_LINE)
647     {
648     }
649 
650     void OnDrawOverlayOnImage(HDC hdc) override
651     {
652         if (!m_bDrawing)
653             return;
654         if (GetAsyncKeyState(VK_SHIFT) < 0)
655             roundTo8Directions(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y);
656         COLORREF rgb = m_bLeftButton ? m_fg : m_bg;
657         Line(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, rgb, toolsModel.GetLineWidth());
658     }
659 };
660 
661 // TOOL_BEZIER
662 struct BezierTool : ToolBase
663 {
664     BOOL m_bLeftButton = FALSE;
665     BOOL m_bDrawing = FALSE;
666 
667     BezierTool() : ToolBase(TOOL_BEZIER)
668     {
669     }
670 
671     void OnDrawOverlayOnImage(HDC hdc)
672     {
673         if (!m_bDrawing)
674             return;
675 
676         COLORREF rgb = (m_bLeftButton ? m_fg : m_bg);
677         switch (s_pointSP)
678         {
679             case 1:
680                 Line(hdc, s_pointStack[0].x, s_pointStack[0].y, s_pointStack[1].x, s_pointStack[1].y, rgb,
681                      toolsModel.GetLineWidth());
682                 break;
683             case 2:
684                 Bezier(hdc, s_pointStack[0], s_pointStack[2], s_pointStack[2], s_pointStack[1], rgb, toolsModel.GetLineWidth());
685                 break;
686             case 3:
687                 Bezier(hdc, s_pointStack[0], s_pointStack[2], s_pointStack[3], s_pointStack[1], rgb, toolsModel.GetLineWidth());
688                 break;
689         }
690     }
691 
692     void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
693     {
694         m_bLeftButton = bLeftButton;
695 
696         if (!m_bDrawing)
697         {
698             m_bDrawing = TRUE;
699             s_pointStack[s_pointSP].x = s_pointStack[s_pointSP + 1].x = x;
700             s_pointStack[s_pointSP].y = s_pointStack[s_pointSP + 1].y = y;
701             ++s_pointSP;
702         }
703         else
704         {
705             ++s_pointSP;
706             s_pointStack[s_pointSP].x = x;
707             s_pointStack[s_pointSP].y = y;
708         }
709 
710         imageModel.NotifyImageChanged();
711     }
712 
713     void OnMouseMove(BOOL bLeftButton, LONG x, LONG y) override
714     {
715         s_pointStack[s_pointSP].x = x;
716         s_pointStack[s_pointSP].y = y;
717         imageModel.NotifyImageChanged();
718     }
719 
720     void OnButtonUp(BOOL bLeftButton, LONG x, LONG y) override
721     {
722         s_pointStack[s_pointSP].x = x;
723         s_pointStack[s_pointSP].y = y;
724         if (s_pointSP >= 3)
725         {
726             OnFinishDraw();
727             return;
728         }
729         imageModel.NotifyImageChanged();
730     }
731 
732     void OnCancelDraw() override
733     {
734         m_bDrawing = FALSE;
735         ToolBase::OnCancelDraw();
736     }
737 
738     void OnFinishDraw() override
739     {
740         imageModel.PushImageForUndo();
741         OnDrawOverlayOnImage(m_hdc);
742         m_bDrawing = FALSE;
743         ToolBase::OnFinishDraw();
744     }
745 };
746 
747 // TOOL_RECT
748 struct RectTool : TwoPointDrawTool
749 {
750     RectTool() : TwoPointDrawTool(TOOL_RECT)
751     {
752     }
753 
754     void OnDrawOverlayOnImage(HDC hdc) override
755     {
756         if (!m_bDrawing)
757             return;
758         if (GetAsyncKeyState(VK_SHIFT) < 0)
759             regularize(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y);
760         if (m_bLeftButton)
761             Rect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
762         else
763             Rect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
764     }
765 };
766 
767 // TOOL_SHAPE
768 struct ShapeTool : ToolBase
769 {
770     BOOL m_bLeftButton = FALSE;
771     BOOL m_bClosed = FALSE;
772 
773     ShapeTool() : ToolBase(TOOL_SHAPE)
774     {
775     }
776 
777     void OnDrawOverlayOnImage(HDC hdc)
778     {
779         if (s_pointSP <= 0)
780             return;
781 
782         if (m_bLeftButton)
783             Poly(hdc, s_pointStack, s_pointSP + 1, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), m_bClosed, FALSE);
784         else
785             Poly(hdc, s_pointStack, s_pointSP + 1, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), m_bClosed, FALSE);
786     }
787 
788     void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
789     {
790         m_bLeftButton = bLeftButton;
791         m_bClosed = FALSE;
792 
793         if ((s_pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
794             roundTo8Directions(s_pointStack[s_pointSP - 1].x, s_pointStack[s_pointSP - 1].y, x, y);
795 
796         s_pointStack[s_pointSP].x = x;
797         s_pointStack[s_pointSP].y = y;
798 
799         if (s_pointSP && bDoubleClick)
800         {
801             OnFinishDraw();
802             return;
803         }
804 
805         if (s_pointSP == 0)
806         {
807             s_pointSP++;
808             s_pointStack[s_pointSP].x = x;
809             s_pointStack[s_pointSP].y = y;
810         }
811 
812         imageModel.NotifyImageChanged();
813     }
814 
815     void OnMouseMove(BOOL bLeftButton, LONG x, LONG y) override
816     {
817         if ((s_pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
818             roundTo8Directions(s_pointStack[s_pointSP - 1].x, s_pointStack[s_pointSP - 1].y, x, y);
819 
820         s_pointStack[s_pointSP].x = x;
821         s_pointStack[s_pointSP].y = y;
822 
823         imageModel.NotifyImageChanged();
824     }
825 
826     void OnButtonUp(BOOL bLeftButton, LONG x, LONG y) override
827     {
828         if ((s_pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
829             roundTo8Directions(s_pointStack[s_pointSP - 1].x, s_pointStack[s_pointSP - 1].y, x, y);
830 
831         m_bClosed = FALSE;
832         if (nearlyEqualPoints(x, y, s_pointStack[0].x, s_pointStack[0].y))
833         {
834             OnFinishDraw();
835             return;
836         }
837         else
838         {
839             s_pointSP++;
840             s_pointStack[s_pointSP].x = x;
841             s_pointStack[s_pointSP].y = y;
842         }
843 
844         if (s_pointSP == _countof(s_pointStack))
845             s_pointSP--;
846 
847         imageModel.NotifyImageChanged();
848     }
849 
850     void OnCancelDraw() override
851     {
852         ToolBase::OnCancelDraw();
853     }
854 
855     void OnFinishDraw() override
856     {
857         if (s_pointSP)
858         {
859             --s_pointSP;
860             m_bClosed = TRUE;
861 
862             imageModel.PushImageForUndo();
863             OnDrawOverlayOnImage(m_hdc);
864         }
865 
866         m_bClosed = FALSE;
867         s_pointSP = 0;
868 
869         ToolBase::OnFinishDraw();
870     }
871 };
872 
873 // TOOL_ELLIPSE
874 struct EllipseTool : TwoPointDrawTool
875 {
876     EllipseTool() : TwoPointDrawTool(TOOL_ELLIPSE)
877     {
878     }
879 
880     void OnDrawOverlayOnImage(HDC hdc) override
881     {
882         if (!m_bDrawing)
883             return;
884         if (GetAsyncKeyState(VK_SHIFT) < 0)
885             regularize(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y);
886         if (m_bLeftButton)
887             Ellp(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
888         else
889             Ellp(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
890     }
891 };
892 
893 // TOOL_RRECT
894 struct RRectTool : TwoPointDrawTool
895 {
896     RRectTool() : TwoPointDrawTool(TOOL_RRECT)
897     {
898     }
899 
900     void OnDrawOverlayOnImage(HDC hdc) override
901     {
902         if (!m_bDrawing)
903             return;
904         if (GetAsyncKeyState(VK_SHIFT) < 0)
905             regularize(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y);
906         if (m_bLeftButton)
907             RRect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
908         else
909             RRect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
910     }
911 };
912 
913 /*static*/ ToolBase*
914 ToolBase::createToolObject(TOOLTYPE type)
915 {
916     switch (type)
917     {
918         case TOOL_FREESEL:  return new FreeSelTool();
919         case TOOL_RECTSEL:  return new RectSelTool();
920         case TOOL_RUBBER:   return new RubberTool();
921         case TOOL_FILL:     return new FillTool();
922         case TOOL_COLOR:    return new ColorTool();
923         case TOOL_ZOOM:     return new ZoomTool();
924         case TOOL_PEN:      return new PenTool();
925         case TOOL_BRUSH:    return new BrushTool();
926         case TOOL_AIRBRUSH: return new AirBrushTool();
927         case TOOL_TEXT:     return new TextTool();
928         case TOOL_LINE:     return new LineTool();
929         case TOOL_BEZIER:   return new BezierTool();
930         case TOOL_RECT:     return new RectTool();
931         case TOOL_SHAPE:    return new ShapeTool();
932         case TOOL_ELLIPSE:  return new EllipseTool();
933         case TOOL_RRECT:    return new RRectTool();
934     }
935     UNREACHABLE;
936     return NULL;
937 }
938