https://git.reactos.org/?p=reactos.git;a=commitdiff;h=ee132a05bafe93fcc6534071120cb51b0b0fc7ac

commit ee132a05bafe93fcc6534071120cb51b0b0fc7ac
Author:     Katayama Hirofumi MZ <[email protected]>
AuthorDate: Sat Jan 1 22:02:36 2022 +0900
Commit:     GitHub <[email protected]>
CommitDate: Sat Jan 1 22:02:36 2022 +0900

    [MSPAINT] Polymorphism on tools (#4210)
    
    - Introduce polymorphism on tools and mouse actions.
    - Implement double-clicking on TOOL_SHAPE.
    - Fix some bugs about mouse handling.
    CORE-17931
---
 base/applications/mspaint/common.h       |   3 +
 base/applications/mspaint/globalvar.h    |   2 -
 base/applications/mspaint/imgarea.cpp    | 136 ++---
 base/applications/mspaint/imgarea.h      |   8 +
 base/applications/mspaint/mouse.cpp      | 990 +++++++++++++++++--------------
 base/applications/mspaint/mouse.h        |  23 -
 base/applications/mspaint/precomp.h      |   1 -
 base/applications/mspaint/scrollbox.cpp  |  18 +-
 base/applications/mspaint/toolbox.cpp    |  94 ++-
 base/applications/mspaint/toolsmodel.cpp |  82 ++-
 base/applications/mspaint/toolsmodel.h   |  53 ++
 base/applications/mspaint/winproc.cpp    |   7 +-
 12 files changed, 786 insertions(+), 631 deletions(-)

diff --git a/base/applications/mspaint/common.h 
b/base/applications/mspaint/common.h
index 288bba64be4..af55873145f 100644
--- a/base/applications/mspaint/common.h
+++ b/base/applications/mspaint/common.h
@@ -30,6 +30,9 @@
 
 BOOL zoomTo(int newZoom, int mouseX, int mouseY);
 BOOL nearlyEqualPoints(INT x0, INT y0, INT x1, INT y1);
+void placeSelWin(void);
+void updateStartAndLast(LONG x, LONG y);
+void updateLast(LONG x, LONG y);
 
 static inline int Zoomed(int xy)
 {
diff --git a/base/applications/mspaint/globalvar.h 
b/base/applications/mspaint/globalvar.h
index cd427c86b05..a639f620be1 100644
--- a/base/applications/mspaint/globalvar.h
+++ b/base/applications/mspaint/globalvar.h
@@ -106,5 +106,3 @@ extern CStretchSkewDialog stretchSkewDialog;
 
 /* VARIABLES declared in mouse.cpp **********************************/
 
-extern POINT pointStack[256];
-extern short pointSP;
diff --git a/base/applications/mspaint/imgarea.cpp 
b/base/applications/mspaint/imgarea.cpp
index d521b6f065d..69a6de62984 100644
--- a/base/applications/mspaint/imgarea.cpp
+++ b/base/applications/mspaint/imgarea.cpp
@@ -159,41 +159,43 @@ LRESULT CImgAreaWindow::OnSetCursor(UINT nMsg, WPARAM 
wParam, LPARAM lParam, BOO
 
 LRESULT CImgAreaWindow::OnLButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, 
BOOL& bHandled)
 {
-    if ((!drawing) || (toolsModel.GetActiveTool() == TOOL_COLOR))
-    {
-        SetCapture();
-        drawing = TRUE;
-        startPaintingL(imageModel.GetDC(), UnZoomed(GET_X_LPARAM(lParam)), 
UnZoomed(GET_Y_LPARAM(lParam)),
-                       paletteModel.GetFgColor(), paletteModel.GetBgColor());
-    }
-    else
-    {
-        SendMessage(WM_LBUTTONUP, wParam, lParam);
-        imageModel.Undo();
-    }
+    drawing = TRUE;
+    SetCapture();
+    INT x = GET_X_LPARAM(lParam), y = GET_Y_LPARAM(lParam);
+    toolsModel.OnButtonDown(TRUE, UnZoomed(x), UnZoomed(y), FALSE);
+    Invalidate(FALSE);
+    return 0;
+}
+
+LRESULT CImgAreaWindow::OnLButtonDblClk(UINT nMsg, WPARAM wParam, LPARAM 
lParam, BOOL& bHandled)
+{
+    drawing = FALSE;
+    ReleaseCapture();
+    INT x = GET_X_LPARAM(lParam), y = GET_Y_LPARAM(lParam);
+    toolsModel.OnButtonDown(TRUE, UnZoomed(x), UnZoomed(y), TRUE);
+    toolsModel.resetTool();
     Invalidate(FALSE);
-    if ((toolsModel.GetActiveTool() == TOOL_ZOOM) && (toolsModel.GetZoom() < 
MAX_ZOOM))
-        zoomTo(toolsModel.GetZoom() * 2, GET_X_LPARAM(lParam), 
GET_Y_LPARAM(lParam));
     return 0;
 }
 
 LRESULT CImgAreaWindow::OnRButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, 
BOOL& bHandled)
 {
-    if ((!drawing) || (toolsModel.GetActiveTool() == TOOL_COLOR))
-    {
-        SetCapture();
-        drawing = TRUE;
-        startPaintingR(imageModel.GetDC(), UnZoomed(GET_X_LPARAM(lParam)), 
UnZoomed(GET_Y_LPARAM(lParam)),
-                       paletteModel.GetFgColor(), paletteModel.GetBgColor());
-    }
-    else
-    {
-        SendMessage(WM_RBUTTONUP, wParam, lParam);
-        imageModel.Undo();
-    }
+    drawing = TRUE;
+    SetCapture();
+    INT x = GET_X_LPARAM(lParam), y = GET_Y_LPARAM(lParam);
+    toolsModel.OnButtonDown(FALSE, UnZoomed(x), UnZoomed(y), FALSE);
+    Invalidate(FALSE);
+    return 0;
+}
+
+LRESULT CImgAreaWindow::OnRButtonDblClk(UINT nMsg, WPARAM wParam, LPARAM 
lParam, BOOL& bHandled)
+{
+    drawing = FALSE;
+    ReleaseCapture();
+    INT x = GET_X_LPARAM(lParam), y = GET_Y_LPARAM(lParam);
+    toolsModel.OnButtonDown(FALSE, UnZoomed(x), UnZoomed(y), TRUE);
+    toolsModel.resetTool();
     Invalidate(FALSE);
-    if ((toolsModel.GetActiveTool() == TOOL_ZOOM) && (toolsModel.GetZoom() > 
MIN_ZOOM))
-        zoomTo(toolsModel.GetZoom() / 2, GET_X_LPARAM(lParam), 
GET_Y_LPARAM(lParam));
     return 0;
 }
 
@@ -201,62 +203,27 @@ LRESULT CImgAreaWindow::OnLButtonUp(UINT nMsg, WPARAM 
wParam, LPARAM lParam, BOO
 {
     if (drawing)
     {
-        endPaintingL(imageModel.GetDC(), UnZoomed(GET_X_LPARAM(lParam)), 
UnZoomed(GET_Y_LPARAM(lParam)), paletteModel.GetFgColor(),
-                     paletteModel.GetBgColor());
+        drawing = FALSE;
+        INT x = GET_X_LPARAM(lParam), y = GET_Y_LPARAM(lParam);
+        toolsModel.OnButtonUp(TRUE, UnZoomed(x), UnZoomed(y));
         Invalidate(FALSE);
-        if (toolsModel.GetActiveTool() == TOOL_COLOR)
-        {
-            COLORREF tempColor =
-                GetPixel(imageModel.GetDC(), UnZoomed(GET_X_LPARAM(lParam)), 
UnZoomed(GET_Y_LPARAM(lParam)));
-            if (tempColor != CLR_INVALID)
-                paletteModel.SetFgColor(tempColor);
-        }
         SendMessage(hStatusBar, SB_SETTEXT, 2, (LPARAM) "");
     }
-    drawing = FALSE;
     ReleaseCapture();
     return 0;
 }
 
 void CImgAreaWindow::cancelDrawing()
 {
-    POINT pt;
-    switch (toolsModel.GetActiveTool())
-    {
-        case TOOL_FREESEL: case TOOL_RECTSEL:
-        case TOOL_TEXT: case TOOL_ZOOM: case TOOL_SHAPE:
-            imageModel.ResetToPrevious();
-            selectionModel.ResetPtStack();
-            pointSP = 0;
-            Invalidate(FALSE);
-            break;
-        default:
-            GetCursorPos(&pt);
-            ScreenToClient(&pt);
-            // FIXME: dirty hack
-            if (GetKeyState(VK_LBUTTON) < 0)
-            {
-                endPaintingL(imageModel.GetDC(), UnZoomed(pt.x), 
UnZoomed(pt.y), paletteModel.GetFgColor(),
-                             paletteModel.GetBgColor());
-            }
-            else if (GetKeyState(VK_RBUTTON) < 0)
-            {
-                endPaintingR(imageModel.GetDC(), UnZoomed(pt.x), 
UnZoomed(pt.y), paletteModel.GetFgColor(),
-                             paletteModel.GetBgColor());
-            }
-            imageModel.Undo();
-            pointSP = 0;
-            selectionModel.ResetPtStack();
-    }
+    drawing = FALSE;
+    toolsModel.OnCancelDraw();
+    Invalidate(FALSE);
 }
 
 LRESULT CImgAreaWindow::OnCaptureChanged(UINT nMsg, WPARAM wParam, LPARAM 
lParam, BOOL& bHandled)
 {
     if (drawing)
-    {
         cancelDrawing();
-        drawing = FALSE;
-    }
     return 0;
 }
 
@@ -270,14 +237,8 @@ LRESULT CImgAreaWindow::OnKeyDown(UINT nMsg, WPARAM 
wParam, LPARAM lParam, BOOL&
         }
         else
         {
-            switch (toolsModel.GetActiveTool())
-            {
-                case TOOL_SHAPE: case TOOL_BEZIER:
-                    cancelDrawing();
-                    break;
-                default:
-                    break;
-            }
+            if (drawing || ToolBase::pointSP != 0)
+                cancelDrawing();
         }
     }
     return 0;
@@ -287,19 +248,12 @@ LRESULT CImgAreaWindow::OnRButtonUp(UINT nMsg, WPARAM 
wParam, LPARAM lParam, BOO
 {
     if (drawing)
     {
-        endPaintingR(imageModel.GetDC(), UnZoomed(GET_X_LPARAM(lParam)), 
UnZoomed(GET_Y_LPARAM(lParam)), paletteModel.GetFgColor(),
-                     paletteModel.GetBgColor());
+        drawing = FALSE;
+        INT x = GET_X_LPARAM(lParam), y = GET_Y_LPARAM(lParam);
+        toolsModel.OnButtonUp(FALSE, UnZoomed(x), UnZoomed(y));
         Invalidate(FALSE);
-        if (toolsModel.GetActiveTool() == TOOL_COLOR)
-        {
-            COLORREF tempColor =
-                GetPixel(imageModel.GetDC(), UnZoomed(GET_X_LPARAM(lParam)), 
UnZoomed(GET_Y_LPARAM(lParam)));
-            if (tempColor != CLR_INVALID)
-                paletteModel.SetBgColor(tempColor);
-        }
         SendMessage(hStatusBar, SB_SETTEXT, 2, (LPARAM) "");
     }
-    drawing = FALSE;
     ReleaseCapture();
     return 0;
 }
@@ -374,9 +328,9 @@ LRESULT CImgAreaWindow::OnMouseMove(UINT nMsg, WPARAM 
wParam, LPARAM lParam, BOO
             default:
                 break;
         }
-        if ((wParam & MK_LBUTTON) != 0)
+        if (wParam & MK_LBUTTON)
         {
-            whilePaintingL(imageModel.GetDC(), xNow, yNow, 
paletteModel.GetFgColor(), paletteModel.GetBgColor());
+            toolsModel.OnMouseMove(TRUE, xNow, yNow);
             Invalidate(FALSE);
             if ((toolsModel.GetActiveTool() >= TOOL_TEXT) || 
(toolsModel.GetActiveTool() == TOOL_RECTSEL) || (toolsModel.GetActiveTool() == 
TOOL_FREESEL))
             {
@@ -387,9 +341,9 @@ LRESULT CImgAreaWindow::OnMouseMove(UINT nMsg, WPARAM 
wParam, LPARAM lParam, BOO
                 SendMessage(hStatusBar, SB_SETTEXT, 2, (LPARAM) (LPCTSTR) 
strSize);
             }
         }
-        if ((wParam & MK_RBUTTON) != 0)
+        if (wParam & MK_RBUTTON)
         {
-            whilePaintingR(imageModel.GetDC(), xNow, yNow, 
paletteModel.GetFgColor(), paletteModel.GetBgColor());
+            toolsModel.OnMouseMove(FALSE, xNow, yNow);
             Invalidate(FALSE);
             if (toolsModel.GetActiveTool() >= TOOL_TEXT)
             {
diff --git a/base/applications/mspaint/imgarea.h 
b/base/applications/mspaint/imgarea.h
index 184076c965a..4c1a41ec27c 100644
--- a/base/applications/mspaint/imgarea.h
+++ b/base/applications/mspaint/imgarea.h
@@ -13,6 +13,10 @@
 class CImgAreaWindow : public CWindowImpl<CMainWindow>
 {
 public:
+    CImgAreaWindow() : drawing(FALSE)
+    {
+    }
+
     DECLARE_WND_CLASS_EX(_T("ImgAreaWindow"), CS_DBLCLKS, COLOR_BTNFACE)
 
     BEGIN_MSG_MAP(CImgAreaWindow)
@@ -20,7 +24,9 @@ public:
         MESSAGE_HANDLER(WM_PAINT, OnPaint)
         MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
         MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
+        MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDblClk)
         MESSAGE_HANDLER(WM_RBUTTONDOWN, OnRButtonDown)
+        MESSAGE_HANDLER(WM_RBUTTONDBLCLK, OnRButtonDblClk)
         MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
         MESSAGE_HANDLER(WM_RBUTTONUP, OnRButtonUp)
         MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
@@ -39,7 +45,9 @@ private:
     LRESULT OnPaint(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
     LRESULT OnSetCursor(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& 
bHandled);
     LRESULT OnLButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& 
bHandled);
+    LRESULT OnLButtonDblClk(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& 
bHandled);
     LRESULT OnRButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& 
bHandled);
+    LRESULT OnRButtonDblClk(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& 
bHandled);
     LRESULT OnLButtonUp(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& 
bHandled);
     LRESULT OnRButtonUp(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& 
bHandled);
     LRESULT OnMouseMove(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& 
bHandled);
diff --git a/base/applications/mspaint/mouse.cpp 
b/base/applications/mspaint/mouse.cpp
index 0720a9fde56..e5b50b617ad 100644
--- a/base/applications/mspaint/mouse.cpp
+++ b/base/applications/mspaint/mouse.cpp
@@ -11,6 +11,9 @@
 
 #include "precomp.h"
 
+INT ToolBase::pointSP = 0;
+POINT ToolBase::pointStack[256] = { { 0 } };
+
 /* FUNCTIONS ********************************************************/
 
 void
@@ -58,512 +61,583 @@ BOOL nearlyEqualPoints(INT x0, INT y0, INT x1, INT y1)
     return (abs(x1 - x0) <= cxThreshold) && (abs(y1 - y0) <= cyThreshold);
 }
 
-POINT pointStack[256];
-short pointSP;
+void updateStartAndLast(LONG x, LONG y)
+{
+    start.x = last.x = x;
+    start.y = last.y = y;
+}
 
-void
-startPaintingL(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg)
+void updateLast(LONG x, LONG y)
 {
-    start.x = x;
-    start.y = y;
     last.x = x;
     last.y = y;
-    switch (toolsModel.GetActiveTool())
+}
+
+void ToolBase::reset()
+{
+    pointSP = 0;
+}
+
+void ToolBase::OnCancelDraw()
+{
+    reset();
+}
+
+void ToolBase::beginEvent()
+{
+    m_hdc = imageModel.GetDC();
+    m_fg = paletteModel.GetFgColor();
+    m_bg = paletteModel.GetBgColor();
+}
+
+void ToolBase::endEvent()
+{
+    m_hdc = NULL;
+}
+
+/* TOOLS ********************************************************/
+
+// TOOL_FREESEL
+struct FreeSelTool : ToolBase
+{
+    FreeSelTool() : ToolBase(TOOL_FREESEL)
     {
-        case TOOL_FREESEL:
-            selectionWindow.ShowWindow(SW_HIDE);
-            selectionModel.ResetPtStack();
-            selectionModel.PushToPtStack(x, y);
-            break;
-        case TOOL_LINE:
-        case TOOL_RECT:
-        case TOOL_ELLIPSE:
-        case TOOL_RRECT:
+    }
+
+    void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick)
+    {
+        imageModel.CopyPrevious();
+        selectionWindow.ShowWindow(SW_HIDE);
+        selectionModel.ResetPtStack();
+        selectionModel.PushToPtStack(x, y);
+    }
+
+    void OnMouseMove(BOOL bLeftButton, LONG x, LONG y)
+    {
+        if (selectionModel.PtStackSize() == 1)
             imageModel.CopyPrevious();
-            break;
-        case TOOL_RECTSEL:
-        case TOOL_TEXT:
+        selectionModel.PushToPtStack(max(0, min(x, imageModel.GetWidth())), 
max(0, min(y, imageModel.GetHeight())));
+        imageModel.ResetToPrevious();
+        selectionModel.DrawFramePoly(m_hdc);
+    }
+
+    void OnButtonUp(BOOL bLeftButton, LONG x, LONG y)
+    {
+        selectionModel.CalculateBoundingBoxAndContents(m_hdc);
+        if (selectionModel.PtStackSize() > 1)
+        {
+            selectionModel.DrawBackgroundPoly(m_hdc, m_bg);
+            imageModel.CopyPrevious();
+
+            selectionModel.DrawSelection(m_hdc);
+
+            placeSelWin();
+            selectionWindow.ShowWindow(SW_SHOW);
+            ForceRefreshSelectionContents();
+        }
+        selectionModel.ResetPtStack();
+    }
+
+    void OnCancelDraw()
+    {
+        imageModel.ResetToPrevious();
+        selectionModel.ResetPtStack();
+        ToolBase::OnCancelDraw();
+    }
+};
+
+// TOOL_RECTSEL
+struct RectSelTool : ToolBase
+{
+    RectSelTool() : ToolBase(TOOL_RECTSEL)
+    {
+    }
+
+    void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick)
+    {
+        imageModel.CopyPrevious();
+        if (bLeftButton)
+        {
             imageModel.CopyPrevious();
             selectionWindow.ShowWindow(SW_HIDE);
             selectionModel.SetSrcRectSizeToZero();
-            break;
-        case TOOL_RUBBER:
-            imageModel.CopyPrevious();
-            Erase(hdc, x, y, x, y, bg, toolsModel.GetRubberRadius());
-            break;
-        case TOOL_FILL:
-            imageModel.CopyPrevious();
-            Fill(hdc, x, y, fg);
-            break;
-        case TOOL_PEN:
-            imageModel.CopyPrevious();
-            SetPixel(hdc, x, y, fg);
-            break;
-        case TOOL_BRUSH:
-            imageModel.CopyPrevious();
-            Brush(hdc, x, y, x, y, fg, toolsModel.GetBrushStyle());
-            break;
-        case TOOL_AIRBRUSH:
-            imageModel.CopyPrevious();
-            Airbrush(hdc, x, y, fg, toolsModel.GetAirBrushWidth());
-            break;
-        case TOOL_BEZIER:
-            pointStack[pointSP].x = x;
-            pointStack[pointSP].y = y;
-            if (pointSP == 0)
-            {
-                imageModel.CopyPrevious();
-                pointSP++;
-            }
-            break;
-        case TOOL_SHAPE:
-            pointStack[pointSP].x = x;
-            pointStack[pointSP].y = y;
-            if (pointSP + 1 >= 2)
-                Poly(hdc, pointStack, pointSP + 1, fg, bg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), FALSE, FALSE);
-            if (pointSP == 0)
-            {
-                imageModel.CopyPrevious();
-                pointSP++;
-            }
-            break;
-        case TOOL_COLOR:
-        case TOOL_ZOOM:
-            break;
+        }
     }
-}
 
-void
-whilePaintingL(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg)
-{
-    switch (toolsModel.GetActiveTool())
+    void OnMouseMove(BOOL bLeftButton, LONG x, LONG y)
     {
-        case TOOL_FREESEL:
-            if (selectionModel.PtStackSize() == 1)
-                imageModel.CopyPrevious();
-            selectionModel.PushToPtStack(max(0, min(x, 
imageModel.GetWidth())), max(0, min(y, imageModel.GetHeight())));
-            imageModel.ResetToPrevious();
-            selectionModel.DrawFramePoly(hdc);
-            break;
-        case TOOL_RECTSEL:
-        case TOOL_TEXT:
+        POINT temp;
+        if (bLeftButton)
         {
-            POINT temp;
             imageModel.ResetToPrevious();
             temp.x = max(0, min(x, imageModel.GetWidth()));
             temp.y = max(0, min(y, imageModel.GetHeight()));
             selectionModel.SetSrcAndDestRectFromPoints(start, temp);
-            RectSel(hdc, start.x, start.y, temp.x, temp.y);
-            break;
+            RectSel(m_hdc, start.x, start.y, temp.x, temp.y);
         }
-        case TOOL_RUBBER:
-            Erase(hdc, last.x, last.y, x, y, bg, toolsModel.GetRubberRadius());
-            break;
-        case TOOL_PEN:
-            Line(hdc, last.x, last.y, x, y, fg, 1);
-            break;
-        case TOOL_BRUSH:
-            Brush(hdc, last.x, last.y, x, y, fg, toolsModel.GetBrushStyle());
-            break;
-        case TOOL_AIRBRUSH:
-            Airbrush(hdc, x, y, fg, toolsModel.GetAirBrushWidth());
-            break;
-        case TOOL_LINE:
-            imageModel.ResetToPrevious();
-            if (GetAsyncKeyState(VK_SHIFT) < 0)
-                roundTo8Directions(start.x, start.y, x, y);
-            Line(hdc, start.x, start.y, x, y, fg, toolsModel.GetLineWidth());
-            break;
-        case TOOL_BEZIER:
-            imageModel.ResetToPrevious();
-            pointStack[pointSP].x = x;
-            pointStack[pointSP].y = y;
-            switch (pointSP)
-            {
-                case 1:
-                    Line(hdc, pointStack[0].x, pointStack[0].y, 
pointStack[1].x, pointStack[1].y, fg,
-                         toolsModel.GetLineWidth());
-                    break;
-                case 2:
-                    Bezier(hdc, pointStack[0], pointStack[2], pointStack[2], 
pointStack[1], fg, toolsModel.GetLineWidth());
-                    break;
-                case 3:
-                    Bezier(hdc, pointStack[0], pointStack[2], pointStack[3], 
pointStack[1], fg, toolsModel.GetLineWidth());
-                    break;
-            }
-            break;
-        case TOOL_RECT:
-            imageModel.ResetToPrevious();
-            if (GetAsyncKeyState(VK_SHIFT) < 0)
-                regularize(start.x, start.y, x, y);
-            Rect(hdc, start.x, start.y, x, y, fg, bg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
-            break;
-        case TOOL_SHAPE:
-            imageModel.ResetToPrevious();
-            pointStack[pointSP].x = x;
-            pointStack[pointSP].y = y;
-            if ((pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
-                roundTo8Directions(pointStack[pointSP - 1].x, 
pointStack[pointSP - 1].y,
-                                   pointStack[pointSP].x, 
pointStack[pointSP].y);
-            if (pointSP + 1 >= 2)
-                Poly(hdc, pointStack, pointSP + 1, fg, bg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), FALSE, FALSE);
-            break;
-        case TOOL_ELLIPSE:
-            imageModel.ResetToPrevious();
-            if (GetAsyncKeyState(VK_SHIFT) < 0)
-                regularize(start.x, start.y, x, y);
-            Ellp(hdc, start.x, start.y, x, y, fg, bg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
-            break;
-        case TOOL_RRECT:
-            imageModel.ResetToPrevious();
-            if (GetAsyncKeyState(VK_SHIFT) < 0)
-                regularize(start.x, start.y, x, y);
-            RRect(hdc, start.x, start.y, x, y, fg, bg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
-            break;
-        case TOOL_FILL:
-        case TOOL_COLOR:
-        case TOOL_ZOOM:
-            break;
     }
 
-    last.x = x;
-    last.y = y;
-}
-
-void
-endPaintingL(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg)
-{
-    switch (toolsModel.GetActiveTool())
+    void OnButtonUp(BOOL bLeftButton, LONG x, LONG y)
     {
-        case TOOL_FREESEL:
+        if (bLeftButton)
         {
-            selectionModel.CalculateBoundingBoxAndContents(hdc);
-            if (selectionModel.PtStackSize() > 1)
+            imageModel.ResetToPrevious();
+            if (selectionModel.IsSrcRectSizeNonzero())
             {
-                selectionModel.DrawBackgroundPoly(hdc, bg);
+                selectionModel.CalculateContents(m_hdc);
+                selectionModel.DrawBackgroundRect(m_hdc, m_bg);
                 imageModel.CopyPrevious();
 
-                selectionModel.DrawSelection(hdc);
+                selectionModel.DrawSelection(m_hdc);
 
                 placeSelWin();
                 selectionWindow.ShowWindow(SW_SHOW);
                 ForceRefreshSelectionContents();
             }
-            selectionModel.ResetPtStack();
-            break;
         }
-        case TOOL_RECTSEL:
-            imageModel.ResetToPrevious();
-            if (selectionModel.IsSrcRectSizeNonzero())
-            {
-                selectionModel.CalculateContents(hdc);
-                selectionModel.DrawBackgroundRect(hdc, bg);
-                imageModel.CopyPrevious();
+    }
 
-                selectionModel.DrawSelection(hdc);
+    void OnCancelDraw()
+    {
+        imageModel.ResetToPrevious();
+        selectionModel.ResetPtStack();
+        ToolBase::OnCancelDraw();
+    }
+};
 
-                placeSelWin();
-                selectionWindow.ShowWindow(SW_SHOW);
-                ForceRefreshSelectionContents();
-            }
-            break;
-        case TOOL_TEXT:
-            imageModel.ResetToPrevious();
-            if (selectionModel.IsSrcRectSizeNonzero())
-            {
-                imageModel.CopyPrevious();
+struct GenericDrawTool : ToolBase
+{
+    GenericDrawTool(TOOLTYPE type) : ToolBase(type)
+    {
+    }
 
-                placeSelWin();
-                selectionWindow.ShowWindow(SW_SHOW);
-                ForceRefreshSelectionContents();
-            }
-            break;
-        case TOOL_RUBBER:
-            Erase(hdc, last.x, last.y, x, y, bg, toolsModel.GetRubberRadius());
-            break;
-        case TOOL_PEN:
-            Line(hdc, last.x, last.y, x, y, fg, 1);
-            SetPixel(hdc, x, y, fg);
-            break;
-        case TOOL_LINE:
-            imageModel.ResetToPrevious();
-            if (GetAsyncKeyState(VK_SHIFT) < 0)
-                roundTo8Directions(start.x, start.y, x, y);
-            Line(hdc, start.x, start.y, x, y, fg, toolsModel.GetLineWidth());
-            break;
-        case TOOL_BEZIER:
-            pointSP++;
-            if (pointSP == 4)
-                pointSP = 0;
-            break;
-        case TOOL_RECT:
-            imageModel.ResetToPrevious();
-            if (GetAsyncKeyState(VK_SHIFT) < 0)
-                regularize(start.x, start.y, x, y);
-            Rect(hdc, start.x, start.y, x, y, fg, bg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
-            break;
-        case TOOL_SHAPE:
-            imageModel.ResetToPrevious();
-            pointStack[pointSP].x = x;
-            pointStack[pointSP].y = y;
-            if ((pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
-                roundTo8Directions(pointStack[pointSP - 1].x, 
pointStack[pointSP - 1].y,
-                                   pointStack[pointSP].x, 
pointStack[pointSP].y);
-            pointSP++;
-            if (pointSP >= 2)
-            {
-                if (nearlyEqualPoints(x, y, pointStack[0].x, pointStack[0].y))
-                {
-                    Poly(hdc, pointStack, pointSP, fg, bg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), TRUE, FALSE);
-                    pointSP = 0;
-                }
-                else
-                {
-                    Poly(hdc, pointStack, pointSP, fg, bg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), FALSE, FALSE);
-                }
-            }
-            if (pointSP == 255)
-                pointSP--;
-            break;
-        case TOOL_ELLIPSE:
-            imageModel.ResetToPrevious();
-            if (GetAsyncKeyState(VK_SHIFT) < 0)
-                regularize(start.x, start.y, x, y);
-            Ellp(hdc, start.x, start.y, x, y, fg, bg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
-            break;
-        case TOOL_RRECT:
-            imageModel.ResetToPrevious();
-            if (GetAsyncKeyState(VK_SHIFT) < 0)
-                regularize(start.x, start.y, x, y);
-            RRect(hdc, start.x, start.y, x, y, fg, bg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
-            break;
-        case TOOL_FILL:
-        case TOOL_COLOR:
-        case TOOL_ZOOM:
-        case TOOL_BRUSH:
-        case TOOL_AIRBRUSH:
-            break;
+    virtual void draw(BOOL bLeftButton, LONG x, LONG y) = 0;
+
+    void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick)
+    {
+        imageModel.CopyPrevious();
+        draw(bLeftButton, x, y);
     }
-}
 
-void
-startPaintingR(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg)
+    void OnMouseMove(BOOL bLeftButton, LONG x, LONG y)
+    {
+        draw(bLeftButton, x, y);
+    }
+
+    void OnButtonUp(BOOL bLeftButton, LONG x, LONG y)
+    {
+        draw(bLeftButton, x, y);
+    }
+
+    void OnCancelDraw()
+    {
+        OnButtonUp(FALSE, 0, 0);
+        imageModel.Undo();
+        selectionModel.ResetPtStack();
+        ToolBase::OnCancelDraw();
+    }
+};
+
+// TOOL_RUBBER
+struct RubberTool : GenericDrawTool
 {
-    start.x = x;
-    start.y = y;
-    last.x = x;
-    last.y = y;
-    switch (toolsModel.GetActiveTool())
-    {
-        case TOOL_FREESEL:
-        case TOOL_TEXT:
-        case TOOL_LINE:
-        case TOOL_RECT:
-        case TOOL_ELLIPSE:
-        case TOOL_RRECT:
-            imageModel.CopyPrevious();
-            break;
-        case TOOL_RUBBER:
-            imageModel.CopyPrevious();
-            Replace(hdc, x, y, x, y, fg, bg, toolsModel.GetRubberRadius());
-            break;
-        case TOOL_FILL:
-            imageModel.CopyPrevious();
-            Fill(hdc, x, y, bg);
-            break;
-        case TOOL_PEN:
-            imageModel.CopyPrevious();
-            SetPixel(hdc, x, y, bg);
-            break;
-        case TOOL_BRUSH:
+    RubberTool() : GenericDrawTool(TOOL_RUBBER)
+    {
+    }
+
+    virtual void draw(BOOL bLeftButton, LONG x, LONG y)
+    {
+        if (bLeftButton)
+            Erase(m_hdc, last.x, last.y, x, y, m_bg, 
toolsModel.GetRubberRadius());
+        else
+            Replace(m_hdc, last.x, last.y, x, y, m_fg, m_bg, 
toolsModel.GetRubberRadius());
+    }
+};
+
+// TOOL_FILL
+struct FillTool : ToolBase
+{
+    FillTool() : ToolBase(TOOL_FILL)
+    {
+    }
+
+    void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick)
+    {
+        imageModel.CopyPrevious();
+        Fill(m_hdc, x, y, bLeftButton ? m_fg : m_bg);
+    }
+};
+
+// TOOL_COLOR
+struct ColorTool : ToolBase
+{
+    ColorTool() : ToolBase(TOOL_COLOR)
+    {
+    }
+
+    void OnButtonUp(BOOL bLeftButton, LONG x, LONG y)
+    {
+        COLORREF tempColor;
+
+        if (0 <= x && x < imageModel.GetWidth() && 0 <= y && y < 
imageModel.GetHeight())
+            tempColor = GetPixel(m_hdc, x, y);
+        else
+            tempColor = RGB(255, 255, 255); // Outside is white
+
+        if (bLeftButton)
+            paletteModel.SetFgColor(tempColor);
+        else
+            paletteModel.SetBgColor(tempColor);
+
+        toolsModel.SetActiveTool(toolsModel.GetOldActiveTool());
+    }
+};
+
+// TOOL_ZOOM
+struct ZoomTool : ToolBase
+{
+    ZoomTool() : ToolBase(TOOL_ZOOM)
+    {
+    }
+
+    void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick)
+    {
+        imageModel.CopyPrevious();
+        if (bLeftButton)
+        {
+            if (toolsModel.GetZoom() < MAX_ZOOM)
+                zoomTo(toolsModel.GetZoom() * 2, x, y);
+        }
+        else
+        {
+            if (toolsModel.GetZoom() > MIN_ZOOM)
+                zoomTo(toolsModel.GetZoom() / 2, x, y);
+        }
+    }
+};
+
+// TOOL_PEN
+struct PenTool : GenericDrawTool
+{
+    PenTool() : GenericDrawTool(TOOL_PEN)
+    {
+    }
+
+    virtual void draw(BOOL bLeftButton, LONG x, LONG y)
+    {
+        COLORREF rgb = bLeftButton ? m_fg : m_bg;
+        Line(m_hdc, last.x, last.y, x, y, rgb, 1);
+        SetPixel(m_hdc, x, y, rgb);
+    }
+};
+
+// TOOL_BRUSH
+struct BrushTool : GenericDrawTool
+{
+    BrushTool() : GenericDrawTool(TOOL_BRUSH)
+    {
+    }
+
+    virtual void draw(BOOL bLeftButton, LONG x, LONG y)
+    {
+        COLORREF rgb = bLeftButton ? m_fg : m_bg;
+        Brush(m_hdc, last.x, last.y, x, y, rgb, toolsModel.GetBrushStyle());
+    }
+};
+
+// TOOL_AIRBRUSH
+struct AirBrushTool : GenericDrawTool
+{
+    AirBrushTool() : GenericDrawTool(TOOL_AIRBRUSH)
+    {
+    }
+
+    virtual void draw(BOOL bLeftButton, LONG x, LONG y)
+    {
+        COLORREF rgb = bLeftButton ? m_fg : m_bg;
+        Airbrush(m_hdc, x, y, rgb, toolsModel.GetAirBrushWidth());
+    }
+};
+
+// TOOL_TEXT
+struct TextTool : ToolBase
+{
+    TextTool() : ToolBase(TOOL_TEXT)
+    {
+    }
+
+    void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick)
+    {
+        imageModel.CopyPrevious();
+    }
+
+    void OnMouseMove(BOOL bLeftButton, LONG x, LONG y)
+    {
+        POINT temp;
+        imageModel.ResetToPrevious();
+        temp.x = max(0, min(x, imageModel.GetWidth()));
+        temp.y = max(0, min(y, imageModel.GetHeight()));
+        selectionModel.SetSrcAndDestRectFromPoints(start, temp);
+        RectSel(m_hdc, start.x, start.y, temp.x, temp.y);
+    }
+
+    void OnButtonUp(BOOL bLeftButton, LONG x, LONG y)
+    {
+        imageModel.ResetToPrevious();
+        if (selectionModel.IsSrcRectSizeNonzero())
+        {
             imageModel.CopyPrevious();
-            Brush(hdc, x, y, x, y, bg, toolsModel.GetBrushStyle());
-            break;
-        case TOOL_AIRBRUSH:
+
+            placeSelWin();
+            selectionWindow.ShowWindow(SW_SHOW);
+            ForceRefreshSelectionContents();
+        }
+    }
+
+    void OnCancelDraw()
+    {
+        imageModel.ResetToPrevious();
+        selectionModel.ResetPtStack();
+        ToolBase::OnCancelDraw();
+    }
+};
+
+// TOOL_LINE
+struct LineTool : GenericDrawTool
+{
+    LineTool() : GenericDrawTool(TOOL_LINE)
+    {
+    }
+
+    virtual void draw(BOOL bLeftButton, LONG x, LONG y)
+    {
+        imageModel.ResetToPrevious();
+        if (GetAsyncKeyState(VK_SHIFT) < 0)
+            roundTo8Directions(start.x, start.y, x, y);
+        COLORREF rgb = bLeftButton ? m_fg : m_bg;
+        Line(m_hdc, start.x, start.y, x, y, rgb, toolsModel.GetLineWidth());
+    }
+};
+
+// TOOL_BEZIER
+struct BezierTool : ToolBase
+{
+    BezierTool() : ToolBase(TOOL_BEZIER)
+    {
+    }
+
+    void draw(BOOL bLeftButton)
+    {
+        COLORREF rgb = (bLeftButton ? m_fg : m_bg);
+        switch (pointSP)
+        {
+            case 1:
+                Line(m_hdc, pointStack[0].x, pointStack[0].y, pointStack[1].x, 
pointStack[1].y, rgb,
+                     toolsModel.GetLineWidth());
+                break;
+            case 2:
+                Bezier(m_hdc, pointStack[0], pointStack[2], pointStack[2], 
pointStack[1], rgb, toolsModel.GetLineWidth());
+                break;
+            case 3:
+                Bezier(m_hdc, pointStack[0], pointStack[2], pointStack[3], 
pointStack[1], rgb, toolsModel.GetLineWidth());
+                break;
+        }
+    }
+
+    void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick)
+    {
+        pointStack[pointSP].x = x;
+        pointStack[pointSP].y = y;
+
+        if (pointSP == 0)
+        {
             imageModel.CopyPrevious();
-            Airbrush(hdc, x, y, bg, toolsModel.GetAirBrushWidth());
-            break;
-        case TOOL_BEZIER:
-            pointStack[pointSP].x = x;
-            pointStack[pointSP].y = y;
-            if (pointSP == 0)
-            {
-                imageModel.CopyPrevious();
-                pointSP++;
-            }
-            break;
-        case TOOL_SHAPE:
-            pointStack[pointSP].x = x;
-            pointStack[pointSP].y = y;
-            if (pointSP + 1 >= 2)
-                Poly(hdc, pointStack, pointSP + 1, bg, fg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), FALSE, FALSE);
-            if (pointSP == 0)
-            {
-                imageModel.CopyPrevious();
-                pointSP++;
-            }
-            break;
-        case TOOL_RECTSEL:
-        case TOOL_COLOR:
-        case TOOL_ZOOM:
-            break;
+            pointSP++;
+        }
     }
-}
 
-void
-whilePaintingR(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg)
+    void OnMouseMove(BOOL bLeftButton, LONG x, LONG y)
+    {
+        imageModel.ResetToPrevious();
+        pointStack[pointSP].x = x;
+        pointStack[pointSP].y = y;
+        draw(bLeftButton);
+    }
+
+    void OnButtonUp(BOOL bLeftButton, LONG x, LONG y)
+    {
+        imageModel.ResetToPrevious();
+        draw(bLeftButton);
+        pointSP++;
+        if (pointSP == 4)
+            pointSP = 0;
+    }
+
+    void OnCancelDraw()
+    {
+        OnButtonUp(FALSE, 0, 0);
+        imageModel.Undo();
+        selectionModel.ResetPtStack();
+        ToolBase::OnCancelDraw();
+    }
+};
+
+// TOOL_RECT
+struct RectTool : GenericDrawTool
 {
-    switch (toolsModel.GetActiveTool())
-    {
-        case TOOL_RUBBER:
-            Replace(hdc, last.x, last.y, x, y, fg, bg, 
toolsModel.GetRubberRadius());
-            break;
-        case TOOL_PEN:
-            Line(hdc, last.x, last.y, x, y, bg, 1);
-            break;
-        case TOOL_BRUSH:
-            Brush(hdc, last.x, last.y, x, y, bg, toolsModel.GetBrushStyle());
-            break;
-        case TOOL_AIRBRUSH:
-            Airbrush(hdc, x, y, bg, toolsModel.GetAirBrushWidth());
-            break;
-        case TOOL_LINE:
-            imageModel.ResetToPrevious();
-            if (GetAsyncKeyState(VK_SHIFT) < 0)
-                roundTo8Directions(start.x, start.y, x, y);
-            Line(hdc, start.x, start.y, x, y, bg, toolsModel.GetLineWidth());
-            break;
-        case TOOL_BEZIER:
-            imageModel.ResetToPrevious();
-            pointStack[pointSP].x = x;
-            pointStack[pointSP].y = y;
-            switch (pointSP)
-            {
-                case 1:
-                    Line(hdc, pointStack[0].x, pointStack[0].y, 
pointStack[1].x, pointStack[1].y, bg,
-                         toolsModel.GetLineWidth());
-                    break;
-                case 2:
-                    Bezier(hdc, pointStack[0], pointStack[2], pointStack[2], 
pointStack[1], bg, toolsModel.GetLineWidth());
-                    break;
-                case 3:
-                    Bezier(hdc, pointStack[0], pointStack[2], pointStack[3], 
pointStack[1], bg, toolsModel.GetLineWidth());
-                    break;
-            }
-            break;
-        case TOOL_RECT:
-            imageModel.ResetToPrevious();
-            if (GetAsyncKeyState(VK_SHIFT) < 0)
-                regularize(start.x, start.y, x, y);
-            Rect(hdc, start.x, start.y, x, y, bg, fg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
-            break;
-        case TOOL_SHAPE:
-            imageModel.ResetToPrevious();
-            pointStack[pointSP].x = x;
-            pointStack[pointSP].y = y;
-            if ((pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
-                roundTo8Directions(pointStack[pointSP - 1].x, 
pointStack[pointSP - 1].y,
-                                   pointStack[pointSP].x, 
pointStack[pointSP].y);
-            if (pointSP + 1 >= 2)
-                Poly(hdc, pointStack, pointSP + 1, bg, fg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), FALSE, FALSE);
-            break;
-        case TOOL_ELLIPSE:
-            imageModel.ResetToPrevious();
-            if (GetAsyncKeyState(VK_SHIFT) < 0)
-                regularize(start.x, start.y, x, y);
-            Ellp(hdc, start.x, start.y, x, y, bg, fg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
-            break;
-        case TOOL_RRECT:
-            imageModel.ResetToPrevious();
-            if (GetAsyncKeyState(VK_SHIFT) < 0)
-                regularize(start.x, start.y, x, y);
-            RRect(hdc, start.x, start.y, x, y, bg, fg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
-            break;
-        case TOOL_FREESEL:
-        case TOOL_RECTSEL:
-        case TOOL_FILL:
-        case TOOL_COLOR:
-        case TOOL_ZOOM:
-        case TOOL_TEXT:
-            break;
+    RectTool() : GenericDrawTool(TOOL_RECT)
+    {
     }
 
-    last.x = x;
-    last.y = y;
-}
+    virtual void draw(BOOL bLeftButton, LONG x, LONG y)
+    {
+        imageModel.ResetToPrevious();
+        if (GetAsyncKeyState(VK_SHIFT) < 0)
+            regularize(start.x, start.y, x, y);
+        if (bLeftButton)
+            Rect(m_hdc, start.x, start.y, x, y, m_fg, m_bg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
+        else
+            Rect(m_hdc, start.x, start.y, x, y, m_bg, m_fg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
+    }
+};
 
-void
-endPaintingR(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg)
+// TOOL_SHAPE
+struct ShapeTool : ToolBase
 {
-    switch (toolsModel.GetActiveTool())
-    {
-        case TOOL_RUBBER:
-            Replace(hdc, last.x, last.y, x, y, fg, bg, 
toolsModel.GetRubberRadius());
-            break;
-        case TOOL_PEN:
-            Line(hdc, last.x, last.y, x, y, bg, 1);
-            SetPixel(hdc, x, y, bg);
-            break;
-        case TOOL_LINE:
-            imageModel.ResetToPrevious();
-            if (GetAsyncKeyState(VK_SHIFT) < 0)
-                roundTo8Directions(start.x, start.y, x, y);
-            Line(hdc, start.x, start.y, x, y, bg, toolsModel.GetLineWidth());
-            break;
-        case TOOL_BEZIER:
+    ShapeTool() : ToolBase(TOOL_SHAPE)
+    {
+    }
+
+    void draw(BOOL bLeftButton, LONG x, LONG y, BOOL bClosed = FALSE)
+    {
+        if (pointSP + 1 >= 2)
+        {
+            if (bLeftButton)
+                Poly(m_hdc, pointStack, pointSP + 1, m_fg, m_bg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), bClosed, FALSE);
+            else
+                Poly(m_hdc, pointStack, pointSP + 1, m_bg, m_fg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), bClosed, FALSE);
+        }
+    }
+
+    void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick)
+    {
+        pointStack[pointSP].x = x;
+        pointStack[pointSP].y = y;
+
+        if (pointSP == 0 && !bDoubleClick)
+        {
+            imageModel.CopyPrevious();
+            draw(bLeftButton, x, y);
+            pointSP++;
+        }
+        else
+        {
+            draw(bLeftButton, x, y, bDoubleClick);
+        }
+    }
+
+    void OnMouseMove(BOOL bLeftButton, LONG x, LONG y)
+    {
+        imageModel.ResetToPrevious();
+        pointStack[pointSP].x = x;
+        pointStack[pointSP].y = y;
+        if ((pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
+            roundTo8Directions(pointStack[pointSP - 1].x, pointStack[pointSP - 
1].y, x, y);
+        draw(bLeftButton, x, y, FALSE);
+    }
+
+    void OnButtonUp(BOOL bLeftButton, LONG x, LONG y)
+    {
+        imageModel.ResetToPrevious();
+        if ((pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
+            roundTo8Directions(pointStack[pointSP - 1].x, pointStack[pointSP - 
1].y, x, y);
+
+        if (nearlyEqualPoints(x, y, pointStack[0].x, pointStack[0].y))
+        {
+            pointSP--;
+            draw(bLeftButton, x, y, TRUE);
+            pointSP = 0;
+        }
+        else
+        {
             pointSP++;
-            if (pointSP == 4)
-                pointSP = 0;
-            break;
-        case TOOL_RECT:
-            imageModel.ResetToPrevious();
-            if (GetAsyncKeyState(VK_SHIFT) < 0)
-                regularize(start.x, start.y, x, y);
-            Rect(hdc, start.x, start.y, x, y, bg, fg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
-            break;
-        case TOOL_SHAPE:
-            imageModel.ResetToPrevious();
             pointStack[pointSP].x = x;
             pointStack[pointSP].y = y;
-            if ((pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
-                roundTo8Directions(pointStack[pointSP - 1].x, 
pointStack[pointSP - 1].y,
-                                   pointStack[pointSP].x, 
pointStack[pointSP].y);
-            pointSP++;
-            if (pointSP >= 2)
-            {
-                if (nearlyEqualPoints(x, y, pointStack[0].x, pointStack[0].y))
-                {
-                    Poly(hdc, pointStack, pointSP, bg, fg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), TRUE, FALSE);
-                    pointSP = 0;
-                }
-                else
-                {
-                    Poly(hdc, pointStack, pointSP, bg, fg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), FALSE, FALSE);
-                }
-            }
-            if (pointSP == 255)
-                pointSP--;
-            break;
-        case TOOL_ELLIPSE:
-            imageModel.ResetToPrevious();
-            if (GetAsyncKeyState(VK_SHIFT) < 0)
-                regularize(start.x, start.y, x, y);
-            Ellp(hdc, start.x, start.y, x, y, bg, fg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
-            break;
-        case TOOL_RRECT:
-            imageModel.ResetToPrevious();
-            if (GetAsyncKeyState(VK_SHIFT) < 0)
-                regularize(start.x, start.y, x, y);
-            RRect(hdc, start.x, start.y, x, y, bg, fg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
-            break;
-        case TOOL_FREESEL:
-        case TOOL_RECTSEL:
-        case TOOL_FILL:
-        case TOOL_COLOR:
-        case TOOL_ZOOM:
-        case TOOL_BRUSH:
-        case TOOL_AIRBRUSH:
-        case TOOL_TEXT:
-            break;
+            draw(bLeftButton, x, y, FALSE);
+        }
+
+        if (pointSP == _countof(pointStack))
+            pointSP--;
+    }
+
+    void OnCancelDraw()
+    {
+        imageModel.ResetToPrevious();
+        selectionModel.ResetPtStack();
+        ToolBase::OnCancelDraw();
+    }
+};
+
+// TOOL_ELLIPSE
+struct EllipseTool : GenericDrawTool
+{
+    EllipseTool() : GenericDrawTool(TOOL_ELLIPSE)
+    {
+    }
+
+    virtual void draw(BOOL bLeftButton, LONG x, LONG y)
+    {
+        imageModel.ResetToPrevious();
+        if (GetAsyncKeyState(VK_SHIFT) < 0)
+            regularize(start.x, start.y, x, y);
+        if (bLeftButton)
+            Ellp(m_hdc, start.x, start.y, x, y, m_fg, m_bg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
+        else
+            Ellp(m_hdc, start.x, start.y, x, y, m_bg, m_fg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
+    }
+};
+
+// TOOL_RRECT
+struct RRectTool : GenericDrawTool
+{
+    RRectTool() : GenericDrawTool(TOOL_RRECT)
+    {
+    }
+
+    virtual void draw(BOOL bLeftButton, LONG x, LONG y)
+    {
+        imageModel.ResetToPrevious();
+        if (GetAsyncKeyState(VK_SHIFT) < 0)
+            regularize(start.x, start.y, x, y);
+        if (bLeftButton)
+            RRect(m_hdc, start.x, start.y, x, y, m_fg, m_bg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
+        else
+            RRect(m_hdc, start.x, start.y, x, y, m_bg, m_fg, 
toolsModel.GetLineWidth(), toolsModel.GetShapeStyle());
+    }
+};
+
+/*static*/ ToolBase*
+ToolBase::createToolObject(TOOLTYPE type)
+{
+    switch (type)
+    {
+        case TOOL_FREESEL:  return new FreeSelTool();
+        case TOOL_RECTSEL:  return new RectSelTool();
+        case TOOL_RUBBER:   return new RubberTool();
+        case TOOL_FILL:     return new FillTool();
+        case TOOL_COLOR:    return new ColorTool();
+        case TOOL_ZOOM:     return new ZoomTool();
+        case TOOL_PEN:      return new PenTool();
+        case TOOL_BRUSH:    return new BrushTool();
+        case TOOL_AIRBRUSH: return new AirBrushTool();
+        case TOOL_TEXT:     return new TextTool();
+        case TOOL_LINE:     return new LineTool();
+        case TOOL_BEZIER:   return new BezierTool();
+        case TOOL_RECT:     return new RectTool();
+        case TOOL_SHAPE:    return new ShapeTool();
+        case TOOL_ELLIPSE:  return new EllipseTool();
+        case TOOL_RRECT:    return new RRectTool();
     }
+    UNREACHABLE;
+    return NULL;
 }
diff --git a/base/applications/mspaint/mouse.h 
b/base/applications/mspaint/mouse.h
deleted file mode 100644
index 59f13fd7425..00000000000
--- a/base/applications/mspaint/mouse.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * PROJECT:     PAINT for ReactOS
- * LICENSE:     LGPL
- * FILE:        base/applications/mspaint/mouse.h
- * PURPOSE:     Things which should not be in the mouse event handler itself
- * PROGRAMMERS: Benedikt Freisen
- */
-
-#pragma once
-
-void placeSelWin(void);
-
-void startPaintingL(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg);
-
-void whilePaintingL(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg);
-
-void endPaintingL(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg);
-
-void startPaintingR(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg);
-
-void whilePaintingR(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg);
-
-void endPaintingR(HDC hdc, LONG x, LONG y, COLORREF fg, COLORREF bg);
diff --git a/base/applications/mspaint/precomp.h 
b/base/applications/mspaint/precomp.h
index d51eafa85e6..5b5bfa8279d 100644
--- a/base/applications/mspaint/precomp.h
+++ b/base/applications/mspaint/precomp.h
@@ -38,7 +38,6 @@
 #include "history.h"
 #include "imgarea.h"
 #include "miniature.h"
-#include "mouse.h"
 #include "palette.h"
 #include "palettemodel.h"
 #include "registry.h"
diff --git a/base/applications/mspaint/scrollbox.cpp 
b/base/applications/mspaint/scrollbox.cpp
index 32f48e6d490..d2be15f8f18 100644
--- a/base/applications/mspaint/scrollbox.cpp
+++ b/base/applications/mspaint/scrollbox.cpp
@@ -176,7 +176,23 @@ LRESULT CScrollboxWindow::OnVScroll(UINT nMsg, WPARAM 
wParam, LPARAM lParam, BOO
 LRESULT CScrollboxWindow::OnLButtonDown(UINT nMsg, WPARAM wParam, LPARAM 
lParam, BOOL& bHandled)
 {
     selectionWindow.ShowWindow(SW_HIDE);
-    pointSP = 0;    // resets the point-buffer of the polygon and bezier 
functions
+
+    switch (toolsModel.GetActiveTool())
+    {
+        case TOOL_BEZIER:
+        case TOOL_SHAPE:
+            if (ToolBase::pointSP != 0)
+            {
+                toolsModel.OnCancelDraw();
+                imageArea.Invalidate();
+            }
+            break;
+
+        default:
+            break;
+    }
+
+    toolsModel.resetTool();  // resets the point-buffer of the polygon and 
bezier functions
     return 0;
 }
 
diff --git a/base/applications/mspaint/toolbox.cpp 
b/base/applications/mspaint/toolbox.cpp
index 7e0a3aeeafd..d11917b5cac 100644
--- a/base/applications/mspaint/toolbox.cpp
+++ b/base/applications/mspaint/toolbox.cpp
@@ -71,58 +71,42 @@ LRESULT CToolBox::OnSysColorChange(UINT nMsg, WPARAM 
wParam, LPARAM lParam, BOOL
     return 0;
 }
 
+struct COMMAND_TO_TOOL
+{
+    UINT id;
+    TOOLTYPE tool;
+};
+
+static const COMMAND_TO_TOOL CommandToToolMapping[] =
+{
+    { ID_FREESEL, TOOL_FREESEL },
+    { ID_RECTSEL, TOOL_RECTSEL },
+    { ID_RUBBER, TOOL_RUBBER },
+    { ID_FILL, TOOL_FILL },
+    { ID_COLOR, TOOL_COLOR },
+    { ID_ZOOM, TOOL_ZOOM },
+    { ID_PEN, TOOL_PEN },
+    { ID_BRUSH, TOOL_BRUSH },
+    { ID_AIRBRUSH, TOOL_AIRBRUSH },
+    { ID_TEXT, TOOL_TEXT },
+    { ID_LINE, TOOL_LINE },
+    { ID_BEZIER, TOOL_BEZIER },
+    { ID_RECT, TOOL_RECT },
+    { ID_SHAPE, TOOL_SHAPE },
+    { ID_ELLIPSE, TOOL_ELLIPSE },
+    { ID_RRECT, TOOL_RRECT },
+};
+
 LRESULT CToolBox::OnCommand(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& 
bHandled)
 {
-    switch (LOWORD(wParam))
+    UINT id = LOWORD(wParam);
+    for (size_t i = 0; i < _countof(CommandToToolMapping); ++i)
     {
-        case ID_FREESEL:
-            toolsModel.SetActiveTool(TOOL_FREESEL);
-            break;
-        case ID_RECTSEL:
-            toolsModel.SetActiveTool(TOOL_RECTSEL);
-            break;
-        case ID_RUBBER:
-            toolsModel.SetActiveTool(TOOL_RUBBER);
-            break;
-        case ID_FILL:
-            toolsModel.SetActiveTool(TOOL_FILL);
-            break;
-        case ID_COLOR:
-            toolsModel.SetActiveTool(TOOL_COLOR);
-            break;
-        case ID_ZOOM:
-            toolsModel.SetActiveTool(TOOL_ZOOM);
-            break;
-        case ID_PEN:
-            toolsModel.SetActiveTool(TOOL_PEN);
-            break;
-        case ID_BRUSH:
-            toolsModel.SetActiveTool(TOOL_BRUSH);
-            break;
-        case ID_AIRBRUSH:
-            toolsModel.SetActiveTool(TOOL_AIRBRUSH);
-            break;
-        case ID_TEXT:
-            toolsModel.SetActiveTool(TOOL_TEXT);
-            break;
-        case ID_LINE:
-            toolsModel.SetActiveTool(TOOL_LINE);
-            break;
-        case ID_BEZIER:
-            toolsModel.SetActiveTool(TOOL_BEZIER);
-            break;
-        case ID_RECT:
-            toolsModel.SetActiveTool(TOOL_RECT);
-            break;
-        case ID_SHAPE:
-            toolsModel.SetActiveTool(TOOL_SHAPE);
-            break;
-        case ID_ELLIPSE:
-            toolsModel.SetActiveTool(TOOL_ELLIPSE);
-            break;
-        case ID_RRECT:
-            toolsModel.SetActiveTool(TOOL_RRECT);
+        if (CommandToToolMapping[i].id == id)
+        {
+            toolsModel.SetActiveTool(CommandToToolMapping[i].tool);
             break;
+        }
     }
     return 0;
 }
@@ -130,6 +114,18 @@ LRESULT CToolBox::OnCommand(UINT nMsg, WPARAM wParam, 
LPARAM lParam, BOOL& bHand
 LRESULT CToolBox::OnToolsModelToolChanged(UINT nMsg, WPARAM wParam, LPARAM 
lParam, BOOL& bHandled)
 {
     selectionWindow.ShowWindow(SW_HIDE);
-    pointSP = 0;                // resets the point-buffer of the polygon and 
bezier functions
+    toolsModel.resetTool(); // resets the point-buffer of the polygon and 
bezier functions
+
+    // Check the toolbar button
+    TOOLTYPE tool = toolsModel.GetActiveTool();
+    for (size_t i = 0; i < _countof(CommandToToolMapping); ++i)
+    {
+        if (CommandToToolMapping[i].tool == tool)
+        {
+            toolbar.SendMessage(TB_CHECKBUTTON, CommandToToolMapping[i].id, 
TRUE);
+            break;
+        }
+    }
+
     return 0;
 }
diff --git a/base/applications/mspaint/toolsmodel.cpp 
b/base/applications/mspaint/toolsmodel.cpp
index a0ceaeff1ef..bedf12d4e9a 100644
--- a/base/applications/mspaint/toolsmodel.cpp
+++ b/base/applications/mspaint/toolsmodel.cpp
@@ -17,11 +17,27 @@ ToolsModel::ToolsModel()
     m_lineWidth = 1;
     m_shapeStyle = 0;
     m_brushStyle = 0;
-    m_activeTool = TOOL_PEN;
+    m_oldActiveTool = m_activeTool = TOOL_PEN;
     m_airBrushWidth = 5;
     m_rubberRadius = 4;
     m_transpBg = FALSE;
     m_zoom = 1000;
+    ZeroMemory(&m_tools, sizeof(m_tools));
+    m_pToolObject = GetOrCreateTool(m_activeTool);
+}
+
+ToolsModel::~ToolsModel()
+{
+    for (size_t i = 0; i < TOOL_MAX + 1; ++i)
+        delete m_tools[i];
+}
+
+ToolBase *ToolsModel::GetOrCreateTool(TOOLTYPE nTool)
+{
+    if (!m_tools[nTool])
+        m_tools[nTool] = ToolBase::createToolObject(nTool);
+
+    return m_tools[nTool];
 }
 
 int ToolsModel::GetLineWidth() const
@@ -62,9 +78,29 @@ TOOLTYPE ToolsModel::GetActiveTool() const
     return m_activeTool;
 }
 
+TOOLTYPE ToolsModel::GetOldActiveTool() const
+{
+    return m_oldActiveTool;
+}
+
 void ToolsModel::SetActiveTool(TOOLTYPE nActiveTool)
 {
+    switch (m_activeTool)
+    {
+        case TOOL_FREESEL:
+        case TOOL_RECTSEL:
+        case TOOL_RUBBER:
+        case TOOL_COLOR:
+        case TOOL_ZOOM:
+        case TOOL_TEXT:
+            break;
+        default:
+            m_oldActiveTool = m_activeTool;
+            break;
+    }
+
     m_activeTool = nActiveTool;
+    m_pToolObject = GetOrCreateTool(m_activeTool);
     NotifyToolChanged();
 }
 
@@ -129,3 +165,47 @@ void ToolsModel::NotifyZoomChanged()
 {
     toolSettingsWindow.SendMessage(WM_TOOLSMODELZOOMCHANGED);
 }
+
+void ToolsModel::OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL 
bDoubleClick)
+{
+    m_pToolObject->beginEvent();
+    updateStartAndLast(x, y);
+    m_pToolObject->OnButtonDown(bLeftButton, x, y, bDoubleClick);
+    m_pToolObject->endEvent();
+}
+
+void ToolsModel::OnMouseMove(BOOL bLeftButton, LONG x, LONG y)
+{
+    m_pToolObject->beginEvent();
+    m_pToolObject->OnMouseMove(bLeftButton, x, y);
+    updateLast(x, y);
+    m_pToolObject->endEvent();
+}
+
+void ToolsModel::OnButtonUp(BOOL bLeftButton, LONG x, LONG y)
+{
+    m_pToolObject->beginEvent();
+    m_pToolObject->OnButtonUp(bLeftButton, x, y);
+    updateLast(x, y);
+    m_pToolObject->endEvent();
+}
+
+void ToolsModel::OnCancelDraw()
+{
+    m_pToolObject->beginEvent();
+    m_pToolObject->OnCancelDraw();
+    m_pToolObject->endEvent();
+}
+
+void ToolsModel::resetTool()
+{
+    m_pToolObject->reset();
+}
+
+void ToolsModel::selectAll()
+{
+    SetActiveTool(TOOL_RECTSEL);
+    OnButtonDown(TRUE, 0, 0, FALSE);
+    OnMouseMove(TRUE, imageModel.GetWidth(), imageModel.GetHeight());
+    OnButtonUp(TRUE, imageModel.GetWidth(), imageModel.GetHeight());
+}
diff --git a/base/applications/mspaint/toolsmodel.h 
b/base/applications/mspaint/toolsmodel.h
index 0fff204d626..b8a70dce4a7 100644
--- a/base/applications/mspaint/toolsmodel.h
+++ b/base/applications/mspaint/toolsmodel.h
@@ -26,10 +26,48 @@ enum TOOLTYPE
     TOOL_SHAPE    = 14,
     TOOL_ELLIPSE  = 15,
     TOOL_RRECT    = 16,
+    TOOL_MAX = TOOL_RRECT,
 };
 
 /* CLASSES **********************************************************/
 
+struct ToolBase
+{
+    TOOLTYPE m_tool;
+    HDC m_hdc;
+    COLORREF m_fg, m_bg;
+    static INT pointSP;
+    static POINT pointStack[256];
+
+    ToolBase(TOOLTYPE tool) : m_tool(tool), m_hdc(NULL)
+    {
+    }
+
+    virtual ~ToolBase()
+    {
+    }
+
+    virtual void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL 
bDoubleClick)
+    {
+    }
+
+    virtual void OnMouseMove(BOOL bLeftButton, LONG x, LONG y)
+    {
+    }
+
+    virtual void OnButtonUp(BOOL bLeftButton, LONG x, LONG y)
+    {
+    }
+
+    virtual void OnCancelDraw();
+
+    void beginEvent();
+    void endEvent();
+    void reset();
+
+    static ToolBase* createToolObject(TOOLTYPE type);
+};
+
 class ToolsModel
 {
 private:
@@ -37,10 +75,15 @@ private:
     int m_shapeStyle;
     int m_brushStyle;
     TOOLTYPE m_activeTool;
+    TOOLTYPE m_oldActiveTool;
     int m_airBrushWidth;
     int m_rubberRadius;
     BOOL m_transpBg;
     int m_zoom;
+    ToolBase* m_tools[TOOL_MAX + 1];
+    ToolBase *m_pToolObject;
+
+    ToolBase *GetOrCreateTool(TOOLTYPE nTool);
 
     void NotifyToolChanged();
     void NotifyToolSettingsChanged();
@@ -48,6 +91,7 @@ private:
 
 public:
     ToolsModel();
+    ~ToolsModel();
     int GetLineWidth() const;
     void SetLineWidth(int nLineWidth);
     int GetShapeStyle() const;
@@ -55,6 +99,7 @@ public:
     int GetBrushStyle() const;
     void SetBrushStyle(int nBrushStyle);
     TOOLTYPE GetActiveTool() const;
+    TOOLTYPE GetOldActiveTool() const;
     void SetActiveTool(TOOLTYPE nActiveTool);
     int GetAirBrushWidth() const;
     void SetAirBrushWidth(int nAirBrushWidth);
@@ -64,4 +109,12 @@ public:
     void SetBackgroundTransparent(BOOL bTransparent);
     int GetZoom() const;
     void SetZoom(int nZoom);
+
+    void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick);
+    void OnMouseMove(BOOL bLeftButton, LONG x, LONG y);
+    void OnButtonUp(BOOL bLeftButton, LONG x, LONG y);
+    void OnCancelDraw();
+
+    void resetTool();
+    void selectAll();
 };
diff --git a/base/applications/mspaint/winproc.cpp 
b/base/applications/mspaint/winproc.cpp
index 4ccc7be0cdb..643a914309c 100644
--- a/base/applications/mspaint/winproc.cpp
+++ b/base/applications/mspaint/winproc.cpp
@@ -559,11 +559,8 @@ LRESULT CMainWindow::OnCommand(UINT nMsg, WPARAM wParam, 
LPARAM lParam, BOOL& bH
         {
             HWND hToolbar = FindWindowEx(toolBoxContainer.m_hWnd, NULL, 
TOOLBARCLASSNAME, NULL);
             SendMessage(hToolbar, TB_CHECKBUTTON, ID_RECTSEL, MAKELPARAM(TRUE, 
0));
-            toolBoxContainer.SendMessage(WM_COMMAND, ID_RECTSEL);
-            //TODO: do this properly
-            startPaintingL(imageModel.GetDC(), 0, 0, 
paletteModel.GetFgColor(), paletteModel.GetBgColor());
-            whilePaintingL(imageModel.GetDC(), imageModel.GetWidth(), 
imageModel.GetHeight(), paletteModel.GetFgColor(), paletteModel.GetBgColor());
-            endPaintingL(imageModel.GetDC(), imageModel.GetWidth(), 
imageModel.GetHeight(), paletteModel.GetFgColor(), paletteModel.GetBgColor());
+            toolsModel.selectAll();
+            imageArea.Invalidate(TRUE);
             break;
         }
         case IDM_EDITCOPYTO:

Reply via email to