Something went wrong. Try again.
Reactos
Something went wrong. Try again.
35 kB · 1271 lines
C++
at listview
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272/* * PROJECT: PAINT for ReactOS * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) * PURPOSE: Things which should not be in the mouse event handler itself * COPYRIGHT: Copyright 2015 Benedikt Freisen <b.freisen@gmx.net> * Copyright 2021-2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> */
#include "precomp.h"#include <atlalloc.h>
static SIZE_T s_cPoints = 0;static CHeapPtr<POINT, CLocalAllocator> s_dynamicPoints;static POINT s_staticPoints[512]; // 512 is enoughstatic SIZE_T s_maxPoints = _countof(s_staticPoints);static LPPOINT s_pPoints = s_staticPoints;static POINT g_ptStart, g_ptEnd;
/* FUNCTIONS ********************************************************/
voidregularize(LONG x0, LONG y0, LONG& x1, LONG& y1){ if (labs(x1 - x0) >= labs(y1 - y0)) y1 = y0 + (y1 > y0 ? labs(x1 - x0) : -labs(x1 - x0)); else x1 = x0 + (x1 > x0 ? labs(y1 - y0) : -labs(y1 - y0));}
voidroundTo8Directions(LONG x0, LONG y0, LONG& x1, LONG& y1){ if (labs(x1 - x0) >= labs(y1 - y0)) { if (labs(y1 - y0) * 5 < labs(x1 - x0) * 2) y1 = y0; else y1 = y0 + (y1 > y0 ? labs(x1 - x0) : -labs(x1 - x0)); } else { if (labs(x1 - x0) * 5 < labs(y1 - y0) * 2) x1 = x0; else x1 = x0 + (x1 > x0 ? labs(y1 - y0) : -labs(y1 - y0)); }}
BOOL nearlyEqualPoints(INT x0, INT y0, INT x1, INT y1){ INT cxThreshold = toolsModel.GetLineWidth() + UnZoomed(GetSystemMetrics(SM_CXDRAG)); INT cyThreshold = toolsModel.GetLineWidth() + UnZoomed(GetSystemMetrics(SM_CYDRAG)); return (abs(x1 - x0) <= cxThreshold) && (abs(y1 - y0) <= cyThreshold);}
void getBoundaryOfPoints(RECT& rcBoundary, SIZE_T cPoints, const POINT *pPoints){ POINT ptMin = { MAXLONG, MAXLONG }, ptMax = { (LONG)MINLONG, (LONG)MINLONG }; while (cPoints-- > 0) { LONG x = pPoints->x, y = pPoints->y; ptMin = { min(x, ptMin.x), min(y, ptMin.y) }; ptMax = { max(x, ptMax.x), max(y, ptMax.y) }; ++pPoints; }
ptMax.x += 1; ptMax.y += 1;
CRect rc(ptMin, ptMax); rcBoundary = rc;}
void ShiftPoints(INT dx, INT dy){ for (SIZE_T i = 0; i < s_cPoints; ++i) { POINT& pt = s_pPoints[i]; pt.x += dx; pt.y += dy; }}
void BuildMaskFromPoints(){ CRect rc; getBoundaryOfPoints(rc, s_cPoints, s_pPoints);
ShiftPoints(-rc.left, -rc.top);
HDC hdcMem = ::CreateCompatibleDC(NULL); HBITMAP hbmMask = ::CreateBitmap(rc.Width(), rc.Height(), 1, 1, NULL); HGDIOBJ hbmOld = ::SelectObject(hdcMem, hbmMask); ::FillRect(hdcMem, &rc, (HBRUSH)::GetStockObject(BLACK_BRUSH)); HGDIOBJ hPenOld = ::SelectObject(hdcMem, GetStockObject(NULL_PEN)); HGDIOBJ hbrOld = ::SelectObject(hdcMem, GetStockObject(WHITE_BRUSH)); ::Polygon(hdcMem, s_pPoints, (INT)s_cPoints); ::SelectObject(hdcMem, hbrOld); ::SelectObject(hdcMem, hPenOld); ::SelectObject(hdcMem, hbmOld); ::DeleteDC(hdcMem);
selectionModel.setMask(rc, hbmMask);}
void ToolBase::reset(){ if (s_pPoints != s_staticPoints) { s_dynamicPoints.Free(); s_pPoints = s_staticPoints; s_maxPoints = _countof(s_staticPoints); }
s_cPoints = 0; g_ptEnd = g_ptStart = { -1, -1 };
if (selectionModel.m_bShow) { selectionModel.Landing(); selectionModel.HideSelection(); }}
void ToolBase::OnEndDraw(BOOL bCancel){ reset(); imageModel.NotifyImageChanged();}
void ToolBase::beginEvent(){ m_hdc = imageModel.GetDC(); m_fg = paletteModel.GetFgColor(); m_bg = paletteModel.GetBgColor();}
void ToolBase::endEvent(){ m_hdc = NULL;}
static void pushToPoints(LONG x, LONG y){ if (s_cPoints + 1 >= s_maxPoints) { SIZE_T newMax = s_maxPoints + 512; SIZE_T cbNew = newMax * sizeof(POINT); if (!s_dynamicPoints.ReallocateBytes(cbNew)) { ATLTRACE("%d, %d, %d\n", (INT)s_cPoints, (INT)s_maxPoints, (INT)cbNew); return; }
if (s_pPoints == s_staticPoints) CopyMemory(s_dynamicPoints, s_staticPoints, s_cPoints * sizeof(POINT));
s_pPoints = s_dynamicPoints; s_maxPoints = newMax; }
s_pPoints[s_cPoints++] = { x, y };}
/* TOOLS ********************************************************/
struct TwoPointDrawTool : ToolBase{ BOOL m_bLeftButton = FALSE; BOOL m_bDrawing = FALSE;
void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override { m_bLeftButton = bLeftButton; m_bDrawing = TRUE; imageModel.NotifyImageChanged(); }
BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override { imageModel.NotifyImageChanged(); return TRUE; }
BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override { CRect rcPartial(g_ptStart, g_ptEnd); rcPartial.NormalizeRect(); SIZE size = toolsModel.GetToolSize(); rcPartial.InflateRect((size.cx + 1) / 2, (size.cy + 1) / 2); imageModel.PushImageForUndo(rcPartial);
OnDrawOverlayOnImage(m_hdc); m_bDrawing = FALSE; imageModel.NotifyImageChanged(); return TRUE; }
void OnEndDraw(BOOL bCancel) override { m_bDrawing = FALSE; ToolBase::OnEndDraw(bCancel); }
void OnSpecialTweak(BOOL bMinus) override { toolsModel.MakeLineThickerOrThinner(bMinus); }};
typedef enum DIRECTION{ NO_DIRECTION = -1, DIRECTION_HORIZONTAL, DIRECTION_VERTICAL, DIRECTION_DIAGONAL_RIGHT_DOWN, DIRECTION_DIAGONAL_RIGHT_UP,} DIRECTION;
#define THRESHOULD_DEG 15
static DIRECTIONGetDirection(LONG x0, LONG y0, LONG x1, LONG y1){ LONG dx = x1 - x0, dy = y1 - y0;
if (labs(dx) <= 8 && labs(dy) <= 8) return NO_DIRECTION;
double radian = atan2((double)dy, (double)dx); if (radian < DEG2RAD(-180 + THRESHOULD_DEG)) { ATLTRACE("DIRECTION_HORIZONTAL: %ld\n", RAD2DEG(radian)); return DIRECTION_HORIZONTAL; } if (radian < DEG2RAD(-90 - THRESHOULD_DEG)) { ATLTRACE("DIRECTION_DIAGONAL_RIGHT_DOWN: %ld\n", RAD2DEG(radian)); return DIRECTION_DIAGONAL_RIGHT_DOWN; } if (radian < DEG2RAD(-90 + THRESHOULD_DEG)) { ATLTRACE("DIRECTION_VERTICAL: %ld\n", RAD2DEG(radian)); return DIRECTION_VERTICAL; } if (radian < DEG2RAD(-THRESHOULD_DEG)) { ATLTRACE("DIRECTION_DIAGONAL_RIGHT_UP: %ld\n", RAD2DEG(radian)); return DIRECTION_DIAGONAL_RIGHT_UP; } if (radian < DEG2RAD(+THRESHOULD_DEG)) { ATLTRACE("DIRECTION_HORIZONTAL: %ld\n", RAD2DEG(radian)); return DIRECTION_HORIZONTAL; } if (radian < DEG2RAD(+90 - THRESHOULD_DEG)) { ATLTRACE("DIRECTION_DIAGONAL_RIGHT_DOWN: %ld\n", RAD2DEG(radian)); return DIRECTION_DIAGONAL_RIGHT_DOWN; } if (radian < DEG2RAD(+90 + THRESHOULD_DEG)) { ATLTRACE("DIRECTION_VERTICAL: %ld\n", RAD2DEG(radian)); return DIRECTION_VERTICAL; } if (radian < DEG2RAD(+180 - THRESHOULD_DEG)) { ATLTRACE("DIRECTION_DIAGONAL_RIGHT_UP: %ld\n", RAD2DEG(radian)); return DIRECTION_DIAGONAL_RIGHT_UP; } ATLTRACE("DIRECTION_HORIZONTAL: %ld\n", RAD2DEG(radian)); return DIRECTION_HORIZONTAL;}
static voidRestrictDrawDirection(DIRECTION dir, LONG x0, LONG y0, LONG& x1, LONG& y1){ switch (dir) { case NO_DIRECTION: default: return;
case DIRECTION_HORIZONTAL: y1 = y0; break;
case DIRECTION_VERTICAL: x1 = x0; break;
case DIRECTION_DIAGONAL_RIGHT_DOWN: y1 = y0 + (x1 - x0); break;
case DIRECTION_DIAGONAL_RIGHT_UP: x1 = x0 - (y1 - y0); break; }}
struct SmoothDrawTool : ToolBase{ DIRECTION m_direction = NO_DIRECTION; BOOL m_bShiftDown = FALSE; BOOL m_bLeftButton = FALSE;
virtual void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) = 0;
void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override { m_direction = NO_DIRECTION; m_bShiftDown = (::GetKeyState(VK_SHIFT) & 0x8000); // Is Shift key pressed? m_bLeftButton = bLeftButton; s_cPoints = 0; pushToPoints(x, y); pushToPoints(x, y); // We have to draw the first point imageModel.NotifyImageChanged(); }
BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override { if (!m_bShiftDown) { pushToPoints(x, y); imageModel.NotifyImageChanged(); return TRUE; }
if (m_direction == NO_DIRECTION) { m_direction = GetDirection(g_ptStart.x, g_ptStart.y, x, y); if (m_direction == NO_DIRECTION) return FALSE; }
RestrictDrawDirection(m_direction, g_ptStart.x, g_ptStart.y, x, y); pushToPoints(x, y); imageModel.NotifyImageChanged(); return TRUE; }
BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override { if (m_bShiftDown && m_direction != NO_DIRECTION) RestrictDrawDirection(m_direction, g_ptStart.x, g_ptStart.y, x, y);
pushToPoints(x, y);
CRect rcPartial; getBoundaryOfPoints(rcPartial, s_cPoints, s_pPoints);
SIZE size = toolsModel.GetToolSize(); rcPartial.InflateRect((size.cx + 1) / 2, (size.cy + 1) / 2);
imageModel.PushImageForUndo(rcPartial);
OnDrawOverlayOnImage(m_hdc); imageModel.NotifyImageChanged(); OnEndDraw(FALSE); return TRUE; }
void OnDrawOverlayOnImage(HDC hdc) override { for (SIZE_T i = 1; i < s_cPoints; ++i) { OnDraw(hdc, m_bLeftButton, s_pPoints[i - 1], s_pPoints[i]); } }};
struct SelectionBaseTool : ToolBase{ BOOL m_bLeftButton = FALSE; BOOL m_bCtrlKey = FALSE; BOOL m_bShiftKey = FALSE; BOOL m_bDrawing = FALSE; BOOL m_bNoDrawBack = FALSE; HITTEST m_hitSelection = HIT_NONE;
BOOL isRectSelect() const { return (toolsModel.GetActiveTool() == TOOL_RECTSEL); }
void OnDrawOverlayOnImage(HDC hdc) override { if (selectionModel.IsLanded() || !selectionModel.m_bShow) return;
if (!m_bNoDrawBack) selectionModel.DrawBackground(hdc, selectionModel.m_rgbBack);
selectionModel.DrawSelection(hdc, paletteModel.GetBgColor(), toolsModel.IsBackgroundTransparent()); }
void OnDrawOverlayOnCanvas(HDC hdc) override { if (m_bDrawing || selectionModel.m_bShow) selectionModel.drawFrameOnCanvas(hdc); }
void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override { m_bLeftButton = bLeftButton; m_bCtrlKey = (::GetKeyState(VK_CONTROL) < 0); m_bShiftKey = (::GetKeyState(VK_SHIFT) < 0); m_bDrawing = FALSE; m_hitSelection = HIT_NONE;
POINT pt = { x, y }; if (!m_bLeftButton) // Show context menu on Right-click { canvasWindow.ImageToCanvas(pt); canvasWindow.ClientToScreen(&pt); mainWindow.TrackPopupMenu(pt, 0); return; }
POINT ptCanvas = pt; canvasWindow.ImageToCanvas(ptCanvas); HITTEST hit = selectionModel.hitTest(ptCanvas); if (hit != HIT_NONE) // Dragging of selection started? { if (m_bCtrlKey || m_bShiftKey) { imageModel.PushImageForUndo(); toolsModel.OnDrawOverlayOnImage(imageModel.GetDC()); } m_hitSelection = hit; selectionModel.m_ptHit = pt; selectionModel.TakeOff(); m_bNoDrawBack |= (m_bCtrlKey || m_bShiftKey); imageModel.NotifyImageChanged(); return; }
selectionModel.Landing(); m_bDrawing = TRUE;
imageModel.Clamp(pt); if (isRectSelect()) { selectionModel.SetRectFromPoints(g_ptStart, pt); } else { s_cPoints = 0; pushToPoints(pt.x, pt.y); }
imageModel.NotifyImageChanged(); }
BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override { POINT pt = { x, y };
if (!m_bLeftButton) return TRUE;
if (m_hitSelection != HIT_NONE) // Now dragging selection? { if (m_bShiftKey) toolsModel.OnDrawOverlayOnImage(imageModel.GetDC());
selectionModel.Dragging(m_hitSelection, pt); imageModel.NotifyImageChanged(); return TRUE; }
if (isRectSelect() && ::GetKeyState(VK_SHIFT) < 0) regularize(g_ptStart.x, g_ptStart.y, pt.x, pt.y);
imageModel.Clamp(pt);
if (isRectSelect()) selectionModel.SetRectFromPoints(g_ptStart, pt); else pushToPoints(pt.x, pt.y);
imageModel.NotifyImageChanged(); return TRUE; }
BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override { POINT pt = { x, y }; m_bDrawing = FALSE;
if (!m_bLeftButton) return TRUE;
if (m_hitSelection != HIT_NONE) // Dragging of selection ended? { if (m_bShiftKey) toolsModel.OnDrawOverlayOnImage(imageModel.GetDC());
selectionModel.Dragging(m_hitSelection, pt); m_hitSelection = HIT_NONE; imageModel.NotifyImageChanged(); return TRUE; }
if (isRectSelect() && ::GetKeyState(VK_SHIFT) < 0) regularize(g_ptStart.x, g_ptStart.y, pt.x, pt.y);
imageModel.Clamp(pt);
if (isRectSelect()) { selectionModel.SetRectFromPoints(g_ptStart, pt); selectionModel.m_bShow = !selectionModel.m_rc.IsRectEmpty(); } else { if (s_cPoints > 2) { BuildMaskFromPoints(); selectionModel.m_bShow = TRUE; } else { s_cPoints = 0; selectionModel.m_bShow = FALSE; } }
m_bNoDrawBack = FALSE; imageModel.NotifyImageChanged(); return TRUE; }
void OnEndDraw(BOOL bCancel) override { if (bCancel) selectionModel.HideSelection(); else selectionModel.Landing();
m_bDrawing = FALSE; m_hitSelection = HIT_NONE; ToolBase::OnEndDraw(bCancel); }
void OnSpecialTweak(BOOL bMinus) override { selectionModel.StretchSelection(bMinus); }};
// TOOL_FREESELstruct FreeSelTool : SelectionBaseTool{ void OnDrawOverlayOnImage(HDC hdc) override { SelectionBaseTool::OnDrawOverlayOnImage(hdc);
if (!selectionModel.m_bShow && m_bDrawing) { /* Draw the freehand selection inverted/xored */ Poly(hdc, s_pPoints, (INT)s_cPoints, 0, 0, 2, 0, FALSE, TRUE); } }};
// TOOL_RECTSELstruct RectSelTool : SelectionBaseTool{ void OnDrawOverlayOnImage(HDC hdc) override { SelectionBaseTool::OnDrawOverlayOnImage(hdc);
if (!selectionModel.m_bShow && m_bDrawing) { CRect& rc = selectionModel.m_rc; if (!rc.IsRectEmpty()) RectSel(hdc, rc.left, rc.top, rc.right, rc.bottom); } }};
// TOOL_RUBBERstruct RubberTool : SmoothDrawTool{ void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) override { if (bLeftButton) Erase(hdc, pt0.x, pt0.y, pt1.x, pt1.y, m_bg, toolsModel.GetRubberRadius()); else Replace(hdc, pt0.x, pt0.y, pt1.x, pt1.y, m_fg, m_bg, toolsModel.GetRubberRadius()); }
void OnSpecialTweak(BOOL bMinus) override { toolsModel.MakeRubberThickerOrThinner(bMinus); }};
// TOOL_FILLstruct FillTool : ToolBase{ void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override { imageModel.PushImageForUndo(); Fill(m_hdc, x, y, bLeftButton ? m_fg : m_bg); }};
// TOOL_COLORstruct ColorTool : ToolBase{ void fetchColor(BOOL bLeftButton, LONG x, LONG y) { COLORREF rgbColor;
if (0 <= x && x < imageModel.GetWidth() && 0 <= y && y < imageModel.GetHeight()) rgbColor = GetPixel(m_hdc, x, y); else rgbColor = RGB(255, 255, 255); // Outside is white
if (bLeftButton) paletteModel.SetFgColor(rgbColor); else paletteModel.SetBgColor(rgbColor); }
BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override { fetchColor(bLeftButton, x, y); return TRUE; }
BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override { fetchColor(bLeftButton, x, y); toolsModel.SetActiveTool(toolsModel.GetOldActiveTool()); return TRUE; }};
// TOOL_ZOOMstruct ZoomTool : ToolBase{ BOOL m_bZoomed = FALSE;
BOOL getNewZoomRect(CRect& rcView, INT newZoom);
void OnDrawOverlayOnCanvas(HDC hdc) override { CRect rcView; INT oldZoom = toolsModel.GetZoom(); if (oldZoom < MAX_ZOOM && getNewZoomRect(rcView, oldZoom * 2)) DrawXorRect(hdc, &rcView); }
void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override { INT newZoom, oldZoom = toolsModel.GetZoom(); if (bLeftButton) newZoom = (oldZoom < MAX_ZOOM) ? (oldZoom * 2) : MIN_ZOOM; else newZoom = (oldZoom > MIN_ZOOM) ? (oldZoom / 2) : MAX_ZOOM;
m_bZoomed = FALSE;
if (oldZoom != newZoom) { CRect rcView; if (getNewZoomRect(rcView, newZoom)) { canvasWindow.zoomTo(newZoom, rcView.left, rcView.top); m_bZoomed = TRUE; } } }
BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override { if (m_bZoomed) toolsModel.SetActiveTool(toolsModel.GetOldActiveTool());
return TRUE; }};
BOOL ZoomTool::getNewZoomRect(CRect& rcView, INT newZoom){ CPoint pt; ::GetCursorPos(&pt); canvasWindow.ScreenToClient(&pt);
canvasWindow.getNewZoomRect(rcView, newZoom, pt);
CRect rc; canvasWindow.GetImageRect(rc); canvasWindow.ImageToCanvas(rc);
return rc.PtInRect(pt);}
// TOOL_PENstruct PenTool : SmoothDrawTool{ void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) override { COLORREF rgb = bLeftButton ? m_fg : m_bg; Line(hdc, pt0.x, pt0.y, pt1.x, pt1.y, rgb, toolsModel.GetPenWidth()); }
void OnSpecialTweak(BOOL bMinus) override { toolsModel.MakePenThickerOrThinner(bMinus); }};
// TOOL_BRUSHstruct BrushTool : SmoothDrawTool{ void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) override { COLORREF rgb = bLeftButton ? m_fg : m_bg; Brush(hdc, pt0.x, pt0.y, pt1.x, pt1.y, rgb, toolsModel.GetBrushStyle(), toolsModel.GetBrushWidth()); }
void OnSpecialTweak(BOOL bMinus) override { toolsModel.MakeBrushThickerOrThinner(bMinus); }};
// TOOL_AIRBRUSHstruct AirBrushTool : SmoothDrawTool{ DWORD m_dwTick = 0;
void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override { m_dwTick = GetTickCount(); SmoothDrawTool::OnButtonDown(bLeftButton, x, y, bDoubleClick); }
void OnDrawOverlayOnImage(HDC hdc) override { srand(m_dwTick); SmoothDrawTool::OnDrawOverlayOnImage(hdc); }
void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) override { COLORREF rgb = bLeftButton ? m_fg : m_bg; Airbrush(hdc, pt1.x, pt1.y, rgb, toolsModel.GetAirBrushRadius()); }
void OnSpecialTweak(BOOL bMinus) override { toolsModel.MakeAirBrushThickerOrThinner(bMinus); }};
// TOOL_TEXTstruct TextTool : ToolBase{ void OnDrawOverlayOnImage(HDC hdc) override { if (canvasWindow.m_drawing) { CRect& rc = selectionModel.m_rc; if (!rc.IsRectEmpty()) RectSel(hdc, rc.left, rc.top, rc.right, rc.bottom); } }
void UpdatePoint(LONG x, LONG y) { POINT pt = { x, y }; imageModel.Clamp(pt); selectionModel.SetRectFromPoints(g_ptStart, pt); imageModel.NotifyImageChanged(); }
void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override { if (!textEditWindow.IsWindow()) textEditWindow.Create(canvasWindow);
UpdatePoint(x, y); }
BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override { UpdatePoint(x, y); return TRUE; }
void draw(HDC hdc) { CStringW szText; textEditWindow.GetWindowText(szText);
CRect rc; textEditWindow.InvalidateEditRect(); textEditWindow.GetEditRect(&rc); rc.InflateRect(-GRIP_SIZE / 2, -GRIP_SIZE / 2);
// Draw the text INT style = (toolsModel.IsBackgroundTransparent() ? 0 : 1); Text(hdc, rc.left, rc.top, rc.right, rc.bottom, m_fg, m_bg, szText, textEditWindow.GetFont(), style); }
void quit() { if (textEditWindow.IsWindow()) textEditWindow.ShowWindow(SW_HIDE); selectionModel.HideSelection(); }
BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override { POINT pt = { x, y }; imageModel.Clamp(pt); selectionModel.SetRectFromPoints(g_ptStart, pt);
BOOL bTextBoxShown = ::IsWindowVisible(textEditWindow); if (bTextBoxShown) { if (textEditWindow.GetWindowTextLength() > 0) { imageModel.PushImageForUndo(); draw(m_hdc); } if (selectionModel.m_rc.IsRectEmpty()) { quit(); return TRUE; } }
if (registrySettings.ShowTextTool) { if (!fontsDialog.IsWindow()) fontsDialog.Create(mainWindow);
fontsDialog.ShowWindow(SW_SHOWNOACTIVATE); }
CRect rc = selectionModel.m_rc;
// Enlarge if tool small INT cxMin = CX_MINTEXTEDIT, cyMin = CY_MINTEXTEDIT; if (selectionModel.m_rc.IsRectEmpty()) { rc.SetRect(x, y, x + cxMin, y + cyMin); } else { if (rc.right - rc.left < cxMin) rc.right = rc.left + cxMin; if (rc.bottom - rc.top < cyMin) rc.bottom = rc.top + cyMin; }
if (!textEditWindow.IsWindow()) textEditWindow.Create(canvasWindow);
textEditWindow.SetWindowText(NULL); textEditWindow.ValidateEditRect(&rc); textEditWindow.ShowWindow(SW_SHOWNOACTIVATE); textEditWindow.SetFocus(); return TRUE; }
void OnEndDraw(BOOL bCancel) override { if (!bCancel) { if (::IsWindowVisible(textEditWindow) && textEditWindow.GetWindowTextLength() > 0) { imageModel.PushImageForUndo(); draw(m_hdc); } } quit(); ToolBase::OnEndDraw(bCancel); }};
// TOOL_LINEstruct LineTool : TwoPointDrawTool{ void OnDrawOverlayOnImage(HDC hdc) override { if (!m_bDrawing) return; if (GetAsyncKeyState(VK_SHIFT) < 0) roundTo8Directions(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y); COLORREF rgb = m_bLeftButton ? m_fg : m_bg; Line(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, rgb, toolsModel.GetLineWidth()); }};
// TOOL_BEZIERstruct BezierTool : ToolBase{ BOOL m_bLeftButton = FALSE;
void OnDrawOverlayOnImage(HDC hdc) { COLORREF rgb = (m_bLeftButton ? m_fg : m_bg); switch (s_cPoints) { case 2: Line(hdc, s_pPoints[0].x, s_pPoints[0].y, s_pPoints[1].x, s_pPoints[1].y, rgb, toolsModel.GetLineWidth()); break; case 3: Bezier(hdc, s_pPoints[0], s_pPoints[2], s_pPoints[2], s_pPoints[1], rgb, toolsModel.GetLineWidth()); break; case 4: Bezier(hdc, s_pPoints[0], s_pPoints[2], s_pPoints[3], s_pPoints[1], rgb, toolsModel.GetLineWidth()); break; } }
void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override { m_bLeftButton = bLeftButton;
if (s_cPoints == 0) { pushToPoints(x, y); pushToPoints(x, y); } else { s_pPoints[s_cPoints - 1] = { x, y }; }
imageModel.NotifyImageChanged(); }
BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override { if (s_cPoints > 0) s_pPoints[s_cPoints - 1] = { x, y }; imageModel.NotifyImageChanged(); return TRUE; }
BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override { if (s_cPoints >= 4) { OnEndDraw(FALSE); return TRUE; } pushToPoints(x, y); imageModel.NotifyImageChanged(); return TRUE; }
void OnEndDraw(BOOL bCancel) override { if (!bCancel && s_cPoints > 1) { // FIXME: I couldn't calculate boundary rectangle from Bezier curve imageModel.PushImageForUndo(); OnDrawOverlayOnImage(m_hdc); } ToolBase::OnEndDraw(bCancel); }
void OnSpecialTweak(BOOL bMinus) override { toolsModel.MakeLineThickerOrThinner(bMinus); }};
// TOOL_RECTstruct RectTool : TwoPointDrawTool{ void OnDrawOverlayOnImage(HDC hdc) override { if (!m_bDrawing) return; if (GetAsyncKeyState(VK_SHIFT) < 0) regularize(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y); if (m_bLeftButton) Rect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle()); else Rect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle()); }};
// TOOL_SHAPEstruct ShapeTool : ToolBase{ BOOL m_bLeftButton = FALSE; BOOL m_bClosed = FALSE;
void OnDrawOverlayOnImage(HDC hdc) { if (s_cPoints <= 0) return;
if (m_bLeftButton) Poly(hdc, s_pPoints, (INT)s_cPoints, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), m_bClosed, FALSE); else Poly(hdc, s_pPoints, (INT)s_cPoints, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), m_bClosed, FALSE); }
void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override { m_bLeftButton = bLeftButton; m_bClosed = FALSE;
if ((s_cPoints > 0) && (GetAsyncKeyState(VK_SHIFT) < 0)) roundTo8Directions(s_pPoints[s_cPoints - 1].x, s_pPoints[s_cPoints - 1].y, x, y);
pushToPoints(x, y);
if (s_cPoints > 1 && bDoubleClick) { OnEndDraw(FALSE); return; }
if (s_cPoints == 1) pushToPoints(x, y); // We have to draw the first point
imageModel.NotifyImageChanged(); }
BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override { if (s_cPoints > 1) { if (GetAsyncKeyState(VK_SHIFT) < 0) roundTo8Directions(s_pPoints[s_cPoints - 2].x, s_pPoints[s_cPoints - 2].y, x, y);
s_pPoints[s_cPoints - 1] = { x, y }; }
imageModel.NotifyImageChanged(); return TRUE; }
BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override { if ((s_cPoints > 1) && (GetAsyncKeyState(VK_SHIFT) < 0)) roundTo8Directions(s_pPoints[s_cPoints - 2].x, s_pPoints[s_cPoints - 2].y, x, y);
m_bClosed = FALSE; if (nearlyEqualPoints(x, y, s_pPoints[0].x, s_pPoints[0].y)) { OnEndDraw(FALSE); return TRUE; }
pushToPoints(x, y); imageModel.NotifyImageChanged(); return TRUE; }
void OnEndDraw(BOOL bCancel) override { if (!bCancel && s_cPoints > 1) { CRect rcPartial; getBoundaryOfPoints(rcPartial, s_cPoints, s_pPoints);
SIZE size = toolsModel.GetToolSize(); rcPartial.InflateRect((size.cx + 1) / 2, (size.cy + 1) / 2);
imageModel.PushImageForUndo(rcPartial);
m_bClosed = TRUE; OnDrawOverlayOnImage(m_hdc); } m_bClosed = FALSE; ToolBase::OnEndDraw(bCancel); }
void OnSpecialTweak(BOOL bMinus) override { toolsModel.MakeLineThickerOrThinner(bMinus); }};
// TOOL_ELLIPSEstruct EllipseTool : TwoPointDrawTool{ void OnDrawOverlayOnImage(HDC hdc) override { if (!m_bDrawing) return; if (GetAsyncKeyState(VK_SHIFT) < 0) regularize(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y); if (m_bLeftButton) Ellp(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle()); else Ellp(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle()); }};
// TOOL_RRECTstruct RRectTool : TwoPointDrawTool{ void OnDrawOverlayOnImage(HDC hdc) override { if (!m_bDrawing) return; if (GetAsyncKeyState(VK_SHIFT) < 0) regularize(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y); if (m_bLeftButton) RRect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle()); else RRect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.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;}
void ToolsModel::OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick){ m_pToolObject->beginEvent(); g_ptEnd = g_ptStart = { x, y }; m_pToolObject->OnButtonDown(bLeftButton, x, y, bDoubleClick); m_pToolObject->endEvent();}
void ToolsModel::OnMouseMove(BOOL bLeftButton, LONG x, LONG y){ m_pToolObject->beginEvent(); if (m_pToolObject->OnMouseMove(bLeftButton, x, y)) g_ptEnd = { x, y };
m_pToolObject->endEvent();}
void ToolsModel::OnButtonUp(BOOL bLeftButton, LONG x, LONG y){ m_pToolObject->beginEvent(); if (m_pToolObject->OnButtonUp(bLeftButton, x, y)) g_ptEnd = { x, y };
m_pToolObject->endEvent();}
void ToolsModel::OnEndDraw(BOOL bCancel){ ATLTRACE("ToolsModel::OnEndDraw(%d)\n", bCancel); m_pToolObject->beginEvent(); m_pToolObject->OnEndDraw(bCancel); m_pToolObject->endEvent();}
void ToolsModel::OnDrawOverlayOnImage(HDC hdc){ m_pToolObject->OnDrawOverlayOnImage(hdc);}
void ToolsModel::OnDrawOverlayOnCanvas(HDC hdc){ m_pToolObject->OnDrawOverlayOnCanvas(hdc);}
void ToolsModel::SpecialTweak(BOOL bMinus){ m_pToolObject->OnSpecialTweak(bMinus);}
void ToolsModel::DrawWithMouseTool(POINT pt, WPARAM wParam){ LONG xRel = pt.x - g_ptStart.x, yRel = pt.y - g_ptStart.y;
switch (m_activeTool) { // freesel, rectsel and text tools always show numbers limited to fit into image area case TOOL_FREESEL: case TOOL_RECTSEL: case TOOL_TEXT: if (xRel < 0) xRel = (pt.x < 0) ? -g_ptStart.x : xRel; else if (pt.x > imageModel.GetWidth()) xRel = imageModel.GetWidth() - g_ptStart.x; if (yRel < 0) yRel = (pt.y < 0) ? -g_ptStart.y : yRel; else if (pt.y > imageModel.GetHeight()) yRel = imageModel.GetHeight() - g_ptStart.y; break;
// while drawing, update cursor coordinates only for tools 3, 7, 8, 9, 14 case TOOL_RUBBER: case TOOL_PEN: case TOOL_BRUSH: case TOOL_AIRBRUSH: case TOOL_SHAPE: { CStringW strCoord; strCoord.Format(L"%ld, %ld", pt.x, pt.y); ::SendMessageW(g_hStatusBar, SB_SETTEXT, 1, (LPARAM)(LPCWSTR)strCoord); break; } default: break; }
// rectsel and shape tools always show non-negative numbers when drawing if (m_activeTool == TOOL_RECTSEL || m_activeTool == TOOL_SHAPE) { xRel = labs(xRel); yRel = labs(yRel); }
if (wParam & MK_LBUTTON) { OnMouseMove(TRUE, pt.x, pt.y); canvasWindow.Invalidate(FALSE); if ((m_activeTool >= TOOL_TEXT) || IsSelection()) { CStringW strSize; if ((m_activeTool >= TOOL_LINE) && (GetAsyncKeyState(VK_SHIFT) < 0)) yRel = xRel; strSize.Format(L"%ld x %ld", xRel, yRel); ::SendMessageW(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)(LPCWSTR)strSize); } }
if (wParam & MK_RBUTTON) { OnMouseMove(FALSE, pt.x, pt.y); canvasWindow.Invalidate(FALSE); if (m_activeTool >= TOOL_TEXT) { CStringW strSize; if ((m_activeTool >= TOOL_LINE) && (GetAsyncKeyState(VK_SHIFT) < 0)) yRel = xRel; strSize.Format(L"%ld x %ld", xRel, yRel); ::SendMessageW(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)(LPCWSTR)strSize); } }}