1 /* 2 * PROJECT: PAINT for ReactOS 3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) 4 * PURPOSE: Providing the canvas window class 5 * COPYRIGHT: Copyright 2015 Benedikt Freisen <b.freisen@gmx.net> 6 */ 7 8 #include "precomp.h" 9 10 CCanvasWindow canvasWindow; 11 12 /* FUNCTIONS ********************************************************/ 13 14 CCanvasWindow::CCanvasWindow() 15 : m_drawing(FALSE) 16 , m_hitSelection(HIT_NONE) 17 , m_hitCanvasSizeBox(HIT_NONE) 18 , m_ptOrig { -1, -1 } 19 { 20 m_ahbmCached[0] = m_ahbmCached[1] = NULL; 21 ::SetRectEmpty(&m_rcResizing); 22 } 23 24 CCanvasWindow::~CCanvasWindow() 25 { 26 if (m_ahbmCached[0]) 27 ::DeleteObject(m_ahbmCached[0]); 28 if (m_ahbmCached[1]) 29 ::DeleteObject(m_ahbmCached[1]); 30 } 31 32 RECT CCanvasWindow::GetBaseRect() 33 { 34 CRect rcBase; 35 GetImageRect(rcBase); 36 ImageToCanvas(rcBase); 37 ::InflateRect(&rcBase, GRIP_SIZE, GRIP_SIZE); 38 return rcBase; 39 } 40 41 VOID CCanvasWindow::ImageToCanvas(POINT& pt) 42 { 43 pt.x = Zoomed(pt.x); 44 pt.y = Zoomed(pt.y); 45 pt.x += GRIP_SIZE - GetScrollPos(SB_HORZ); 46 pt.y += GRIP_SIZE - GetScrollPos(SB_VERT); 47 } 48 49 VOID CCanvasWindow::ImageToCanvas(RECT& rc) 50 { 51 rc.left = Zoomed(rc.left); 52 rc.top = Zoomed(rc.top); 53 rc.right = Zoomed(rc.right); 54 rc.bottom = Zoomed(rc.bottom); 55 ::OffsetRect(&rc, GRIP_SIZE - GetScrollPos(SB_HORZ), GRIP_SIZE - GetScrollPos(SB_VERT)); 56 } 57 58 VOID CCanvasWindow::CanvasToImage(POINT& pt, BOOL bZoomed) 59 { 60 pt.x -= GRIP_SIZE - GetScrollPos(SB_HORZ); 61 pt.y -= GRIP_SIZE - GetScrollPos(SB_VERT); 62 if (bZoomed) 63 return; 64 pt.x = UnZoomed(pt.x); 65 pt.y = UnZoomed(pt.y); 66 } 67 68 VOID CCanvasWindow::CanvasToImage(RECT& rc, BOOL bZoomed) 69 { 70 ::OffsetRect(&rc, GetScrollPos(SB_HORZ) - GRIP_SIZE, GetScrollPos(SB_VERT) - GRIP_SIZE); 71 if (bZoomed) 72 return; 73 rc.left = UnZoomed(rc.left); 74 rc.top = UnZoomed(rc.top); 75 rc.right = UnZoomed(rc.right); 76 rc.bottom = UnZoomed(rc.bottom); 77 } 78 79 VOID CCanvasWindow::GetImageRect(RECT& rc) 80 { 81 ::SetRect(&rc, 0, 0, imageModel.GetWidth(), imageModel.GetHeight()); 82 } 83 84 HITTEST CCanvasWindow::CanvasHitTest(POINT pt) 85 { 86 if (selectionModel.m_bShow || ::IsWindowVisible(textEditWindow)) 87 return HIT_INNER; 88 RECT rcBase = GetBaseRect(); 89 return getSizeBoxHitTest(pt, &rcBase); 90 } 91 92 VOID CCanvasWindow::getNewZoomRect(CRect& rcView, INT newZoom, CPoint ptTarget) 93 { 94 CRect rcImage; 95 GetImageRect(rcImage); 96 ImageToCanvas(rcImage); 97 98 // Calculate the zoom rectangle 99 INT oldZoom = toolsModel.GetZoom(); 100 GetClientRect(rcView); 101 LONG cxView = rcView.right * oldZoom / newZoom, cyView = rcView.bottom * oldZoom / newZoom; 102 ::SetRect(&rcView, ptTarget.x - cxView / 2, ptTarget.y - cyView / 2, 103 ptTarget.x + cxView / 2, ptTarget.y + cyView / 2); 104 105 // Shift the rectangle if necessary 106 INT dx = 0, dy = 0; 107 if (rcView.left < rcImage.left) 108 dx = rcImage.left - rcView.left; 109 else if (rcImage.right < rcView.right) 110 dx = rcImage.right - rcView.right; 111 if (rcView.top < rcImage.top) 112 dy = rcImage.top - rcView.top; 113 else if (rcImage.bottom < rcView.bottom) 114 dy = rcImage.bottom - rcView.bottom; 115 rcView.OffsetRect(dx, dy); 116 117 rcView.IntersectRect(&rcView, &rcImage); 118 } 119 120 VOID CCanvasWindow::zoomTo(INT newZoom, LONG left, LONG top) 121 { 122 POINT pt = { left, top }; 123 CanvasToImage(pt); 124 125 toolsModel.SetZoom(newZoom); 126 ImageToCanvas(pt); 127 pt.x += GetScrollPos(SB_HORZ); 128 pt.y += GetScrollPos(SB_VERT); 129 130 updateScrollRange(); 131 updateScrollPos(pt.x, pt.y); 132 Invalidate(TRUE); 133 } 134 135 VOID CCanvasWindow::DoDraw(HDC hDC, RECT& rcClient, RECT& rcPaint) 136 { 137 // This is the target area we have to draw on 138 CRect rcCanvasDraw; 139 rcCanvasDraw.IntersectRect(&rcClient, &rcPaint); 140 141 // We use a memory bitmap to reduce flickering 142 HDC hdcMem0 = ::CreateCompatibleDC(hDC); 143 m_ahbmCached[0] = CachedBufferDIB(m_ahbmCached[0], rcClient.right, rcClient.bottom); 144 HGDIOBJ hbm0Old = ::SelectObject(hdcMem0, m_ahbmCached[0]); 145 146 // Fill the background on hdcMem0 147 ::FillRect(hdcMem0, &rcCanvasDraw, (HBRUSH)(COLOR_APPWORKSPACE + 1)); 148 149 // Draw the sizeboxes if necessary 150 RECT rcBase = GetBaseRect(); 151 if (!selectionModel.m_bShow && !::IsWindowVisible(textEditWindow)) 152 drawSizeBoxes(hdcMem0, &rcBase, FALSE, &rcCanvasDraw); 153 154 // Calculate image size 155 CRect rcImage; 156 GetImageRect(rcImage); 157 SIZE sizeImage = { imageModel.GetWidth(), imageModel.GetHeight() }; 158 159 // Calculate the target area on the image 160 CRect rcImageDraw = rcCanvasDraw; 161 CanvasToImage(rcImageDraw); 162 rcImageDraw.IntersectRect(&rcImageDraw, &rcImage); 163 164 // Consider rounding down by zooming 165 rcImageDraw.right += 1; 166 rcImageDraw.bottom += 1; 167 168 // hdcMem1 <-- imageModel 169 HDC hdcMem1 = ::CreateCompatibleDC(hDC); 170 m_ahbmCached[1] = CachedBufferDIB(m_ahbmCached[1], sizeImage.cx, sizeImage.cy); 171 HGDIOBJ hbm1Old = ::SelectObject(hdcMem1, m_ahbmCached[1]); 172 ::BitBlt(hdcMem1, rcImageDraw.left, rcImageDraw.top, rcImageDraw.Width(), rcImageDraw.Height(), 173 imageModel.GetDC(), rcImageDraw.left, rcImageDraw.top, SRCCOPY); 174 175 // Draw overlay #1 on hdcMem1 176 toolsModel.OnDrawOverlayOnImage(hdcMem1); 177 178 // Transfer the bits with stretch (hdcMem0 <-- hdcMem1) 179 ImageToCanvas(rcImage); 180 ::StretchBlt(hdcMem0, rcImage.left, rcImage.top, rcImage.Width(), rcImage.Height(), 181 hdcMem1, 0, 0, sizeImage.cx, sizeImage.cy, SRCCOPY); 182 183 // Clean up hdcMem1 184 ::SelectObject(hdcMem1, hbm1Old); 185 ::DeleteDC(hdcMem1); 186 187 // Draw the grid on hdcMem0 188 if (g_showGrid && toolsModel.GetZoom() >= 4000) 189 { 190 HPEN oldPen = (HPEN) ::SelectObject(hdcMem0, ::CreatePen(PS_SOLID, 1, RGB(160, 160, 160))); 191 for (INT counter = 0; counter < sizeImage.cy; counter++) 192 { 193 POINT pt0 = { 0, counter }, pt1 = { sizeImage.cx, counter }; 194 ImageToCanvas(pt0); 195 ImageToCanvas(pt1); 196 ::MoveToEx(hdcMem0, pt0.x, pt0.y, NULL); 197 ::LineTo(hdcMem0, pt1.x, pt1.y); 198 } 199 for (INT counter = 0; counter < sizeImage.cx; counter++) 200 { 201 POINT pt0 = { counter, 0 }, pt1 = { counter, sizeImage.cy }; 202 ImageToCanvas(pt0); 203 ImageToCanvas(pt1); 204 ::MoveToEx(hdcMem0, pt0.x, pt0.y, NULL); 205 ::LineTo(hdcMem0, pt1.x, pt1.y); 206 } 207 ::DeleteObject(::SelectObject(hdcMem0, oldPen)); 208 } 209 210 // Draw overlay #2 on hdcMem0 211 toolsModel.OnDrawOverlayOnCanvas(hdcMem0); 212 213 // Draw new frame on hdcMem0 if any 214 if (m_hitCanvasSizeBox != HIT_NONE && !::IsRectEmpty(&m_rcResizing)) 215 DrawXorRect(hdcMem0, &m_rcResizing); 216 217 // Transfer the bits (hDC <-- hdcMem0) 218 ::BitBlt(hDC, rcCanvasDraw.left, rcCanvasDraw.top, rcCanvasDraw.Width(), rcCanvasDraw.Height(), 219 hdcMem0, rcCanvasDraw.left, rcCanvasDraw.top, SRCCOPY); 220 221 // Clean up hdcMem0 222 ::SelectObject(hdcMem0, hbm0Old); 223 ::DeleteDC(hdcMem0); 224 } 225 226 VOID CCanvasWindow::updateScrollRange() 227 { 228 CRect rcClient; 229 GetClientRect(&rcClient); 230 231 CSize sizePage(rcClient.right, rcClient.bottom); 232 CSize sizeZoomed = { Zoomed(imageModel.GetWidth()), Zoomed(imageModel.GetHeight()) }; 233 CSize sizeWhole = { sizeZoomed.cx + (GRIP_SIZE * 2), sizeZoomed.cy + (GRIP_SIZE * 2) }; 234 235 // show/hide the scrollbars 236 ShowScrollBar(SB_HORZ, sizePage.cx < sizeWhole.cx); 237 ShowScrollBar(SB_VERT, sizePage.cy < sizeWhole.cy); 238 239 if (sizePage.cx < sizeWhole.cx || sizePage.cy < sizeWhole.cy) 240 { 241 GetClientRect(&rcClient); // Scrollbars might change, get client rectangle again 242 sizePage = CSize(rcClient.right, rcClient.bottom); 243 } 244 245 SCROLLINFO si = { sizeof(si), SIF_PAGE | SIF_RANGE }; 246 si.nMin = 0; 247 248 si.nMax = sizeWhole.cx; 249 si.nPage = sizePage.cx; 250 SetScrollInfo(SB_HORZ, &si); 251 252 si.nMax = sizeWhole.cy; 253 si.nPage = sizePage.cy; 254 SetScrollInfo(SB_VERT, &si); 255 } 256 257 VOID CCanvasWindow::updateScrollPos(INT x, INT y) 258 { 259 SetScrollPos(SB_HORZ, x); 260 SetScrollPos(SB_VERT, y); 261 } 262 263 LRESULT CCanvasWindow::OnSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 264 { 265 if (m_hWnd) 266 updateScrollRange(); 267 268 return 0; 269 } 270 271 VOID CCanvasWindow::OnHVScroll(WPARAM wParam, INT fnBar) 272 { 273 SCROLLINFO si; 274 si.cbSize = sizeof(SCROLLINFO); 275 si.fMask = SIF_ALL; 276 GetScrollInfo(fnBar, &si); 277 switch (LOWORD(wParam)) 278 { 279 case SB_THUMBTRACK: 280 case SB_THUMBPOSITION: 281 si.nPos = (SHORT)HIWORD(wParam); 282 break; 283 case SB_LINELEFT: 284 si.nPos -= 5; 285 break; 286 case SB_LINERIGHT: 287 si.nPos += 5; 288 break; 289 case SB_PAGELEFT: 290 si.nPos -= si.nPage; 291 break; 292 case SB_PAGERIGHT: 293 si.nPos += si.nPage; 294 break; 295 } 296 si.nPos = max(min(si.nPos, si.nMax), si.nMin); 297 SetScrollInfo(fnBar, &si); 298 Invalidate(); 299 } 300 301 LRESULT CCanvasWindow::OnHScroll(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 302 { 303 OnHVScroll(wParam, SB_HORZ); 304 return 0; 305 } 306 307 LRESULT CCanvasWindow::OnVScroll(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 308 { 309 OnHVScroll(wParam, SB_VERT); 310 return 0; 311 } 312 313 LRESULT CCanvasWindow::OnButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 314 { 315 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; 316 317 m_nMouseDownMsg = nMsg; 318 BOOL bLeftButton = (nMsg == WM_LBUTTONDOWN); 319 320 if (nMsg == WM_MBUTTONDOWN) 321 { 322 m_ptOrig = pt; 323 SetCapture(); 324 ::SetCursor(::LoadCursor(g_hinstExe, MAKEINTRESOURCE(IDC_HANDDRAG))); 325 return 0; 326 } 327 328 HITTEST hitSelection = SelectionHitTest(pt); 329 if (hitSelection != HIT_NONE) 330 { 331 selectionModel.m_nSelectionBrush = 0; // Selection Brush is OFF 332 if (bLeftButton) 333 { 334 CanvasToImage(pt); 335 if (::GetKeyState(VK_CONTROL) < 0) // Ctrl+Click is Selection Clone 336 { 337 imageModel.SelectionClone(); 338 } 339 else if (::GetKeyState(VK_SHIFT) < 0) // Shift+Dragging is Selection Brush 340 { 341 selectionModel.m_nSelectionBrush = 1; // Selection Brush is ON 342 } 343 StartSelectionDrag(hitSelection, pt); 344 } 345 else 346 { 347 ClientToScreen(&pt); 348 mainWindow.TrackPopupMenu(pt, 0); 349 } 350 return 0; 351 } 352 353 HITTEST hit = CanvasHitTest(pt); 354 if (hit == HIT_NONE || hit == HIT_BORDER) 355 { 356 switch (toolsModel.GetActiveTool()) 357 { 358 case TOOL_BEZIER: 359 case TOOL_SHAPE: 360 toolsModel.OnCancelDraw(); 361 Invalidate(); 362 break; 363 364 case TOOL_FREESEL: 365 case TOOL_RECTSEL: 366 toolsModel.OnFinishDraw(); 367 Invalidate(); 368 break; 369 370 default: 371 break; 372 } 373 374 toolsModel.resetTool(); // resets the point-buffer of the polygon and bezier functions 375 return 0; 376 } 377 378 CanvasToImage(pt, TRUE); 379 380 if (hit == HIT_INNER) 381 { 382 m_drawing = TRUE; 383 UnZoomed(pt); 384 SetCapture(); 385 toolsModel.OnButtonDown(bLeftButton, pt.x, pt.y, FALSE); 386 Invalidate(FALSE); 387 return 0; 388 } 389 390 if (bLeftButton) 391 { 392 m_hitCanvasSizeBox = hit; 393 UnZoomed(pt); 394 m_ptOrig = pt; 395 SetCapture(); 396 } 397 return 0; 398 } 399 400 LRESULT CCanvasWindow::OnButtonDblClk(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 401 { 402 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; 403 CanvasToImage(pt); 404 405 m_drawing = FALSE; 406 ::ReleaseCapture(); 407 m_nMouseDownMsg = 0; 408 409 toolsModel.OnButtonDown(nMsg == WM_LBUTTONDBLCLK, pt.x, pt.y, TRUE); 410 toolsModel.resetTool(); 411 Invalidate(FALSE); 412 return 0; 413 } 414 415 LRESULT CCanvasWindow::OnMouseMove(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 416 { 417 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; 418 419 if (m_nMouseDownMsg == WM_MBUTTONDOWN) 420 { 421 INT x = GetScrollPos(SB_HORZ) - (pt.x - m_ptOrig.x); 422 INT y = GetScrollPos(SB_VERT) - (pt.y - m_ptOrig.y); 423 SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, x), 0); 424 SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBPOSITION, y), 0); 425 m_ptOrig = pt; 426 return 0; 427 } 428 429 CanvasToImage(pt); 430 431 if (toolsModel.GetActiveTool() == TOOL_ZOOM) 432 Invalidate(); 433 434 if (m_hitSelection != HIT_NONE) 435 { 436 SelectionDragging(pt); 437 return 0; 438 } 439 440 if (!m_drawing || toolsModel.GetActiveTool() <= TOOL_AIRBRUSH) 441 { 442 TRACKMOUSEEVENT tme = { sizeof(tme) }; 443 tme.dwFlags = TME_LEAVE; 444 tme.hwndTrack = m_hWnd; 445 tme.dwHoverTime = 0; 446 ::TrackMouseEvent(&tme); 447 448 if (!m_drawing) 449 { 450 RECT rcImage; 451 GetImageRect(rcImage); 452 453 CString strCoord; 454 if (::PtInRect(&rcImage, pt)) 455 strCoord.Format(_T("%ld, %ld"), pt.x, pt.y); 456 ::SendMessage(g_hStatusBar, SB_SETTEXT, 1, (LPARAM) (LPCTSTR) strCoord); 457 } 458 } 459 460 if (m_drawing) 461 { 462 // values displayed in statusbar 463 LONG xRel = pt.x - g_ptStart.x; 464 LONG yRel = pt.y - g_ptStart.y; 465 466 switch (toolsModel.GetActiveTool()) 467 { 468 // freesel, rectsel and text tools always show numbers limited to fit into image area 469 case TOOL_FREESEL: 470 case TOOL_RECTSEL: 471 case TOOL_TEXT: 472 if (xRel < 0) 473 xRel = (pt.x < 0) ? -g_ptStart.x : xRel; 474 else if (pt.x > imageModel.GetWidth()) 475 xRel = imageModel.GetWidth() - g_ptStart.x; 476 if (yRel < 0) 477 yRel = (pt.y < 0) ? -g_ptStart.y : yRel; 478 else if (pt.y > imageModel.GetHeight()) 479 yRel = imageModel.GetHeight() - g_ptStart.y; 480 break; 481 482 // while drawing, update cursor coordinates only for tools 3, 7, 8, 9, 14 483 case TOOL_RUBBER: 484 case TOOL_PEN: 485 case TOOL_BRUSH: 486 case TOOL_AIRBRUSH: 487 case TOOL_SHAPE: 488 { 489 CString strCoord; 490 strCoord.Format(_T("%ld, %ld"), pt.x, pt.y); 491 ::SendMessage(g_hStatusBar, SB_SETTEXT, 1, (LPARAM) (LPCTSTR) strCoord); 492 break; 493 } 494 default: 495 break; 496 } 497 498 // rectsel and shape tools always show non-negative numbers when drawing 499 if (toolsModel.GetActiveTool() == TOOL_RECTSEL || toolsModel.GetActiveTool() == TOOL_SHAPE) 500 { 501 if (xRel < 0) 502 xRel = -xRel; 503 if (yRel < 0) 504 yRel = -yRel; 505 } 506 507 if (wParam & MK_LBUTTON) 508 { 509 toolsModel.OnMouseMove(TRUE, pt.x, pt.y); 510 Invalidate(FALSE); 511 if ((toolsModel.GetActiveTool() >= TOOL_TEXT) || toolsModel.IsSelection()) 512 { 513 CString strSize; 514 if ((toolsModel.GetActiveTool() >= TOOL_LINE) && (GetAsyncKeyState(VK_SHIFT) < 0)) 515 yRel = xRel; 516 strSize.Format(_T("%ld x %ld"), xRel, yRel); 517 ::SendMessage(g_hStatusBar, SB_SETTEXT, 2, (LPARAM) (LPCTSTR) strSize); 518 } 519 } 520 521 if (wParam & MK_RBUTTON) 522 { 523 toolsModel.OnMouseMove(FALSE, pt.x, pt.y); 524 Invalidate(FALSE); 525 if (toolsModel.GetActiveTool() >= TOOL_TEXT) 526 { 527 CString strSize; 528 if ((toolsModel.GetActiveTool() >= TOOL_LINE) && (GetAsyncKeyState(VK_SHIFT) < 0)) 529 yRel = xRel; 530 strSize.Format(_T("%ld x %ld"), xRel, yRel); 531 ::SendMessage(g_hStatusBar, SB_SETTEXT, 2, (LPARAM) (LPCTSTR) strSize); 532 } 533 } 534 return 0; 535 } 536 537 if (m_hitCanvasSizeBox == HIT_NONE || ::GetCapture() != m_hWnd) 538 return 0; 539 540 // Dragging now... Calculate the new size 541 INT cxImage = imageModel.GetWidth(), cyImage = imageModel.GetHeight(); 542 INT cxDelta = pt.x - m_ptOrig.x; 543 INT cyDelta = pt.y - m_ptOrig.y; 544 switch (m_hitCanvasSizeBox) 545 { 546 case HIT_UPPER_LEFT: 547 cxImage -= cxDelta; 548 cyImage -= cyDelta; 549 break; 550 case HIT_UPPER_CENTER: 551 cyImage -= cyDelta; 552 break; 553 case HIT_UPPER_RIGHT: 554 cxImage += cxDelta; 555 cyImage -= cyDelta; 556 break; 557 case HIT_MIDDLE_LEFT: 558 cxImage -= cxDelta; 559 break; 560 case HIT_MIDDLE_RIGHT: 561 cxImage += cxDelta; 562 break; 563 case HIT_LOWER_LEFT: 564 cxImage -= cxDelta; 565 cyImage += cyDelta; 566 break; 567 case HIT_LOWER_CENTER: 568 cyImage += cyDelta; 569 break; 570 case HIT_LOWER_RIGHT: 571 cxImage += cxDelta; 572 cyImage += cyDelta; 573 break; 574 default: 575 return 0; 576 } 577 578 // Limit bitmap size 579 cxImage = max(1, cxImage); 580 cyImage = max(1, cyImage); 581 cxImage = min(MAXWORD, cxImage); 582 cyImage = min(MAXWORD, cyImage); 583 584 // Display new size 585 CString strSize; 586 strSize.Format(_T("%d x %d"), cxImage, cyImage); 587 ::SendMessage(g_hStatusBar, SB_SETTEXT, 2, (LPARAM) (LPCTSTR) strSize); 588 589 // Dragging now... Fix the position... 590 CRect rcResizing = { 0, 0, cxImage, cyImage }; 591 switch (m_hitCanvasSizeBox) 592 { 593 case HIT_UPPER_LEFT: 594 ::OffsetRect(&rcResizing, cxDelta, cyDelta); 595 break; 596 case HIT_UPPER_CENTER: 597 ::OffsetRect(&rcResizing, 0, cyDelta); 598 break; 599 case HIT_UPPER_RIGHT: 600 ::OffsetRect(&rcResizing, 0, cyDelta); 601 break; 602 case HIT_MIDDLE_LEFT: 603 ::OffsetRect(&rcResizing, cxDelta, 0); 604 break; 605 case HIT_LOWER_LEFT: 606 ::OffsetRect(&rcResizing, cxDelta, 0); 607 break; 608 default: 609 break; 610 } 611 ImageToCanvas(rcResizing); 612 m_rcResizing = rcResizing; 613 Invalidate(TRUE); 614 615 return 0; 616 } 617 618 LRESULT CCanvasWindow::OnButtonUp(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 619 { 620 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; 621 CanvasToImage(pt); 622 623 ::ReleaseCapture(); 624 625 BOOL bLeftButton = (m_nMouseDownMsg == WM_LBUTTONDOWN); 626 m_nMouseDownMsg = 0; 627 628 if (m_drawing) 629 { 630 m_drawing = FALSE; 631 toolsModel.OnButtonUp(bLeftButton, pt.x, pt.y); 632 Invalidate(FALSE); 633 ::SendMessage(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)_T("")); 634 return 0; 635 } 636 else if (m_hitSelection != HIT_NONE && bLeftButton) 637 { 638 EndSelectionDrag(pt); 639 return 0; 640 } 641 642 if (m_hitCanvasSizeBox == HIT_NONE || !bLeftButton) 643 return 0; 644 645 // Resize the image 646 INT cxImage = imageModel.GetWidth(), cyImage = imageModel.GetHeight(); 647 INT cxDelta = pt.x - m_ptOrig.x; 648 INT cyDelta = pt.y - m_ptOrig.y; 649 switch (m_hitCanvasSizeBox) 650 { 651 case HIT_UPPER_LEFT: 652 imageModel.Crop(cxImage - cxDelta, cyImage - cyDelta, cxDelta, cyDelta); 653 break; 654 case HIT_UPPER_CENTER: 655 imageModel.Crop(cxImage, cyImage - cyDelta, 0, cyDelta); 656 break; 657 case HIT_UPPER_RIGHT: 658 imageModel.Crop(cxImage + cxDelta, cyImage - cyDelta, 0, cyDelta); 659 break; 660 case HIT_MIDDLE_LEFT: 661 imageModel.Crop(cxImage - cxDelta, cyImage, cxDelta, 0); 662 break; 663 case HIT_MIDDLE_RIGHT: 664 imageModel.Crop(cxImage + cxDelta, cyImage, 0, 0); 665 break; 666 case HIT_LOWER_LEFT: 667 imageModel.Crop(cxImage - cxDelta, cyImage + cyDelta, cxDelta, 0); 668 break; 669 case HIT_LOWER_CENTER: 670 imageModel.Crop(cxImage, cyImage + cyDelta, 0, 0); 671 break; 672 case HIT_LOWER_RIGHT: 673 imageModel.Crop(cxImage + cxDelta, cyImage + cyDelta, 0, 0); 674 break; 675 default: 676 break; 677 } 678 ::SetRectEmpty(&m_rcResizing); 679 680 g_imageSaved = FALSE; 681 682 m_hitCanvasSizeBox = HIT_NONE; 683 toolsModel.resetTool(); // resets the point-buffer of the polygon and bezier functions 684 updateScrollRange(); 685 Invalidate(TRUE); 686 return 0; 687 } 688 689 LRESULT CCanvasWindow::OnSetCursor(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 690 { 691 if (CWaitCursor::IsWaiting()) 692 { 693 bHandled = FALSE; 694 return 0; 695 } 696 697 if (m_nMouseDownMsg == WM_MBUTTONDOWN) 698 { 699 ::SetCursor(::LoadCursor(g_hinstExe, MAKEINTRESOURCE(IDC_HANDDRAG))); 700 return 0; 701 } 702 703 POINT pt; 704 ::GetCursorPos(&pt); 705 ScreenToClient(&pt); 706 707 CRect rcClient; 708 GetClientRect(&rcClient); 709 710 if (!::PtInRect(&rcClient, pt)) 711 { 712 bHandled = FALSE; 713 return 0; 714 } 715 716 HITTEST hitSelection = SelectionHitTest(pt); 717 if (hitSelection != HIT_NONE) 718 { 719 if (!setCursorOnSizeBox(hitSelection)) 720 ::SetCursor(::LoadCursor(NULL, IDC_SIZEALL)); 721 return 0; 722 } 723 724 CRect rcImage; 725 GetImageRect(rcImage); 726 ImageToCanvas(rcImage); 727 728 if (::PtInRect(&rcImage, pt)) 729 { 730 switch (toolsModel.GetActiveTool()) 731 { 732 case TOOL_FILL: 733 ::SetCursor(::LoadIcon(g_hinstExe, MAKEINTRESOURCE(IDC_FILL))); 734 break; 735 case TOOL_COLOR: 736 ::SetCursor(::LoadIcon(g_hinstExe, MAKEINTRESOURCE(IDC_COLOR))); 737 break; 738 case TOOL_ZOOM: 739 ::SetCursor(::LoadIcon(g_hinstExe, MAKEINTRESOURCE(IDC_ZOOM))); 740 break; 741 case TOOL_PEN: 742 ::SetCursor(::LoadIcon(g_hinstExe, MAKEINTRESOURCE(IDC_PEN))); 743 break; 744 case TOOL_AIRBRUSH: 745 ::SetCursor(::LoadIcon(g_hinstExe, MAKEINTRESOURCE(IDC_AIRBRUSH))); 746 break; 747 default: 748 ::SetCursor(::LoadCursor(NULL, IDC_CROSS)); 749 } 750 return 0; 751 } 752 753 if (selectionModel.m_bShow || !setCursorOnSizeBox(CanvasHitTest(pt))) 754 bHandled = FALSE; 755 756 return 0; 757 } 758 759 LRESULT CCanvasWindow::OnKeyDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 760 { 761 if (wParam == VK_ESCAPE && ::GetCapture() == m_hWnd) 762 { 763 // Cancel dragging 764 ::ReleaseCapture(); 765 m_nMouseDownMsg = 0; 766 m_hitCanvasSizeBox = HIT_NONE; 767 ::SetRectEmpty(&m_rcResizing); 768 Invalidate(TRUE); 769 } 770 771 return 0; 772 } 773 774 LRESULT CCanvasWindow::OnCancelMode(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 775 { 776 // Cancel dragging 777 m_hitCanvasSizeBox = HIT_NONE; 778 ::SetRectEmpty(&m_rcResizing); 779 Invalidate(TRUE); 780 return 0; 781 } 782 783 LRESULT CCanvasWindow::OnMouseWheel(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 784 { 785 return ::SendMessage(GetParent(), nMsg, wParam, lParam); 786 } 787 788 LRESULT CCanvasWindow::OnCaptureChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 789 { 790 ::SendMessage(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)_T("")); 791 return 0; 792 } 793 794 LRESULT CCanvasWindow::OnEraseBkgnd(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 795 { 796 return TRUE; // do nothing => transparent background 797 } 798 799 LRESULT CCanvasWindow::OnPaint(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 800 { 801 RECT rcClient; 802 GetClientRect(&rcClient); 803 804 PAINTSTRUCT ps; 805 HDC hDC = BeginPaint(&ps); 806 DoDraw(hDC, rcClient, ps.rcPaint); 807 EndPaint(&ps); 808 return 0; 809 } 810 811 VOID CCanvasWindow::cancelDrawing() 812 { 813 selectionModel.ClearColorImage(); 814 selectionModel.ClearMaskImage(); 815 m_hitSelection = HIT_NONE; 816 m_drawing = FALSE; 817 toolsModel.OnCancelDraw(); 818 Invalidate(FALSE); 819 } 820 821 VOID CCanvasWindow::finishDrawing() 822 { 823 toolsModel.OnFinishDraw(); 824 m_drawing = FALSE; 825 Invalidate(FALSE); 826 } 827 828 HITTEST CCanvasWindow::SelectionHitTest(POINT ptImage) 829 { 830 if (!selectionModel.m_bShow) 831 return HIT_NONE; 832 833 RECT rcSelection = selectionModel.m_rc; 834 Zoomed(rcSelection); 835 ::OffsetRect(&rcSelection, GRIP_SIZE - GetScrollPos(SB_HORZ), GRIP_SIZE - GetScrollPos(SB_VERT)); 836 ::InflateRect(&rcSelection, GRIP_SIZE, GRIP_SIZE); 837 838 return getSizeBoxHitTest(ptImage, &rcSelection); 839 } 840 841 VOID CCanvasWindow::StartSelectionDrag(HITTEST hit, POINT ptImage) 842 { 843 m_hitSelection = hit; 844 selectionModel.m_ptHit = ptImage; 845 selectionModel.TakeOff(); 846 847 SetCapture(); 848 Invalidate(FALSE); 849 } 850 851 VOID CCanvasWindow::SelectionDragging(POINT ptImage) 852 { 853 if (selectionModel.m_nSelectionBrush) 854 { 855 imageModel.SelectionClone(selectionModel.m_nSelectionBrush == 1); 856 selectionModel.m_nSelectionBrush = 2; // Selection Brush is ON and drawn 857 } 858 859 selectionModel.Dragging(m_hitSelection, ptImage); 860 Invalidate(FALSE); 861 } 862 863 VOID CCanvasWindow::EndSelectionDrag(POINT ptImage) 864 { 865 selectionModel.Dragging(m_hitSelection, ptImage); 866 m_hitSelection = HIT_NONE; 867 Invalidate(FALSE); 868 } 869 870 VOID CCanvasWindow::MoveSelection(INT xDelta, INT yDelta) 871 { 872 if (!selectionModel.m_bShow) 873 return; 874 875 selectionModel.TakeOff(); 876 ::OffsetRect(&selectionModel.m_rc, xDelta, yDelta); 877 Invalidate(FALSE); 878 } 879 880 LRESULT CCanvasWindow::OnCtlColorEdit(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 881 { 882 SetTextColor((HDC)wParam, paletteModel.GetFgColor()); 883 SetBkMode((HDC)wParam, TRANSPARENT); 884 return (LRESULT)GetStockObject(NULL_BRUSH); 885 } 886 887 LRESULT CCanvasWindow::OnPaletteModelColorChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 888 { 889 imageModel.NotifyImageChanged(); 890 return 0; 891 } 892