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 selectionModel.m_nSelectionBrush = 0; // Selection Brush is OFF 272 if (bLeftButton) 273 { 274 CanvasToImage(pt); 275 if (::GetKeyState(VK_CONTROL) < 0) // Ctrl+Click is Selection Clone 276 { 277 imageModel.SelectionClone(); 278 } 279 else if (::GetKeyState(VK_SHIFT) < 0) // Shift+Dragging is Selection Brush 280 { 281 selectionModel.m_nSelectionBrush = 1; // Selection Brush is ON 282 } 283 StartSelectionDrag(hitSelection, pt); 284 } 285 else 286 { 287 canvasWindow.ClientToScreen(&pt); 288 mainWindow.TrackPopupMenu(pt, 0); 289 } 290 return 0; 291 } 292 293 HITTEST hit = CanvasHitTest(pt); 294 if (hit == HIT_NONE || hit == HIT_BORDER) 295 { 296 switch (toolsModel.GetActiveTool()) 297 { 298 case TOOL_BEZIER: 299 case TOOL_SHAPE: 300 toolsModel.OnCancelDraw(); 301 canvasWindow.Invalidate(); 302 break; 303 304 case TOOL_FREESEL: 305 case TOOL_RECTSEL: 306 toolsModel.OnFinishDraw(); 307 canvasWindow.Invalidate(); 308 break; 309 310 default: 311 break; 312 } 313 314 toolsModel.resetTool(); // resets the point-buffer of the polygon and bezier functions 315 return 0; 316 } 317 318 CanvasToImage(pt, TRUE); 319 320 if (hit == HIT_INNER) 321 { 322 m_drawing = TRUE; 323 UnZoomed(pt); 324 SetCapture(); 325 toolsModel.OnButtonDown(bLeftButton, pt.x, pt.y, FALSE); 326 Invalidate(FALSE); 327 return 0; 328 } 329 330 if (bLeftButton) 331 { 332 m_hitCanvasSizeBox = hit; 333 UnZoomed(pt); 334 m_ptOrig = pt; 335 SetCapture(); 336 } 337 return 0; 338 } 339 340 LRESULT CCanvasWindow::OnLButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 341 { 342 return OnLRButtonDown(TRUE, nMsg, wParam, lParam, bHandled); 343 } 344 345 LRESULT CCanvasWindow::OnRButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 346 { 347 return OnLRButtonDown(FALSE, nMsg, wParam, lParam, bHandled); 348 } 349 350 LRESULT CCanvasWindow::OnLRButtonDblClk(BOOL bLeftButton, UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 351 { 352 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; 353 CanvasToImage(pt); 354 355 m_drawing = FALSE; 356 ReleaseCapture(); 357 358 toolsModel.OnButtonDown(bLeftButton, pt.x, pt.y, TRUE); 359 toolsModel.resetTool(); 360 Invalidate(FALSE); 361 return 0; 362 } 363 364 LRESULT CCanvasWindow::OnLButtonDblClk(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 365 { 366 return OnLRButtonDblClk(TRUE, nMsg, wParam, lParam, bHandled); 367 } 368 369 LRESULT CCanvasWindow::OnRButtonDblClk(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 370 { 371 return OnLRButtonDblClk(FALSE, nMsg, wParam, lParam, bHandled); 372 } 373 374 LRESULT CCanvasWindow::OnMouseMove(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 375 { 376 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; 377 CanvasToImage(pt); 378 379 if (m_hitSelection != HIT_NONE) 380 { 381 SelectionDragging(pt); 382 return 0; 383 } 384 385 if (!m_drawing || toolsModel.GetActiveTool() <= TOOL_AIRBRUSH) 386 { 387 if (toolsModel.GetActiveTool() == TOOL_ZOOM) 388 { 389 Invalidate(FALSE); 390 UpdateWindow(); 391 CanvasToImage(pt); 392 drawZoomFrame(pt.x, pt.y); 393 } 394 395 TRACKMOUSEEVENT tme = { sizeof(tme) }; 396 tme.dwFlags = TME_LEAVE; 397 tme.hwndTrack = m_hWnd; 398 tme.dwHoverTime = 0; 399 ::TrackMouseEvent(&tme); 400 401 if (!m_drawing) 402 { 403 CString strCoord; 404 strCoord.Format(_T("%ld, %ld"), pt.x, pt.y); 405 ::SendMessage(g_hStatusBar, SB_SETTEXT, 1, (LPARAM) (LPCTSTR) strCoord); 406 } 407 } 408 409 if (m_drawing) 410 { 411 // values displayed in statusbar 412 LONG xRel = pt.x - g_ptStart.x; 413 LONG yRel = pt.y - g_ptStart.y; 414 415 switch (toolsModel.GetActiveTool()) 416 { 417 // freesel, rectsel and text tools always show numbers limited to fit into image area 418 case TOOL_FREESEL: 419 case TOOL_RECTSEL: 420 case TOOL_TEXT: 421 if (xRel < 0) 422 xRel = (pt.x < 0) ? -g_ptStart.x : xRel; 423 else if (pt.x > imageModel.GetWidth()) 424 xRel = imageModel.GetWidth() - g_ptStart.x; 425 if (yRel < 0) 426 yRel = (pt.y < 0) ? -g_ptStart.y : yRel; 427 else if (pt.y > imageModel.GetHeight()) 428 yRel = imageModel.GetHeight() - g_ptStart.y; 429 break; 430 431 // while drawing, update cursor coordinates only for tools 3, 7, 8, 9, 14 432 case TOOL_RUBBER: 433 case TOOL_PEN: 434 case TOOL_BRUSH: 435 case TOOL_AIRBRUSH: 436 case TOOL_SHAPE: 437 { 438 CString strCoord; 439 strCoord.Format(_T("%ld, %ld"), pt.x, pt.y); 440 ::SendMessage(g_hStatusBar, SB_SETTEXT, 1, (LPARAM) (LPCTSTR) strCoord); 441 break; 442 } 443 default: 444 break; 445 } 446 447 // rectsel and shape tools always show non-negative numbers when drawing 448 if (toolsModel.GetActiveTool() == TOOL_RECTSEL || toolsModel.GetActiveTool() == TOOL_SHAPE) 449 { 450 if (xRel < 0) 451 xRel = -xRel; 452 if (yRel < 0) 453 yRel = -yRel; 454 } 455 456 if (wParam & MK_LBUTTON) 457 { 458 toolsModel.OnMouseMove(TRUE, pt.x, pt.y); 459 Invalidate(FALSE); 460 if ((toolsModel.GetActiveTool() >= TOOL_TEXT) || toolsModel.IsSelection()) 461 { 462 CString strSize; 463 if ((toolsModel.GetActiveTool() >= TOOL_LINE) && (GetAsyncKeyState(VK_SHIFT) < 0)) 464 yRel = xRel; 465 strSize.Format(_T("%ld x %ld"), xRel, yRel); 466 ::SendMessage(g_hStatusBar, SB_SETTEXT, 2, (LPARAM) (LPCTSTR) strSize); 467 } 468 } 469 470 if (wParam & MK_RBUTTON) 471 { 472 toolsModel.OnMouseMove(FALSE, pt.x, pt.y); 473 Invalidate(FALSE); 474 if (toolsModel.GetActiveTool() >= TOOL_TEXT) 475 { 476 CString strSize; 477 if ((toolsModel.GetActiveTool() >= TOOL_LINE) && (GetAsyncKeyState(VK_SHIFT) < 0)) 478 yRel = xRel; 479 strSize.Format(_T("%ld x %ld"), xRel, yRel); 480 ::SendMessage(g_hStatusBar, SB_SETTEXT, 2, (LPARAM) (LPCTSTR) strSize); 481 } 482 } 483 return 0; 484 } 485 486 if (m_hitCanvasSizeBox == HIT_NONE || ::GetCapture() != m_hWnd) 487 return 0; 488 489 // Dragging now... Calculate the new size 490 INT cxImage = imageModel.GetWidth(), cyImage = imageModel.GetHeight(); 491 INT cxDelta = pt.x - m_ptOrig.x; 492 INT cyDelta = pt.y - m_ptOrig.y; 493 switch (m_hitCanvasSizeBox) 494 { 495 case HIT_UPPER_LEFT: 496 cxImage -= cxDelta; 497 cyImage -= cyDelta; 498 break; 499 case HIT_UPPER_CENTER: 500 cyImage -= cyDelta; 501 break; 502 case HIT_UPPER_RIGHT: 503 cxImage += cxDelta; 504 cyImage -= cyDelta; 505 break; 506 case HIT_MIDDLE_LEFT: 507 cxImage -= cxDelta; 508 break; 509 case HIT_MIDDLE_RIGHT: 510 cxImage += cxDelta; 511 break; 512 case HIT_LOWER_LEFT: 513 cxImage -= cxDelta; 514 cyImage += cyDelta; 515 break; 516 case HIT_LOWER_CENTER: 517 cyImage += cyDelta; 518 break; 519 case HIT_LOWER_RIGHT: 520 cxImage += cxDelta; 521 cyImage += cyDelta; 522 break; 523 default: 524 return 0; 525 } 526 527 // Limit bitmap size 528 cxImage = max(1, cxImage); 529 cyImage = max(1, cyImage); 530 cxImage = min(MAXWORD, cxImage); 531 cyImage = min(MAXWORD, cyImage); 532 533 // Display new size 534 CString strSize; 535 strSize.Format(_T("%d x %d"), cxImage, cyImage); 536 ::SendMessage(g_hStatusBar, SB_SETTEXT, 2, (LPARAM) (LPCTSTR) strSize); 537 538 // Dragging now... Fix the position... 539 CRect rcResizing = { 0, 0, cxImage, cyImage }; 540 switch (m_hitCanvasSizeBox) 541 { 542 case HIT_UPPER_LEFT: 543 ::OffsetRect(&rcResizing, cxDelta, cyDelta); 544 break; 545 case HIT_UPPER_CENTER: 546 ::OffsetRect(&rcResizing, 0, cyDelta); 547 break; 548 case HIT_UPPER_RIGHT: 549 ::OffsetRect(&rcResizing, 0, cyDelta); 550 break; 551 case HIT_MIDDLE_LEFT: 552 ::OffsetRect(&rcResizing, cxDelta, 0); 553 break; 554 case HIT_LOWER_LEFT: 555 ::OffsetRect(&rcResizing, cxDelta, 0); 556 break; 557 default: 558 break; 559 } 560 ImageToCanvas(rcResizing); 561 m_rcResizing = rcResizing; 562 Invalidate(TRUE); 563 564 return 0; 565 } 566 567 LRESULT CCanvasWindow::OnLRButtonUp(BOOL bLeftButton, UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 568 { 569 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; 570 CanvasToImage(pt); 571 572 ::ReleaseCapture(); 573 574 if (m_drawing) 575 { 576 m_drawing = FALSE; 577 toolsModel.OnButtonUp(bLeftButton, pt.x, pt.y); 578 Invalidate(FALSE); 579 ::SendMessage(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)_T("")); 580 return 0; 581 } 582 else if (m_hitSelection != HIT_NONE && bLeftButton) 583 { 584 EndSelectionDrag(pt); 585 return 0; 586 } 587 588 if (m_hitCanvasSizeBox == HIT_NONE || !bLeftButton) 589 return 0; 590 591 // Resize the image 592 INT cxImage = imageModel.GetWidth(), cyImage = imageModel.GetHeight(); 593 INT cxDelta = pt.x - m_ptOrig.x; 594 INT cyDelta = pt.y - m_ptOrig.y; 595 switch (m_hitCanvasSizeBox) 596 { 597 case HIT_UPPER_LEFT: 598 imageModel.Crop(cxImage - cxDelta, cyImage - cyDelta, cxDelta, cyDelta); 599 break; 600 case HIT_UPPER_CENTER: 601 imageModel.Crop(cxImage, cyImage - cyDelta, 0, cyDelta); 602 break; 603 case HIT_UPPER_RIGHT: 604 imageModel.Crop(cxImage + cxDelta, cyImage - cyDelta, 0, cyDelta); 605 break; 606 case HIT_MIDDLE_LEFT: 607 imageModel.Crop(cxImage - cxDelta, cyImage, cxDelta, 0); 608 break; 609 case HIT_MIDDLE_RIGHT: 610 imageModel.Crop(cxImage + cxDelta, cyImage, 0, 0); 611 break; 612 case HIT_LOWER_LEFT: 613 imageModel.Crop(cxImage - cxDelta, cyImage + cyDelta, cxDelta, 0); 614 break; 615 case HIT_LOWER_CENTER: 616 imageModel.Crop(cxImage, cyImage + cyDelta, 0, 0); 617 break; 618 case HIT_LOWER_RIGHT: 619 imageModel.Crop(cxImage + cxDelta, cyImage + cyDelta, 0, 0); 620 break; 621 default: 622 break; 623 } 624 ::SetRectEmpty(&m_rcResizing); 625 626 g_imageSaved = FALSE; 627 628 m_hitCanvasSizeBox = HIT_NONE; 629 toolsModel.resetTool(); // resets the point-buffer of the polygon and bezier functions 630 Update(NULL); 631 Invalidate(TRUE); 632 return 0; 633 } 634 635 LRESULT CCanvasWindow::OnLButtonUp(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 636 { 637 return OnLRButtonUp(TRUE, nMsg, wParam, lParam, bHandled); 638 } 639 640 LRESULT CCanvasWindow::OnRButtonUp(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 641 { 642 return OnLRButtonUp(FALSE, nMsg, wParam, lParam, bHandled); 643 } 644 645 LRESULT CCanvasWindow::OnSetCursor(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 646 { 647 if (CWaitCursor::IsWaiting()) 648 { 649 bHandled = FALSE; 650 return 0; 651 } 652 653 POINT pt; 654 ::GetCursorPos(&pt); 655 ScreenToClient(&pt); 656 657 CRect rcClient; 658 GetClientRect(&rcClient); 659 660 if (!::PtInRect(&rcClient, pt)) 661 { 662 bHandled = FALSE; 663 return 0; 664 } 665 666 HITTEST hitSelection = SelectionHitTest(pt); 667 if (hitSelection != HIT_NONE) 668 { 669 if (!setCursorOnSizeBox(hitSelection)) 670 ::SetCursor(::LoadCursor(NULL, IDC_SIZEALL)); 671 return 0; 672 } 673 674 CRect rcImage; 675 GetImageRect(rcImage); 676 ImageToCanvas(rcImage); 677 678 if (::PtInRect(&rcImage, pt)) 679 { 680 switch (toolsModel.GetActiveTool()) 681 { 682 case TOOL_FILL: 683 ::SetCursor(::LoadIcon(g_hinstExe, MAKEINTRESOURCE(IDC_FILL))); 684 break; 685 case TOOL_COLOR: 686 ::SetCursor(::LoadIcon(g_hinstExe, MAKEINTRESOURCE(IDC_COLOR))); 687 break; 688 case TOOL_ZOOM: 689 ::SetCursor(::LoadIcon(g_hinstExe, MAKEINTRESOURCE(IDC_ZOOM))); 690 break; 691 case TOOL_PEN: 692 ::SetCursor(::LoadIcon(g_hinstExe, MAKEINTRESOURCE(IDC_PEN))); 693 break; 694 case TOOL_AIRBRUSH: 695 ::SetCursor(::LoadIcon(g_hinstExe, MAKEINTRESOURCE(IDC_AIRBRUSH))); 696 break; 697 default: 698 ::SetCursor(::LoadCursor(NULL, IDC_CROSS)); 699 } 700 return 0; 701 } 702 703 if (selectionModel.m_bShow || !setCursorOnSizeBox(CanvasHitTest(pt))) 704 bHandled = FALSE; 705 706 return 0; 707 } 708 709 LRESULT CCanvasWindow::OnKeyDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 710 { 711 if (wParam == VK_ESCAPE && ::GetCapture() == m_hWnd) 712 { 713 // Cancel dragging 714 ::ReleaseCapture(); 715 m_hitCanvasSizeBox = HIT_NONE; 716 ::SetRectEmpty(&m_rcResizing); 717 Invalidate(TRUE); 718 } 719 720 return 0; 721 } 722 723 LRESULT CCanvasWindow::OnCancelMode(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 724 { 725 // Cancel dragging 726 m_hitCanvasSizeBox = HIT_NONE; 727 ::SetRectEmpty(&m_rcResizing); 728 Invalidate(TRUE); 729 return 0; 730 } 731 732 LRESULT CCanvasWindow::OnMouseWheel(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 733 { 734 return ::SendMessage(GetParent(), nMsg, wParam, lParam); 735 } 736 737 LRESULT CCanvasWindow::OnCaptureChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 738 { 739 ::SendMessage(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)_T("")); 740 return 0; 741 } 742 743 LRESULT CCanvasWindow::OnEraseBkgnd(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 744 { 745 return TRUE; // do nothing => transparent background 746 } 747 748 LRESULT CCanvasWindow::OnPaint(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 749 { 750 RECT rcClient; 751 GetClientRect(&rcClient); 752 753 PAINTSTRUCT ps; 754 HDC hDC = BeginPaint(&ps); 755 DoDraw(hDC, rcClient, ps.rcPaint); 756 EndPaint(&ps); 757 return 0; 758 } 759 760 VOID CCanvasWindow::cancelDrawing() 761 { 762 selectionModel.ClearColorImage(); 763 selectionModel.ClearMaskImage(); 764 m_hitSelection = HIT_NONE; 765 m_drawing = FALSE; 766 toolsModel.OnCancelDraw(); 767 Invalidate(FALSE); 768 } 769 770 VOID CCanvasWindow::finishDrawing() 771 { 772 toolsModel.OnFinishDraw(); 773 m_drawing = FALSE; 774 Invalidate(FALSE); 775 } 776 777 HITTEST CCanvasWindow::SelectionHitTest(POINT ptImage) 778 { 779 if (!selectionModel.m_bShow) 780 return HIT_NONE; 781 782 RECT rcSelection = selectionModel.m_rc; 783 Zoomed(rcSelection); 784 ::OffsetRect(&rcSelection, GRIP_SIZE - GetScrollPos(SB_HORZ), GRIP_SIZE - GetScrollPos(SB_VERT)); 785 ::InflateRect(&rcSelection, GRIP_SIZE, GRIP_SIZE); 786 787 return getSizeBoxHitTest(ptImage, &rcSelection); 788 } 789 790 VOID CCanvasWindow::StartSelectionDrag(HITTEST hit, POINT ptImage) 791 { 792 m_hitSelection = hit; 793 selectionModel.m_ptHit = ptImage; 794 selectionModel.TakeOff(); 795 796 SetCapture(); 797 Invalidate(FALSE); 798 } 799 800 VOID CCanvasWindow::SelectionDragging(POINT ptImage) 801 { 802 if (selectionModel.m_nSelectionBrush) 803 { 804 imageModel.SelectionClone(selectionModel.m_nSelectionBrush == 1); 805 selectionModel.m_nSelectionBrush = 2; // Selection Brush is ON and drawn 806 } 807 808 selectionModel.Dragging(m_hitSelection, ptImage); 809 Invalidate(FALSE); 810 } 811 812 VOID CCanvasWindow::EndSelectionDrag(POINT ptImage) 813 { 814 selectionModel.Dragging(m_hitSelection, ptImage); 815 m_hitSelection = HIT_NONE; 816 Invalidate(FALSE); 817 } 818 819 VOID CCanvasWindow::MoveSelection(INT xDelta, INT yDelta) 820 { 821 if (!selectionModel.m_bShow) 822 return; 823 824 selectionModel.TakeOff(); 825 ::OffsetRect(&selectionModel.m_rc, xDelta, yDelta); 826 Invalidate(FALSE); 827 } 828 829 LRESULT CCanvasWindow::OnCtlColorEdit(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 830 { 831 SetTextColor((HDC)wParam, paletteModel.GetFgColor()); 832 SetBkMode((HDC)wParam, TRANSPARENT); 833 return (LRESULT)GetStockObject(NULL_BRUSH); 834 } 835 836 LRESULT CCanvasWindow::OnPaletteModelColorChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 837 { 838 imageModel.NotifyImageChanged(); 839 return 0; 840 } 841