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