reactos/base/applications/mspaint/canvas.cpp
Katayama Hirofumi MZ 29e147beca
[MSPAINT][ATL] Encapsulation: mainWindow (#5178)
- Add DoCreate methods to CFullscreenWindow, CMiniatureWindow, and CMainWindow classes.
- Do encapsulation around mainWindow and _tWinMain.
- Add GetOpenFileName, GetSaveFileName, and ChooseColor helper methods to CMainWindow class.
- Move some code in WinMain into CMainWindow::OnCreate.
- Delay creation of CFullscreenWindow and CMiniatureWindow.
- Extend ATL CImage class as CImageDx in newly-created atlimagedx.h of mspaint.
CORE-18867
2023-03-28 22:31:26 +09:00

346 lines
9.4 KiB
C++

/*
* PROJECT: PAINT for ReactOS
* LICENSE: LGPL
* FILE: base/applications/mspaint/canvas.cpp
* PURPOSE: Providing the canvas window class
* PROGRAMMERS: Benedikt Freisen
*/
#include "precomp.h"
CCanvasWindow canvasWindow;
/* FUNCTIONS ********************************************************/
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);
}
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);
if (imageArea.IsWindow() && hwndFrom != imageArea.m_hWnd)
{
INT dx = -GetScrollPos(SB_HORZ), dy = -GetScrollPos(SB_VERT);
imageArea.MoveWindow(dx + GRIP_SIZE, dy + GRIP_SIZE, sizeZoomed.cx, sizeZoomed.cy, TRUE);
}
}
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::OnLButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
CANVAS_HITTEST hit = HitTest(pt);
if (hit == HIT_NONE || hit == HIT_BORDER)
{
if (toolsModel.GetActiveTool() == TOOL_BEZIER ||
toolsModel.GetActiveTool() == TOOL_SHAPE)
{
if (ToolBase::pointSP != 0)
{
toolsModel.OnCancelDraw();
imageArea.Invalidate();
}
}
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;
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(hStatusBar, SB_SETTEXT, 2, (LPARAM) (LPCTSTR) strSize);
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;
default:
break;
}
// Finish dragging
m_whereHit = HIT_NONE;
toolsModel.resetTool(); // resets the point-buffer of the polygon and bezier functions
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);
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(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;
}