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