mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 06:15:26 +00:00
cb98e91b55
- s/CANVAS_HITTEST/HITTEST/ - s/m_whereHit/m_hitCanvasSizeBox/ - s/m_rcNew/m_rcResizing/ - Add comments to CCanvasWindow. CORE-18867
819 lines
23 KiB
C++
819 lines
23 KiB
C++
/*
|
|
* PROJECT: PAINT for ReactOS
|
|
* LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
|
|
* PURPOSE: Providing the canvas window class
|
|
* COPYRIGHT: Copyright 2009 Benedikt Freisen <b.freisen@gmx.net>
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
CCanvasWindow canvasWindow;
|
|
|
|
/* FUNCTIONS ********************************************************/
|
|
|
|
CCanvasWindow::CCanvasWindow()
|
|
: m_drawing(FALSE)
|
|
, m_hitSelection(HIT_NONE)
|
|
, m_hitCanvasSizeBox(HIT_NONE)
|
|
, m_ptOrig { -1, -1 }
|
|
{
|
|
m_ahbmCached[0] = m_ahbmCached[1] = NULL;
|
|
::SetRectEmpty(&m_rcResizing);
|
|
}
|
|
|
|
CCanvasWindow::~CCanvasWindow()
|
|
{
|
|
if (m_ahbmCached[0])
|
|
::DeleteObject(m_ahbmCached[0]);
|
|
if (m_ahbmCached[1])
|
|
::DeleteObject(m_ahbmCached[1]);
|
|
}
|
|
|
|
VOID CCanvasWindow::drawZoomFrame(INT mouseX, INT mouseY)
|
|
{
|
|
// FIXME: Draw the border of the area that is to be zoomed in
|
|
CRect rc;
|
|
GetImageRect(rc);
|
|
ImageToCanvas(rc);
|
|
|
|
HDC hdc = GetDC();
|
|
DrawXorRect(hdc, &rc);
|
|
ReleaseDC(hdc);
|
|
}
|
|
|
|
RECT CCanvasWindow::GetBaseRect()
|
|
{
|
|
CRect rcBase;
|
|
GetImageRect(rcBase);
|
|
ImageToCanvas(rcBase);
|
|
::InflateRect(&rcBase, GRIP_SIZE, GRIP_SIZE);
|
|
return rcBase;
|
|
}
|
|
|
|
VOID CCanvasWindow::ImageToCanvas(POINT& pt)
|
|
{
|
|
pt.x = Zoomed(pt.x);
|
|
pt.y = Zoomed(pt.y);
|
|
pt.x += GRIP_SIZE - GetScrollPos(SB_HORZ);
|
|
pt.y += GRIP_SIZE - GetScrollPos(SB_VERT);
|
|
}
|
|
|
|
VOID CCanvasWindow::ImageToCanvas(RECT& rc)
|
|
{
|
|
rc.left = Zoomed(rc.left);
|
|
rc.top = Zoomed(rc.top);
|
|
rc.right = Zoomed(rc.right);
|
|
rc.bottom = Zoomed(rc.bottom);
|
|
::OffsetRect(&rc, GRIP_SIZE - GetScrollPos(SB_HORZ), GRIP_SIZE - GetScrollPos(SB_VERT));
|
|
}
|
|
|
|
VOID CCanvasWindow::CanvasToImage(POINT& pt, BOOL bZoomed)
|
|
{
|
|
pt.x -= GRIP_SIZE - GetScrollPos(SB_HORZ);
|
|
pt.y -= GRIP_SIZE - GetScrollPos(SB_VERT);
|
|
if (bZoomed)
|
|
return;
|
|
pt.x = UnZoomed(pt.x);
|
|
pt.y = UnZoomed(pt.y);
|
|
}
|
|
|
|
VOID CCanvasWindow::CanvasToImage(RECT& rc, BOOL bZoomed)
|
|
{
|
|
::OffsetRect(&rc, GetScrollPos(SB_HORZ) - GRIP_SIZE, GetScrollPos(SB_VERT) - GRIP_SIZE);
|
|
if (bZoomed)
|
|
return;
|
|
rc.left = UnZoomed(rc.left);
|
|
rc.top = UnZoomed(rc.top);
|
|
rc.right = UnZoomed(rc.right);
|
|
rc.bottom = UnZoomed(rc.bottom);
|
|
}
|
|
|
|
VOID CCanvasWindow::GetImageRect(RECT& rc)
|
|
{
|
|
::SetRect(&rc, 0, 0, imageModel.GetWidth(), imageModel.GetHeight());
|
|
}
|
|
|
|
HITTEST CCanvasWindow::CanvasHitTest(POINT pt)
|
|
{
|
|
if (selectionModel.m_bShow || ::IsWindowVisible(textEditWindow))
|
|
return HIT_INNER;
|
|
RECT rcBase = GetBaseRect();
|
|
return getSizeBoxHitTest(pt, &rcBase);
|
|
}
|
|
|
|
VOID CCanvasWindow::DoDraw(HDC hDC, RECT& rcClient, RECT& rcPaint)
|
|
{
|
|
// We use a memory bitmap to reduce flickering
|
|
HDC hdcMem0 = ::CreateCompatibleDC(hDC);
|
|
m_ahbmCached[0] = CachedBufferDIB(m_ahbmCached[0], rcClient.right, rcClient.bottom);
|
|
HGDIOBJ hbm0Old = ::SelectObject(hdcMem0, m_ahbmCached[0]);
|
|
|
|
// Fill the background on hdcMem0
|
|
::FillRect(hdcMem0, &rcPaint, (HBRUSH)(COLOR_APPWORKSPACE + 1));
|
|
|
|
// Draw the sizeboxes if necessary
|
|
RECT rcBase = GetBaseRect();
|
|
if (!selectionModel.m_bShow && !::IsWindowVisible(textEditWindow))
|
|
drawSizeBoxes(hdcMem0, &rcBase, FALSE, &rcPaint);
|
|
|
|
// Calculate image size
|
|
CRect rcImage;
|
|
GetImageRect(rcImage);
|
|
SIZE sizeImage = { imageModel.GetWidth(), imageModel.GetHeight() };
|
|
|
|
// hdcMem1 <-- imageModel
|
|
HDC hdcMem1 = ::CreateCompatibleDC(hDC);
|
|
m_ahbmCached[1] = CachedBufferDIB(m_ahbmCached[1], sizeImage.cx, sizeImage.cy);
|
|
HGDIOBJ hbm1Old = ::SelectObject(hdcMem1, m_ahbmCached[1]);
|
|
BitBlt(hdcMem1, 0, 0, sizeImage.cx, sizeImage.cy, imageModel.GetDC(), 0, 0, SRCCOPY);
|
|
|
|
// Draw overlay #1 on hdcMem1
|
|
toolsModel.OnDrawOverlayOnImage(hdcMem1);
|
|
|
|
// Transfer the bits with stretch (hdcMem0 <-- hdcMem1)
|
|
ImageToCanvas(rcImage);
|
|
::StretchBlt(hdcMem0, rcImage.left, rcImage.top, rcImage.Width(), rcImage.Height(),
|
|
hdcMem1, 0, 0, sizeImage.cx, sizeImage.cy, SRCCOPY);
|
|
|
|
// Clean up hdcMem1
|
|
::SelectObject(hdcMem1, hbm1Old);
|
|
::DeleteDC(hdcMem1);
|
|
|
|
// Draw the grid on hdcMem0
|
|
if (g_showGrid && toolsModel.GetZoom() >= 4000)
|
|
{
|
|
HPEN oldPen = (HPEN) ::SelectObject(hdcMem0, ::CreatePen(PS_SOLID, 1, RGB(160, 160, 160)));
|
|
for (INT counter = 0; counter < sizeImage.cy; counter++)
|
|
{
|
|
POINT pt0 = { 0, counter }, pt1 = { sizeImage.cx, counter };
|
|
ImageToCanvas(pt0);
|
|
ImageToCanvas(pt1);
|
|
::MoveToEx(hdcMem0, pt0.x, pt0.y, NULL);
|
|
::LineTo(hdcMem0, pt1.x, pt1.y);
|
|
}
|
|
for (INT counter = 0; counter < sizeImage.cx; counter++)
|
|
{
|
|
POINT pt0 = { counter, 0 }, pt1 = { counter, sizeImage.cy };
|
|
ImageToCanvas(pt0);
|
|
ImageToCanvas(pt1);
|
|
::MoveToEx(hdcMem0, pt0.x, pt0.y, NULL);
|
|
::LineTo(hdcMem0, pt1.x, pt1.y);
|
|
}
|
|
::DeleteObject(::SelectObject(hdcMem0, oldPen));
|
|
}
|
|
|
|
// Draw overlay #2 on hdcMem0
|
|
toolsModel.OnDrawOverlayOnCanvas(hdcMem0);
|
|
|
|
// Draw new frame on hdcMem0 if any
|
|
if (m_hitCanvasSizeBox != HIT_NONE && !::IsRectEmpty(&m_rcResizing))
|
|
DrawXorRect(hdcMem0, &m_rcResizing);
|
|
|
|
// Transfer the bits (hDC <-- hdcMem0)
|
|
::BitBlt(hDC,
|
|
rcPaint.left, rcPaint.top,
|
|
rcPaint.right - rcPaint.left, rcPaint.bottom - rcPaint.top,
|
|
hdcMem0, rcPaint.left, rcPaint.top, SRCCOPY);
|
|
|
|
// Clean up hdcMem0
|
|
::SelectObject(hdcMem0, hbm0Old);
|
|
::DeleteDC(hdcMem0);
|
|
}
|
|
|
|
VOID CCanvasWindow::Update(HWND hwndFrom)
|
|
{
|
|
CRect rcClient;
|
|
GetClientRect(&rcClient);
|
|
|
|
CSize sizePage(rcClient.right, rcClient.bottom);
|
|
CSize sizeZoomed = { Zoomed(imageModel.GetWidth()), Zoomed(imageModel.GetHeight()) };
|
|
CSize sizeWhole = { sizeZoomed.cx + (GRIP_SIZE * 2), sizeZoomed.cy + (GRIP_SIZE * 2) };
|
|
|
|
// show/hide the scrollbars
|
|
ShowScrollBar(SB_HORZ, sizePage.cx < sizeWhole.cx);
|
|
ShowScrollBar(SB_VERT, sizePage.cy < sizeWhole.cy);
|
|
|
|
if (sizePage.cx < sizeWhole.cx || sizePage.cy < sizeWhole.cy)
|
|
{
|
|
GetClientRect(&rcClient); // Scrollbars might change, get client rectangle again
|
|
sizePage = CSize(rcClient.right, rcClient.bottom);
|
|
}
|
|
|
|
SCROLLINFO si = { sizeof(si), SIF_PAGE | SIF_RANGE };
|
|
si.nMin = 0;
|
|
|
|
si.nMax = sizeWhole.cx;
|
|
si.nPage = sizePage.cx;
|
|
SetScrollInfo(SB_HORZ, &si);
|
|
|
|
si.nMax = sizeWhole.cy;
|
|
si.nPage = sizePage.cy;
|
|
SetScrollInfo(SB_VERT, &si);
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
if (m_hWnd)
|
|
Update(m_hWnd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
VOID CCanvasWindow::OnHVScroll(WPARAM wParam, INT fnBar)
|
|
{
|
|
SCROLLINFO si;
|
|
si.cbSize = sizeof(SCROLLINFO);
|
|
si.fMask = SIF_ALL;
|
|
GetScrollInfo(fnBar, &si);
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case SB_THUMBTRACK:
|
|
case SB_THUMBPOSITION:
|
|
si.nPos = HIWORD(wParam);
|
|
break;
|
|
case SB_LINELEFT:
|
|
si.nPos -= 5;
|
|
break;
|
|
case SB_LINERIGHT:
|
|
si.nPos += 5;
|
|
break;
|
|
case SB_PAGELEFT:
|
|
si.nPos -= si.nPage;
|
|
break;
|
|
case SB_PAGERIGHT:
|
|
si.nPos += si.nPage;
|
|
break;
|
|
}
|
|
SetScrollInfo(fnBar, &si);
|
|
Update(m_hWnd);
|
|
Invalidate(FALSE); // FIXME: Flicker
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnHScroll(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
OnHVScroll(wParam, SB_HORZ);
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnVScroll(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
OnHVScroll(wParam, SB_VERT);
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnLRButtonDown(BOOL bLeftButton, UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
|
|
HITTEST hitSelection = SelectionHitTest(pt);
|
|
if (hitSelection != HIT_NONE)
|
|
{
|
|
if (bLeftButton)
|
|
{
|
|
CanvasToImage(pt);
|
|
StartSelectionDrag(hitSelection, pt);
|
|
}
|
|
else
|
|
{
|
|
canvasWindow.ClientToScreen(&pt);
|
|
mainWindow.TrackPopupMenu(pt, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
HITTEST hit = CanvasHitTest(pt);
|
|
if (hit == HIT_NONE || hit == HIT_BORDER)
|
|
{
|
|
switch (toolsModel.GetActiveTool())
|
|
{
|
|
case TOOL_BEZIER:
|
|
case TOOL_SHAPE:
|
|
toolsModel.OnCancelDraw();
|
|
canvasWindow.Invalidate();
|
|
break;
|
|
|
|
case TOOL_FREESEL:
|
|
case TOOL_RECTSEL:
|
|
toolsModel.OnFinishDraw();
|
|
canvasWindow.Invalidate();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
toolsModel.resetTool(); // resets the point-buffer of the polygon and bezier functions
|
|
return 0;
|
|
}
|
|
|
|
CanvasToImage(pt, TRUE);
|
|
|
|
if (hit == HIT_INNER)
|
|
{
|
|
m_drawing = TRUE;
|
|
UnZoomed(pt);
|
|
SetCapture();
|
|
toolsModel.OnButtonDown(bLeftButton, pt.x, pt.y, FALSE);
|
|
Invalidate(FALSE);
|
|
return 0;
|
|
}
|
|
|
|
if (bLeftButton)
|
|
{
|
|
m_hitCanvasSizeBox = hit;
|
|
UnZoomed(pt);
|
|
m_ptOrig = pt;
|
|
SetCapture();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnLButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
return OnLRButtonDown(TRUE, nMsg, wParam, lParam, bHandled);
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnRButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
return OnLRButtonDown(FALSE, nMsg, wParam, lParam, bHandled);
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnLRButtonDblClk(BOOL bLeftButton, UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
CanvasToImage(pt);
|
|
|
|
m_drawing = FALSE;
|
|
ReleaseCapture();
|
|
|
|
toolsModel.OnButtonDown(bLeftButton, pt.x, pt.y, TRUE);
|
|
toolsModel.resetTool();
|
|
Invalidate(FALSE);
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnLButtonDblClk(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
return OnLRButtonDblClk(TRUE, nMsg, wParam, lParam, bHandled);
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnRButtonDblClk(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
return OnLRButtonDblClk(FALSE, nMsg, wParam, lParam, bHandled);
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnMouseMove(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
CanvasToImage(pt);
|
|
|
|
if (m_hitSelection != HIT_NONE)
|
|
{
|
|
SelectionDragging(pt);
|
|
return 0;
|
|
}
|
|
|
|
if (!m_drawing || toolsModel.GetActiveTool() <= TOOL_AIRBRUSH)
|
|
{
|
|
if (toolsModel.GetActiveTool() == TOOL_ZOOM)
|
|
{
|
|
Invalidate(FALSE);
|
|
UpdateWindow();
|
|
CanvasToImage(pt);
|
|
drawZoomFrame(pt.x, pt.y);
|
|
}
|
|
|
|
TRACKMOUSEEVENT tme = { sizeof(tme) };
|
|
tme.dwFlags = TME_LEAVE;
|
|
tme.hwndTrack = m_hWnd;
|
|
tme.dwHoverTime = 0;
|
|
::TrackMouseEvent(&tme);
|
|
|
|
if (!m_drawing)
|
|
{
|
|
CString strCoord;
|
|
strCoord.Format(_T("%ld, %ld"), pt.x, pt.y);
|
|
::SendMessage(g_hStatusBar, SB_SETTEXT, 1, (LPARAM) (LPCTSTR) strCoord);
|
|
}
|
|
}
|
|
|
|
if (m_drawing)
|
|
{
|
|
// values displayed in statusbar
|
|
LONG xRel = pt.x - g_ptStart.x;
|
|
LONG yRel = pt.y - g_ptStart.y;
|
|
|
|
switch (toolsModel.GetActiveTool())
|
|
{
|
|
// 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:
|
|
{
|
|
CString strCoord;
|
|
strCoord.Format(_T("%ld, %ld"), pt.x, pt.y);
|
|
::SendMessage(g_hStatusBar, SB_SETTEXT, 1, (LPARAM) (LPCTSTR) strCoord);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// rectsel and shape tools always show non-negative numbers when drawing
|
|
if (toolsModel.GetActiveTool() == TOOL_RECTSEL || toolsModel.GetActiveTool() == TOOL_SHAPE)
|
|
{
|
|
if (xRel < 0)
|
|
xRel = -xRel;
|
|
if (yRel < 0)
|
|
yRel = -yRel;
|
|
}
|
|
|
|
if (wParam & MK_LBUTTON)
|
|
{
|
|
toolsModel.OnMouseMove(TRUE, pt.x, pt.y);
|
|
Invalidate(FALSE);
|
|
if ((toolsModel.GetActiveTool() >= TOOL_TEXT) || toolsModel.IsSelection())
|
|
{
|
|
CString strSize;
|
|
if ((toolsModel.GetActiveTool() >= TOOL_LINE) && (GetAsyncKeyState(VK_SHIFT) < 0))
|
|
yRel = xRel;
|
|
strSize.Format(_T("%ld x %ld"), xRel, yRel);
|
|
::SendMessage(g_hStatusBar, SB_SETTEXT, 2, (LPARAM) (LPCTSTR) strSize);
|
|
}
|
|
}
|
|
|
|
if (wParam & MK_RBUTTON)
|
|
{
|
|
toolsModel.OnMouseMove(FALSE, pt.x, pt.y);
|
|
Invalidate(FALSE);
|
|
if (toolsModel.GetActiveTool() >= TOOL_TEXT)
|
|
{
|
|
CString strSize;
|
|
if ((toolsModel.GetActiveTool() >= TOOL_LINE) && (GetAsyncKeyState(VK_SHIFT) < 0))
|
|
yRel = xRel;
|
|
strSize.Format(_T("%ld x %ld"), xRel, yRel);
|
|
::SendMessage(g_hStatusBar, SB_SETTEXT, 2, (LPARAM) (LPCTSTR) strSize);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (m_hitCanvasSizeBox == HIT_NONE || ::GetCapture() != m_hWnd)
|
|
return 0;
|
|
|
|
// Dragging now... Calculate the new size
|
|
INT cxImage = imageModel.GetWidth(), cyImage = imageModel.GetHeight();
|
|
INT cxDelta = pt.x - m_ptOrig.x;
|
|
INT cyDelta = pt.y - m_ptOrig.y;
|
|
switch (m_hitCanvasSizeBox)
|
|
{
|
|
case HIT_UPPER_LEFT:
|
|
cxImage -= cxDelta;
|
|
cyImage -= cyDelta;
|
|
break;
|
|
case HIT_UPPER_CENTER:
|
|
cyImage -= cyDelta;
|
|
break;
|
|
case HIT_UPPER_RIGHT:
|
|
cxImage += cxDelta;
|
|
cyImage -= cyDelta;
|
|
break;
|
|
case HIT_MIDDLE_LEFT:
|
|
cxImage -= cxDelta;
|
|
break;
|
|
case HIT_MIDDLE_RIGHT:
|
|
cxImage += cxDelta;
|
|
break;
|
|
case HIT_LOWER_LEFT:
|
|
cxImage -= cxDelta;
|
|
cyImage += cyDelta;
|
|
break;
|
|
case HIT_LOWER_CENTER:
|
|
cyImage += cyDelta;
|
|
break;
|
|
case HIT_LOWER_RIGHT:
|
|
cxImage += cxDelta;
|
|
cyImage += cyDelta;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
// Limit bitmap size
|
|
cxImage = max(1, cxImage);
|
|
cyImage = max(1, cyImage);
|
|
cxImage = min(MAXWORD, cxImage);
|
|
cyImage = min(MAXWORD, cyImage);
|
|
|
|
// Display new size
|
|
CString strSize;
|
|
strSize.Format(_T("%d x %d"), cxImage, cyImage);
|
|
::SendMessage(g_hStatusBar, SB_SETTEXT, 2, (LPARAM) (LPCTSTR) strSize);
|
|
|
|
// Dragging now... Fix the position...
|
|
CRect rcResizing = { 0, 0, cxImage, cyImage };
|
|
switch (m_hitCanvasSizeBox)
|
|
{
|
|
case HIT_UPPER_LEFT:
|
|
::OffsetRect(&rcResizing, cxDelta, cyDelta);
|
|
break;
|
|
case HIT_UPPER_CENTER:
|
|
::OffsetRect(&rcResizing, 0, cyDelta);
|
|
break;
|
|
case HIT_UPPER_RIGHT:
|
|
::OffsetRect(&rcResizing, 0, cyDelta);
|
|
break;
|
|
case HIT_MIDDLE_LEFT:
|
|
::OffsetRect(&rcResizing, cxDelta, 0);
|
|
break;
|
|
case HIT_LOWER_LEFT:
|
|
::OffsetRect(&rcResizing, cxDelta, 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
ImageToCanvas(rcResizing);
|
|
m_rcResizing = rcResizing;
|
|
Invalidate(TRUE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnLRButtonUp(BOOL bLeftButton, UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
CanvasToImage(pt);
|
|
|
|
::ReleaseCapture();
|
|
|
|
if (m_drawing)
|
|
{
|
|
m_drawing = FALSE;
|
|
toolsModel.OnButtonUp(bLeftButton, pt.x, pt.y);
|
|
Invalidate(FALSE);
|
|
::SendMessage(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)_T(""));
|
|
return 0;
|
|
}
|
|
else if (m_hitSelection != HIT_NONE && bLeftButton)
|
|
{
|
|
EndSelectionDrag(pt);
|
|
return 0;
|
|
}
|
|
|
|
if (m_hitCanvasSizeBox == HIT_NONE || !bLeftButton)
|
|
return 0;
|
|
|
|
// Resize the image
|
|
INT cxImage = imageModel.GetWidth(), cyImage = imageModel.GetHeight();
|
|
INT cxDelta = pt.x - m_ptOrig.x;
|
|
INT cyDelta = pt.y - m_ptOrig.y;
|
|
switch (m_hitCanvasSizeBox)
|
|
{
|
|
case HIT_UPPER_LEFT:
|
|
imageModel.Crop(cxImage - cxDelta, cyImage - cyDelta, cxDelta, cyDelta);
|
|
break;
|
|
case HIT_UPPER_CENTER:
|
|
imageModel.Crop(cxImage, cyImage - cyDelta, 0, cyDelta);
|
|
break;
|
|
case HIT_UPPER_RIGHT:
|
|
imageModel.Crop(cxImage + cxDelta, cyImage - cyDelta, 0, cyDelta);
|
|
break;
|
|
case HIT_MIDDLE_LEFT:
|
|
imageModel.Crop(cxImage - cxDelta, cyImage, cxDelta, 0);
|
|
break;
|
|
case HIT_MIDDLE_RIGHT:
|
|
imageModel.Crop(cxImage + cxDelta, cyImage, 0, 0);
|
|
break;
|
|
case HIT_LOWER_LEFT:
|
|
imageModel.Crop(cxImage - cxDelta, cyImage + cyDelta, cxDelta, 0);
|
|
break;
|
|
case HIT_LOWER_CENTER:
|
|
imageModel.Crop(cxImage, cyImage + cyDelta, 0, 0);
|
|
break;
|
|
case HIT_LOWER_RIGHT:
|
|
imageModel.Crop(cxImage + cxDelta, cyImage + cyDelta, 0, 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
::SetRectEmpty(&m_rcResizing);
|
|
|
|
g_imageSaved = FALSE;
|
|
|
|
m_hitCanvasSizeBox = HIT_NONE;
|
|
toolsModel.resetTool(); // resets the point-buffer of the polygon and bezier functions
|
|
Update(NULL);
|
|
Invalidate(TRUE);
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnLButtonUp(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
return OnLRButtonUp(TRUE, nMsg, wParam, lParam, bHandled);
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnRButtonUp(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
return OnLRButtonUp(FALSE, nMsg, wParam, lParam, bHandled);
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnSetCursor(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
POINT pt;
|
|
::GetCursorPos(&pt);
|
|
ScreenToClient(&pt);
|
|
|
|
CRect rcClient;
|
|
GetClientRect(&rcClient);
|
|
|
|
if (!::PtInRect(&rcClient, pt))
|
|
{
|
|
bHandled = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
HITTEST hitSelection = SelectionHitTest(pt);
|
|
if (hitSelection != HIT_NONE)
|
|
{
|
|
if (!setCursorOnSizeBox(hitSelection))
|
|
::SetCursor(::LoadCursor(NULL, IDC_SIZEALL));
|
|
return 0;
|
|
}
|
|
|
|
CRect rcImage;
|
|
GetImageRect(rcImage);
|
|
ImageToCanvas(rcImage);
|
|
|
|
if (::PtInRect(&rcImage, pt))
|
|
{
|
|
switch (toolsModel.GetActiveTool())
|
|
{
|
|
case TOOL_FILL:
|
|
::SetCursor(::LoadIcon(g_hinstExe, MAKEINTRESOURCE(IDC_FILL)));
|
|
break;
|
|
case TOOL_COLOR:
|
|
::SetCursor(::LoadIcon(g_hinstExe, MAKEINTRESOURCE(IDC_COLOR)));
|
|
break;
|
|
case TOOL_ZOOM:
|
|
::SetCursor(::LoadIcon(g_hinstExe, MAKEINTRESOURCE(IDC_ZOOM)));
|
|
break;
|
|
case TOOL_PEN:
|
|
::SetCursor(::LoadIcon(g_hinstExe, MAKEINTRESOURCE(IDC_PEN)));
|
|
break;
|
|
case TOOL_AIRBRUSH:
|
|
::SetCursor(::LoadIcon(g_hinstExe, MAKEINTRESOURCE(IDC_AIRBRUSH)));
|
|
break;
|
|
default:
|
|
::SetCursor(::LoadCursor(NULL, IDC_CROSS));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (selectionModel.m_bShow || !setCursorOnSizeBox(CanvasHitTest(pt)))
|
|
bHandled = FALSE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnKeyDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
if (wParam == VK_ESCAPE && ::GetCapture() == m_hWnd)
|
|
{
|
|
// Cancel dragging
|
|
::ReleaseCapture();
|
|
m_hitCanvasSizeBox = HIT_NONE;
|
|
::SetRectEmpty(&m_rcResizing);
|
|
Invalidate(TRUE);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnCancelMode(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
// Cancel dragging
|
|
m_hitCanvasSizeBox = HIT_NONE;
|
|
::SetRectEmpty(&m_rcResizing);
|
|
Invalidate(TRUE);
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnMouseWheel(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
return ::SendMessage(GetParent(), nMsg, wParam, lParam);
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnCaptureChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
::SendMessage(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)_T(""));
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnEraseBkgnd(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
return TRUE; // do nothing => transparent background
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnPaint(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
RECT rcClient;
|
|
GetClientRect(&rcClient);
|
|
|
|
PAINTSTRUCT ps;
|
|
HDC hDC = BeginPaint(&ps);
|
|
DoDraw(hDC, rcClient, ps.rcPaint);
|
|
EndPaint(&ps);
|
|
return 0;
|
|
}
|
|
|
|
VOID CCanvasWindow::cancelDrawing()
|
|
{
|
|
selectionModel.ClearColor();
|
|
selectionModel.ClearMask();
|
|
m_hitSelection = HIT_NONE;
|
|
m_drawing = FALSE;
|
|
toolsModel.OnCancelDraw();
|
|
Invalidate(FALSE);
|
|
}
|
|
|
|
VOID CCanvasWindow::finishDrawing()
|
|
{
|
|
toolsModel.OnFinishDraw();
|
|
m_drawing = FALSE;
|
|
Invalidate(FALSE);
|
|
}
|
|
|
|
HITTEST CCanvasWindow::SelectionHitTest(POINT ptImage)
|
|
{
|
|
if (!selectionModel.m_bShow)
|
|
return HIT_NONE;
|
|
|
|
RECT rcSelection = selectionModel.m_rc;
|
|
Zoomed(rcSelection);
|
|
::OffsetRect(&rcSelection, GRIP_SIZE - GetScrollPos(SB_HORZ), GRIP_SIZE - GetScrollPos(SB_VERT));
|
|
::InflateRect(&rcSelection, GRIP_SIZE, GRIP_SIZE);
|
|
|
|
return getSizeBoxHitTest(ptImage, &rcSelection);
|
|
}
|
|
|
|
VOID CCanvasWindow::StartSelectionDrag(HITTEST hit, POINT ptImage)
|
|
{
|
|
m_hitSelection = hit;
|
|
selectionModel.m_ptHit = ptImage;
|
|
selectionModel.TakeOff();
|
|
|
|
SetCapture();
|
|
Invalidate(FALSE);
|
|
}
|
|
|
|
VOID CCanvasWindow::SelectionDragging(POINT ptImage)
|
|
{
|
|
selectionModel.Dragging(m_hitSelection, ptImage);
|
|
Invalidate(FALSE);
|
|
}
|
|
|
|
VOID CCanvasWindow::EndSelectionDrag(POINT ptImage)
|
|
{
|
|
selectionModel.Dragging(m_hitSelection, ptImage);
|
|
m_hitSelection = HIT_NONE;
|
|
Invalidate(FALSE);
|
|
}
|
|
|
|
VOID CCanvasWindow::MoveSelection(INT xDelta, INT yDelta)
|
|
{
|
|
if (!selectionModel.m_bShow)
|
|
return;
|
|
|
|
selectionModel.TakeOff();
|
|
::OffsetRect(&selectionModel.m_rc, xDelta, yDelta);
|
|
Invalidate(FALSE);
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnCtlColorEdit(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
SetTextColor((HDC)wParam, paletteModel.GetFgColor());
|
|
SetBkMode((HDC)wParam, TRANSPARENT);
|
|
return (LRESULT)GetStockObject(NULL_BRUSH);
|
|
}
|
|
|
|
LRESULT CCanvasWindow::OnPaletteModelColorChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
{
|
|
imageModel.NotifyImageChanged();
|
|
return 0;
|
|
}
|