2015-05-08 16:02:36 +00:00
|
|
|
/*
|
|
|
|
* PROJECT: PAINT for ReactOS
|
|
|
|
* LICENSE: LGPL
|
2023-03-16 22:45:14 +00:00
|
|
|
* FILE: base/applications/mspaint/canvas.cpp
|
|
|
|
* PURPOSE: Providing the canvas window class
|
2015-05-08 16:02:36 +00:00
|
|
|
* PROGRAMMERS: Benedikt Freisen
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *********************************************************/
|
|
|
|
|
|
|
|
#include "precomp.h"
|
2018-11-16 22:05:51 +00:00
|
|
|
|
2015-05-08 16:02:36 +00:00
|
|
|
/* FUNCTIONS ********************************************************/
|
|
|
|
|
2023-03-19 01:42:10 +00:00
|
|
|
CCanvasWindow::CCanvasWindow()
|
|
|
|
: m_whereHit(HIT_NONE)
|
|
|
|
, m_ptOrig { 0, 0 }
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
RECT CCanvasWindow::GetBaseRect()
|
|
|
|
{
|
|
|
|
CRect rcBase = { 0, 0, Zoomed(imageModel.GetWidth()), Zoomed(imageModel.GetHeight()) };
|
|
|
|
::InflateRect(&rcBase, GRIP_SIZE, GRIP_SIZE);
|
|
|
|
::OffsetRect(&rcBase, GRIP_SIZE - GetScrollPos(SB_HORZ), GRIP_SIZE - GetScrollPos(SB_VERT));
|
|
|
|
return rcBase;
|
|
|
|
}
|
|
|
|
|
|
|
|
CANVAS_HITTEST CCanvasWindow::HitTest(POINT pt)
|
|
|
|
{
|
|
|
|
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 hdcMem = ::CreateCompatibleDC(hDC);
|
|
|
|
HBITMAP hbm = ::CreateCompatibleBitmap(hDC, rcClient.right, rcClient.bottom);
|
|
|
|
HGDIOBJ hbmOld = ::SelectObject(hdcMem, hbm);
|
|
|
|
|
|
|
|
// Fill the background
|
|
|
|
::FillRect(hdcMem, &rcPaint, (HBRUSH)(COLOR_APPWORKSPACE + 1));
|
|
|
|
|
|
|
|
// Draw the sizeboxes
|
|
|
|
RECT rcBase = GetBaseRect();
|
|
|
|
drawSizeBoxes(hdcMem, &rcBase, FALSE, &rcPaint);
|
|
|
|
|
|
|
|
// Transfer the bits
|
|
|
|
::BitBlt(hDC,
|
|
|
|
rcPaint.left, rcPaint.top,
|
|
|
|
rcPaint.right - rcPaint.left, rcPaint.bottom - rcPaint.top,
|
|
|
|
hdcMem, rcPaint.left, rcPaint.top, SRCCOPY);
|
|
|
|
|
|
|
|
::SelectObject(hdcMem, hbmOld);
|
|
|
|
::DeleteDC(hdcMem);
|
|
|
|
}
|
|
|
|
|
2023-03-16 22:51:12 +00:00
|
|
|
VOID CCanvasWindow::Update(HWND hwndFrom)
|
2015-05-08 16:02:36 +00:00
|
|
|
{
|
2023-03-19 01:42:10 +00:00
|
|
|
CRect rcClient;
|
|
|
|
GetClientRect(&rcClient);
|
2018-11-16 22:05:51 +00:00
|
|
|
|
2023-03-19 01:42:10 +00:00
|
|
|
CSize sizePage(rcClient.right, rcClient.bottom);
|
2023-03-16 22:28:42 +00:00
|
|
|
CSize sizeZoomed = { Zoomed(imageModel.GetWidth()), Zoomed(imageModel.GetHeight()) };
|
|
|
|
CSize sizeWhole = { sizeZoomed.cx + (GRIP_SIZE * 2), sizeZoomed.cy + (GRIP_SIZE * 2) };
|
2018-11-16 22:05:51 +00:00
|
|
|
|
|
|
|
/* show/hide the scrollbars */
|
2023-03-19 01:42:10 +00:00
|
|
|
ShowScrollBar(SB_HORZ, sizePage.cx < sizeWhole.cx);
|
|
|
|
ShowScrollBar(SB_VERT, sizePage.cy < sizeWhole.cy);
|
2018-11-16 22:05:51 +00:00
|
|
|
|
2023-03-19 01:42:10 +00:00
|
|
|
if (sizePage.cx < sizeWhole.cx || sizePage.cy < sizeWhole.cy)
|
2023-03-16 22:28:42 +00:00
|
|
|
{
|
2023-03-19 01:42:10 +00:00
|
|
|
GetClientRect(&rcClient); // Scrollbars might change, get client rectangle again
|
|
|
|
sizePage = CSize(rcClient.right, rcClient.bottom);
|
2023-03-16 22:28:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SCROLLINFO si = { sizeof(si), SIF_PAGE | SIF_RANGE };
|
2015-05-08 16:02:36 +00:00
|
|
|
si.nMin = 0;
|
2018-11-16 22:05:51 +00:00
|
|
|
|
2023-03-16 22:28:42 +00:00
|
|
|
si.nMax = sizeWhole.cx;
|
2023-03-19 01:42:10 +00:00
|
|
|
si.nPage = sizePage.cx;
|
2023-03-16 22:51:12 +00:00
|
|
|
SetScrollInfo(SB_HORZ, &si);
|
2018-11-16 22:05:51 +00:00
|
|
|
|
2023-03-16 22:28:42 +00:00
|
|
|
si.nMax = sizeWhole.cy;
|
2023-03-19 01:42:10 +00:00
|
|
|
si.nPage = sizePage.cy;
|
2023-03-16 22:51:12 +00:00
|
|
|
SetScrollInfo(SB_VERT, &si);
|
2018-11-16 22:05:51 +00:00
|
|
|
|
2023-03-16 22:28:42 +00:00
|
|
|
if (imageArea.IsWindow() && hwndFrom != imageArea.m_hWnd)
|
2023-03-19 01:42:10 +00:00
|
|
|
{
|
|
|
|
INT dx = -GetScrollPos(SB_HORZ), dy = -GetScrollPos(SB_VERT);
|
2023-03-16 22:28:42 +00:00
|
|
|
imageArea.MoveWindow(dx + GRIP_SIZE, dy + GRIP_SIZE, sizeZoomed.cx, sizeZoomed.cy, TRUE);
|
2023-03-19 01:42:10 +00:00
|
|
|
}
|
2015-05-08 16:02:36 +00:00
|
|
|
}
|
|
|
|
|
2023-03-16 22:45:14 +00:00
|
|
|
LRESULT CCanvasWindow::OnSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
2015-05-08 16:02:36 +00:00
|
|
|
{
|
2023-03-16 22:28:42 +00:00
|
|
|
if (m_hWnd)
|
2023-03-16 22:51:12 +00:00
|
|
|
Update(m_hWnd);
|
2023-03-19 01:42:10 +00:00
|
|
|
|
2015-07-07 10:42:49 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2015-05-08 16:02:36 +00:00
|
|
|
|
2023-03-19 01:42:10 +00:00
|
|
|
VOID CCanvasWindow::OnHVScroll(WPARAM wParam, INT fnBar)
|
2015-07-07 10:42:49 +00:00
|
|
|
{
|
2023-03-16 22:28:42 +00:00
|
|
|
SCROLLINFO si;
|
|
|
|
si.cbSize = sizeof(SCROLLINFO);
|
|
|
|
si.fMask = SIF_ALL;
|
2023-03-19 01:42:10 +00:00
|
|
|
GetScrollInfo(fnBar, &si);
|
2023-03-16 22:28:42 +00:00
|
|
|
switch (LOWORD(wParam))
|
2015-07-07 10:42:49 +00:00
|
|
|
{
|
2023-03-16 22:28:42 +00:00
|
|
|
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;
|
2015-05-08 16:02:36 +00:00
|
|
|
}
|
2023-03-19 01:42:10 +00:00
|
|
|
SetScrollInfo(fnBar, &si);
|
2023-03-16 22:51:12 +00:00
|
|
|
Update(m_hWnd);
|
2023-03-19 01:42:10 +00:00
|
|
|
Invalidate(FALSE); // FIXME: Flicker
|
|
|
|
}
|
|
|
|
|
|
|
|
LRESULT CCanvasWindow::OnHScroll(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
|
|
{
|
|
|
|
OnHVScroll(wParam, SB_HORZ);
|
2015-07-07 10:42:49 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2015-05-08 16:02:36 +00:00
|
|
|
|
2023-03-16 22:45:14 +00:00
|
|
|
LRESULT CCanvasWindow::OnVScroll(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
2015-07-07 10:42:49 +00:00
|
|
|
{
|
2023-03-19 01:42:10 +00:00
|
|
|
OnHVScroll(wParam, SB_VERT);
|
2015-05-08 16:02:36 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2017-06-19 13:02:09 +00:00
|
|
|
|
2023-03-16 22:45:14 +00:00
|
|
|
LRESULT CCanvasWindow::OnLButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
2017-06-19 13:02:09 +00:00
|
|
|
{
|
2023-03-19 01:42:10 +00:00
|
|
|
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
|
|
CANVAS_HITTEST hit = HitTest(pt);
|
|
|
|
|
|
|
|
if (hit == HIT_NONE || hit == HIT_BORDER)
|
2022-01-01 13:02:36 +00:00
|
|
|
{
|
2023-03-19 01:42:10 +00:00
|
|
|
if (toolsModel.GetActiveTool() == TOOL_BEZIER ||
|
|
|
|
toolsModel.GetActiveTool() == TOOL_SHAPE)
|
|
|
|
{
|
2022-01-01 13:02:36 +00:00
|
|
|
if (ToolBase::pointSP != 0)
|
|
|
|
{
|
|
|
|
toolsModel.OnCancelDraw();
|
|
|
|
imageArea.Invalidate();
|
|
|
|
}
|
2023-03-19 01:42:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
toolsModel.resetTool(); // resets the point-buffer of the polygon and bezier functions
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hit == HIT_INNER)
|
|
|
|
{
|
|
|
|
// TODO: In the future, we handle the events of the window-less image area.
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start dragging
|
|
|
|
m_whereHit = hit;
|
|
|
|
m_ptOrig = pt;
|
|
|
|
SetCapture();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
LRESULT CCanvasWindow::OnMouseMove(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
|
|
{
|
|
|
|
if (m_whereHit == HIT_NONE || ::GetCapture() != m_hWnd)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Dragging now... Calculate the new size
|
|
|
|
INT cxImage = imageModel.GetWidth(), cyImage = imageModel.GetHeight();
|
|
|
|
INT cxDelta = UnZoomed(GET_X_LPARAM(lParam) - m_ptOrig.x);
|
|
|
|
INT cyDelta = UnZoomed(GET_Y_LPARAM(lParam) - m_ptOrig.y);
|
|
|
|
switch (m_whereHit)
|
|
|
|
{
|
|
|
|
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;
|
2022-01-01 13:02:36 +00:00
|
|
|
break;
|
2023-03-19 01:42:10 +00:00
|
|
|
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(hStatusBar, SB_SETTEXT, 2, (LPARAM) (LPCTSTR) strSize);
|
2022-01-01 13:02:36 +00:00
|
|
|
|
2023-03-19 01:42:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
LRESULT CCanvasWindow::OnLButtonUp(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
|
|
{
|
|
|
|
::ReleaseCapture();
|
|
|
|
|
|
|
|
if (m_whereHit == HIT_NONE)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Resize the image
|
|
|
|
INT cxImage = imageModel.GetWidth(), cyImage = imageModel.GetHeight();
|
|
|
|
INT cxDelta = UnZoomed(GET_X_LPARAM(lParam) - m_ptOrig.x);
|
|
|
|
INT cyDelta = UnZoomed(GET_Y_LPARAM(lParam) - m_ptOrig.y);
|
|
|
|
switch (m_whereHit)
|
|
|
|
{
|
|
|
|
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;
|
2022-01-01 13:02:36 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-03-19 01:42:10 +00:00
|
|
|
// Finish dragging
|
|
|
|
m_whereHit = HIT_NONE;
|
2022-01-01 13:02:36 +00:00
|
|
|
toolsModel.resetTool(); // resets the point-buffer of the polygon and bezier functions
|
2023-03-19 01:42:10 +00:00
|
|
|
Update(NULL);
|
|
|
|
Invalidate(TRUE);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
LRESULT CCanvasWindow::OnSetCursor(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
|
|
{
|
|
|
|
POINT pt;
|
|
|
|
::GetCursorPos(&pt);
|
|
|
|
ScreenToClient(&pt);
|
|
|
|
|
|
|
|
if (!setCursorOnSizeBox(HitTest(pt)))
|
|
|
|
::SetCursor(::LoadCursor(NULL, IDC_ARROW));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
LRESULT CCanvasWindow::OnKeyDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
|
|
{
|
|
|
|
if (wParam == VK_ESCAPE && ::GetCapture() == m_hWnd)
|
|
|
|
{
|
|
|
|
// Cancel dragging
|
|
|
|
m_whereHit = HIT_NONE;
|
|
|
|
::ReleaseCapture();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
LRESULT CCanvasWindow::OnCancelMode(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
|
|
{
|
|
|
|
// Cancel dragging
|
|
|
|
m_whereHit = HIT_NONE;
|
|
|
|
Invalidate(TRUE);
|
2017-06-19 13:02:09 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2021-12-27 13:20:22 +00:00
|
|
|
|
2023-03-16 22:45:14 +00:00
|
|
|
LRESULT CCanvasWindow::OnMouseWheel(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
2021-12-27 13:20:22 +00:00
|
|
|
{
|
|
|
|
return ::SendMessage(GetParent(), nMsg, wParam, lParam);
|
|
|
|
}
|
2023-03-19 01:42:10 +00:00
|
|
|
|
|
|
|
LRESULT CCanvasWindow::OnCaptureChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
|
|
{
|
|
|
|
SendMessage(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;
|
|
|
|
}
|