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