1 /* 2 * PROJECT: PAINT for ReactOS 3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) 4 * PURPOSE: Things which should not be in the mouse event handler itself 5 * COPYRIGHT: Copyright 2015 Benedikt Freisen <b.freisen@gmx.net> 6 * Copyright 2021-2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 7 */ 8 9 /* INCLUDES *********************************************************/ 10 11 #include "precomp.h" 12 13 INT ToolBase::s_pointSP = 0; 14 POINT ToolBase::s_pointStack[256] = { { 0 } }; 15 static POINT g_ptStart, g_ptEnd; 16 17 /* FUNCTIONS ********************************************************/ 18 19 void 20 regularize(LONG x0, LONG y0, LONG& x1, LONG& y1) 21 { 22 if (labs(x1 - x0) >= labs(y1 - y0)) 23 y1 = y0 + (y1 > y0 ? labs(x1 - x0) : -labs(x1 - x0)); 24 else 25 x1 = x0 + (x1 > x0 ? labs(y1 - y0) : -labs(y1 - y0)); 26 } 27 28 void 29 roundTo8Directions(LONG x0, LONG y0, LONG& x1, LONG& y1) 30 { 31 if (labs(x1 - x0) >= labs(y1 - y0)) 32 { 33 if (labs(y1 - y0) * 5 < labs(x1 - x0) * 2) 34 y1 = y0; 35 else 36 y1 = y0 + (y1 > y0 ? labs(x1 - x0) : -labs(x1 - x0)); 37 } 38 else 39 { 40 if (labs(x1 - x0) * 5 < labs(y1 - y0) * 2) 41 x1 = x0; 42 else 43 x1 = x0 + (x1 > x0 ? labs(y1 - y0) : -labs(y1 - y0)); 44 } 45 } 46 47 BOOL nearlyEqualPoints(INT x0, INT y0, INT x1, INT y1) 48 { 49 INT cxThreshold = toolsModel.GetLineWidth() + UnZoomed(GetSystemMetrics(SM_CXDRAG)); 50 INT cyThreshold = toolsModel.GetLineWidth() + UnZoomed(GetSystemMetrics(SM_CYDRAG)); 51 return (abs(x1 - x0) <= cxThreshold) && (abs(y1 - y0) <= cyThreshold); 52 } 53 54 void ToolBase::reset() 55 { 56 s_pointSP = 0; 57 g_ptStart.x = g_ptStart.y = g_ptEnd.x = g_ptEnd.y = -1; 58 selectionModel.ResetPtStack(); 59 if (selectionModel.m_bShow) 60 { 61 selectionModel.Landing(); 62 selectionModel.HideSelection(); 63 } 64 } 65 66 void ToolBase::OnEndDraw(BOOL bCancel) 67 { 68 reset(); 69 imageModel.NotifyImageChanged(); 70 } 71 72 void ToolBase::beginEvent() 73 { 74 m_hdc = imageModel.GetDC(); 75 m_fg = paletteModel.GetFgColor(); 76 m_bg = paletteModel.GetBgColor(); 77 } 78 79 void ToolBase::endEvent() 80 { 81 m_hdc = NULL; 82 } 83 84 void ToolBase::OnDrawSelectionOnCanvas(HDC hdc) 85 { 86 if (!selectionModel.m_bShow) 87 return; 88 89 RECT rcSelection = selectionModel.m_rc; 90 canvasWindow.ImageToCanvas(rcSelection); 91 92 ::InflateRect(&rcSelection, GRIP_SIZE, GRIP_SIZE); 93 drawSizeBoxes(hdc, &rcSelection, TRUE); 94 } 95 96 /* TOOLS ********************************************************/ 97 98 // TOOL_FREESEL 99 struct FreeSelTool : ToolBase 100 { 101 BOOL m_bLeftButton = FALSE; 102 103 FreeSelTool() : ToolBase(TOOL_FREESEL) 104 { 105 } 106 107 void OnDrawOverlayOnImage(HDC hdc) override 108 { 109 if (!selectionModel.IsLanded()) 110 selectionModel.DrawSelection(hdc, paletteModel.GetBgColor(), toolsModel.IsBackgroundTransparent()); 111 112 if (canvasWindow.m_drawing) 113 { 114 selectionModel.DrawFramePoly(hdc); 115 } 116 } 117 118 void OnDrawOverlayOnCanvas(HDC hdc) override 119 { 120 OnDrawSelectionOnCanvas(hdc); 121 } 122 123 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override 124 { 125 selectionModel.Landing(); 126 if (bLeftButton) 127 { 128 selectionModel.HideSelection(); 129 selectionModel.ResetPtStack(); 130 POINT pt = { x, y }; 131 selectionModel.PushToPtStack(pt); 132 } 133 m_bLeftButton = bLeftButton; 134 } 135 136 BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override 137 { 138 if (bLeftButton) 139 { 140 POINT pt = { x, y }; 141 imageModel.Clamp(pt); 142 selectionModel.PushToPtStack(pt); 143 imageModel.NotifyImageChanged(); 144 } 145 return TRUE; 146 } 147 148 BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override 149 { 150 if (bLeftButton) 151 { 152 if (selectionModel.PtStackSize() > 2) 153 { 154 selectionModel.BuildMaskFromPtStack(); 155 selectionModel.m_bShow = TRUE; 156 } 157 else 158 { 159 selectionModel.ResetPtStack(); 160 selectionModel.m_bShow = FALSE; 161 } 162 imageModel.NotifyImageChanged(); 163 } 164 else 165 { 166 POINT pt = { x, y }; 167 canvasWindow.ClientToScreen(&pt); 168 mainWindow.TrackPopupMenu(pt, 0); 169 } 170 return TRUE; 171 } 172 173 void OnEndDraw(BOOL bCancel) override 174 { 175 if (bCancel) 176 selectionModel.HideSelection(); 177 else 178 selectionModel.Landing(); 179 ToolBase::OnEndDraw(bCancel); 180 } 181 182 void OnSpecialTweak(BOOL bMinus) override 183 { 184 selectionModel.StretchSelection(bMinus); 185 } 186 }; 187 188 // TOOL_RECTSEL 189 struct RectSelTool : ToolBase 190 { 191 BOOL m_bLeftButton = FALSE; 192 193 RectSelTool() : ToolBase(TOOL_RECTSEL) 194 { 195 } 196 197 void OnDrawOverlayOnImage(HDC hdc) override 198 { 199 if (!selectionModel.IsLanded()) 200 selectionModel.DrawSelection(hdc, paletteModel.GetBgColor(), toolsModel.IsBackgroundTransparent()); 201 202 if (canvasWindow.m_drawing) 203 { 204 RECT rc = selectionModel.m_rc; 205 if (!::IsRectEmpty(&rc)) 206 RectSel(hdc, rc.left, rc.top, rc.right, rc.bottom); 207 } 208 } 209 210 void OnDrawOverlayOnCanvas(HDC hdc) override 211 { 212 OnDrawSelectionOnCanvas(hdc); 213 } 214 215 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override 216 { 217 selectionModel.Landing(); 218 if (bLeftButton) 219 { 220 selectionModel.HideSelection(); 221 } 222 m_bLeftButton = bLeftButton; 223 } 224 225 BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override 226 { 227 if (bLeftButton) 228 { 229 POINT pt = { x, y }; 230 imageModel.Clamp(pt); 231 selectionModel.SetRectFromPoints(g_ptStart, pt); 232 imageModel.NotifyImageChanged(); 233 } 234 return TRUE; 235 } 236 237 BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override 238 { 239 POINT pt = { x, y }; 240 if (bLeftButton) 241 { 242 imageModel.Clamp(pt); 243 selectionModel.SetRectFromPoints(g_ptStart, pt); 244 selectionModel.m_bShow = !selectionModel.m_rc.IsRectEmpty(); 245 imageModel.NotifyImageChanged(); 246 } 247 else 248 { 249 canvasWindow.ClientToScreen(&pt); 250 mainWindow.TrackPopupMenu(pt, 0); 251 } 252 return TRUE; 253 } 254 255 void OnEndDraw(BOOL bCancel) override 256 { 257 if (bCancel) 258 selectionModel.HideSelection(); 259 else 260 selectionModel.Landing(); 261 ToolBase::OnEndDraw(bCancel); 262 } 263 264 void OnSpecialTweak(BOOL bMinus) override 265 { 266 selectionModel.StretchSelection(bMinus); 267 } 268 }; 269 270 struct TwoPointDrawTool : ToolBase 271 { 272 BOOL m_bLeftButton = FALSE; 273 BOOL m_bDrawing = FALSE; 274 275 TwoPointDrawTool(TOOLTYPE type) : ToolBase(type) 276 { 277 } 278 279 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override 280 { 281 m_bLeftButton = bLeftButton; 282 m_bDrawing = TRUE; 283 imageModel.NotifyImageChanged(); 284 } 285 286 BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override 287 { 288 imageModel.NotifyImageChanged(); 289 return TRUE; 290 } 291 292 BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override 293 { 294 imageModel.PushImageForUndo(); 295 OnDrawOverlayOnImage(m_hdc); 296 m_bDrawing = FALSE; 297 imageModel.NotifyImageChanged(); 298 return TRUE; 299 } 300 301 void OnEndDraw(BOOL bCancel) override 302 { 303 m_bDrawing = FALSE; 304 ToolBase::OnEndDraw(bCancel); 305 } 306 307 void OnSpecialTweak(BOOL bMinus) override 308 { 309 toolsModel.MakeLineThickerOrThinner(bMinus); 310 } 311 }; 312 313 typedef enum DIRECTION 314 { 315 NO_DIRECTION = -1, 316 DIRECTION_HORIZONTAL, 317 DIRECTION_VERTICAL, 318 DIRECTION_DIAGONAL_RIGHT_DOWN, 319 DIRECTION_DIAGONAL_RIGHT_UP, 320 } DIRECTION; 321 322 #define THRESHOULD_DEG 15 323 324 static DIRECTION 325 GetDirection(LONG x0, LONG y0, LONG x1, LONG y1) 326 { 327 LONG dx = x1 - x0, dy = y1 - y0; 328 329 if (labs(dx) <= 8 && labs(dy) <= 8) 330 return NO_DIRECTION; 331 332 double radian = atan2((double)dy, (double)dx); 333 if (radian < DEG2RAD(-180 + THRESHOULD_DEG)) 334 { 335 ATLTRACE("DIRECTION_HORIZONTAL: %ld\n", RAD2DEG(radian)); 336 return DIRECTION_HORIZONTAL; 337 } 338 if (radian < DEG2RAD(-90 - THRESHOULD_DEG)) 339 { 340 ATLTRACE("DIRECTION_DIAGONAL_RIGHT_DOWN: %ld\n", RAD2DEG(radian)); 341 return DIRECTION_DIAGONAL_RIGHT_DOWN; 342 } 343 if (radian < DEG2RAD(-90 + THRESHOULD_DEG)) 344 { 345 ATLTRACE("DIRECTION_VERTICAL: %ld\n", RAD2DEG(radian)); 346 return DIRECTION_VERTICAL; 347 } 348 if (radian < DEG2RAD(-THRESHOULD_DEG)) 349 { 350 ATLTRACE("DIRECTION_DIAGONAL_RIGHT_UP: %ld\n", RAD2DEG(radian)); 351 return DIRECTION_DIAGONAL_RIGHT_UP; 352 } 353 if (radian < DEG2RAD(+THRESHOULD_DEG)) 354 { 355 ATLTRACE("DIRECTION_HORIZONTAL: %ld\n", RAD2DEG(radian)); 356 return DIRECTION_HORIZONTAL; 357 } 358 if (radian < DEG2RAD(+90 - THRESHOULD_DEG)) 359 { 360 ATLTRACE("DIRECTION_DIAGONAL_RIGHT_DOWN: %ld\n", RAD2DEG(radian)); 361 return DIRECTION_DIAGONAL_RIGHT_DOWN; 362 } 363 if (radian < DEG2RAD(+90 + THRESHOULD_DEG)) 364 { 365 ATLTRACE("DIRECTION_VERTICAL: %ld\n", RAD2DEG(radian)); 366 return DIRECTION_VERTICAL; 367 } 368 if (radian < DEG2RAD(+180 - THRESHOULD_DEG)) 369 { 370 ATLTRACE("DIRECTION_DIAGONAL_RIGHT_UP: %ld\n", RAD2DEG(radian)); 371 return DIRECTION_DIAGONAL_RIGHT_UP; 372 } 373 ATLTRACE("DIRECTION_HORIZONTAL: %ld\n", RAD2DEG(radian)); 374 return DIRECTION_HORIZONTAL; 375 } 376 377 static void 378 RestrictDrawDirection(DIRECTION dir, LONG x0, LONG y0, LONG& x1, LONG& y1) 379 { 380 switch (dir) 381 { 382 case NO_DIRECTION: 383 default: 384 return; 385 386 case DIRECTION_HORIZONTAL: 387 y1 = y0; 388 break; 389 390 case DIRECTION_VERTICAL: 391 x1 = x0; 392 break; 393 394 case DIRECTION_DIAGONAL_RIGHT_DOWN: 395 y1 = y0 + (x1 - x0); 396 break; 397 398 case DIRECTION_DIAGONAL_RIGHT_UP: 399 x1 = x0 - (y1 - y0); 400 break; 401 } 402 } 403 404 struct SmoothDrawTool : ToolBase 405 { 406 DIRECTION m_direction = NO_DIRECTION; 407 BOOL m_bShiftDown = FALSE; 408 409 SmoothDrawTool(TOOLTYPE type) : ToolBase(type) 410 { 411 } 412 413 virtual void draw(BOOL bLeftButton, LONG x, LONG y) = 0; 414 415 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override 416 { 417 m_direction = NO_DIRECTION; 418 imageModel.PushImageForUndo(); 419 imageModel.NotifyImageChanged(); 420 m_bShiftDown = (::GetKeyState(VK_SHIFT) & 0x8000); // Is Shift key pressed? 421 } 422 423 BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override 424 { 425 if (m_bShiftDown) 426 { 427 if (m_direction == NO_DIRECTION) 428 { 429 m_direction = GetDirection(g_ptStart.x, g_ptStart.y, x, y); 430 if (m_direction == NO_DIRECTION) 431 return FALSE; 432 } 433 434 RestrictDrawDirection(m_direction, g_ptStart.x, g_ptStart.y, x, y); 435 } 436 else 437 { 438 draw(bLeftButton, x, y); 439 g_ptStart.x = g_ptEnd.x = x; 440 g_ptStart.y = g_ptEnd.y = y; 441 return TRUE; 442 } 443 444 draw(bLeftButton, x, y); 445 imageModel.NotifyImageChanged(); 446 return TRUE; 447 } 448 449 BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override 450 { 451 if (m_bShiftDown && m_direction != NO_DIRECTION) 452 { 453 RestrictDrawDirection(m_direction, g_ptStart.x, g_ptStart.y, x, y); 454 } 455 456 draw(bLeftButton, x, y); 457 OnEndDraw(FALSE); 458 return TRUE; 459 } 460 461 void OnEndDraw(BOOL bCancel) override 462 { 463 if (bCancel) 464 { 465 LONG x = 0, y = 0; 466 OnButtonUp(FALSE, x, y); 467 imageModel.Undo(TRUE); 468 } 469 ToolBase::OnEndDraw(bCancel); 470 } 471 }; 472 473 // TOOL_RUBBER 474 struct RubberTool : SmoothDrawTool 475 { 476 RubberTool() : SmoothDrawTool(TOOL_RUBBER) 477 { 478 } 479 480 void draw(BOOL bLeftButton, LONG x, LONG y) override 481 { 482 if (bLeftButton) 483 Erase(m_hdc, g_ptEnd.x, g_ptEnd.y, x, y, m_bg, toolsModel.GetRubberRadius()); 484 else 485 Replace(m_hdc, g_ptEnd.x, g_ptEnd.y, x, y, m_fg, m_bg, toolsModel.GetRubberRadius()); 486 } 487 488 void OnSpecialTweak(BOOL bMinus) override 489 { 490 toolsModel.MakeRubberThickerOrThinner(bMinus); 491 } 492 }; 493 494 // TOOL_FILL 495 struct FillTool : ToolBase 496 { 497 FillTool() : ToolBase(TOOL_FILL) 498 { 499 } 500 501 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override 502 { 503 imageModel.PushImageForUndo(); 504 Fill(m_hdc, x, y, bLeftButton ? m_fg : m_bg); 505 } 506 }; 507 508 // TOOL_COLOR 509 struct ColorTool : ToolBase 510 { 511 ColorTool() : ToolBase(TOOL_COLOR) 512 { 513 } 514 515 void fetchColor(BOOL bLeftButton, LONG x, LONG y) 516 { 517 COLORREF rgbColor; 518 519 if (0 <= x && x < imageModel.GetWidth() && 0 <= y && y < imageModel.GetHeight()) 520 rgbColor = GetPixel(m_hdc, x, y); 521 else 522 rgbColor = RGB(255, 255, 255); // Outside is white 523 524 if (bLeftButton) 525 paletteModel.SetFgColor(rgbColor); 526 else 527 paletteModel.SetBgColor(rgbColor); 528 } 529 530 BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override 531 { 532 fetchColor(bLeftButton, x, y); 533 return TRUE; 534 } 535 536 BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override 537 { 538 fetchColor(bLeftButton, x, y); 539 toolsModel.SetActiveTool(toolsModel.GetOldActiveTool()); 540 return TRUE; 541 } 542 }; 543 544 // TOOL_ZOOM 545 struct ZoomTool : ToolBase 546 { 547 BOOL m_bZoomed = FALSE; 548 549 ZoomTool() : ToolBase(TOOL_ZOOM) 550 { 551 } 552 553 BOOL getNewZoomRect(CRect& rcView, INT newZoom); 554 555 void OnDrawOverlayOnCanvas(HDC hdc) override 556 { 557 CRect rcView; 558 INT oldZoom = toolsModel.GetZoom(); 559 if (oldZoom < MAX_ZOOM && getNewZoomRect(rcView, oldZoom * 2)) 560 DrawXorRect(hdc, &rcView); 561 } 562 563 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override 564 { 565 INT newZoom, oldZoom = toolsModel.GetZoom(); 566 if (bLeftButton) 567 newZoom = (oldZoom < MAX_ZOOM) ? (oldZoom * 2) : MIN_ZOOM; 568 else 569 newZoom = (oldZoom > MIN_ZOOM) ? (oldZoom / 2) : MAX_ZOOM; 570 571 m_bZoomed = FALSE; 572 573 if (oldZoom != newZoom) 574 { 575 CRect rcView; 576 if (getNewZoomRect(rcView, newZoom)) 577 { 578 canvasWindow.zoomTo(newZoom, rcView.left, rcView.top); 579 m_bZoomed = TRUE; 580 } 581 } 582 } 583 584 BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override 585 { 586 if (m_bZoomed) 587 toolsModel.SetActiveTool(toolsModel.GetOldActiveTool()); 588 589 return TRUE; 590 } 591 }; 592 593 BOOL ZoomTool::getNewZoomRect(CRect& rcView, INT newZoom) 594 { 595 CPoint pt; 596 ::GetCursorPos(&pt); 597 canvasWindow.ScreenToClient(&pt); 598 599 canvasWindow.getNewZoomRect(rcView, newZoom, pt); 600 601 CRect rc; 602 canvasWindow.GetImageRect(rc); 603 canvasWindow.ImageToCanvas(rc); 604 605 return rc.PtInRect(pt); 606 } 607 608 // TOOL_PEN 609 struct PenTool : SmoothDrawTool 610 { 611 PenTool() : SmoothDrawTool(TOOL_PEN) 612 { 613 } 614 615 void draw(BOOL bLeftButton, LONG x, LONG y) override 616 { 617 COLORREF rgb = bLeftButton ? m_fg : m_bg; 618 Line(m_hdc, g_ptEnd.x, g_ptEnd.y, x, y, rgb, toolsModel.GetPenWidth()); 619 } 620 621 void OnSpecialTweak(BOOL bMinus) override 622 { 623 toolsModel.MakePenThickerOrThinner(bMinus); 624 } 625 }; 626 627 // TOOL_BRUSH 628 struct BrushTool : SmoothDrawTool 629 { 630 BrushTool() : SmoothDrawTool(TOOL_BRUSH) 631 { 632 } 633 634 void draw(BOOL bLeftButton, LONG x, LONG y) override 635 { 636 COLORREF rgb = bLeftButton ? m_fg : m_bg; 637 Brush(m_hdc, g_ptEnd.x, g_ptEnd.y, x, y, rgb, toolsModel.GetBrushStyle(), 638 toolsModel.GetBrushWidth()); 639 } 640 641 void OnSpecialTweak(BOOL bMinus) override 642 { 643 toolsModel.MakeBrushThickerOrThinner(bMinus); 644 } 645 }; 646 647 // TOOL_AIRBRUSH 648 struct AirBrushTool : SmoothDrawTool 649 { 650 AirBrushTool() : SmoothDrawTool(TOOL_AIRBRUSH) 651 { 652 } 653 654 void draw(BOOL bLeftButton, LONG x, LONG y) override 655 { 656 COLORREF rgb = bLeftButton ? m_fg : m_bg; 657 Airbrush(m_hdc, x, y, rgb, toolsModel.GetAirBrushWidth()); 658 } 659 660 void OnSpecialTweak(BOOL bMinus) override 661 { 662 toolsModel.MakeAirBrushThickerOrThinner(bMinus); 663 } 664 }; 665 666 // TOOL_TEXT 667 struct TextTool : ToolBase 668 { 669 TextTool() : ToolBase(TOOL_TEXT) 670 { 671 } 672 673 void OnDrawOverlayOnImage(HDC hdc) override 674 { 675 if (canvasWindow.m_drawing) 676 { 677 RECT rc = selectionModel.m_rc; 678 if (!::IsRectEmpty(&rc)) 679 RectSel(hdc, rc.left, rc.top, rc.right, rc.bottom); 680 } 681 } 682 683 void UpdatePoint(LONG x, LONG y) 684 { 685 POINT pt = { x, y }; 686 imageModel.Clamp(pt); 687 selectionModel.SetRectFromPoints(g_ptStart, pt); 688 imageModel.NotifyImageChanged(); 689 } 690 691 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override 692 { 693 if (!textEditWindow.IsWindow()) 694 textEditWindow.Create(canvasWindow); 695 696 UpdatePoint(x, y); 697 } 698 699 BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override 700 { 701 UpdatePoint(x, y); 702 return TRUE; 703 } 704 705 void draw(HDC hdc) 706 { 707 CStringW szText; 708 textEditWindow.GetWindowText(szText); 709 710 RECT rc; 711 textEditWindow.InvalidateEditRect(); 712 textEditWindow.GetEditRect(&rc); 713 ::InflateRect(&rc, -GRIP_SIZE / 2, -GRIP_SIZE / 2); 714 715 // Draw the text 716 INT style = (toolsModel.IsBackgroundTransparent() ? 0 : 1); 717 Text(hdc, rc.left, rc.top, rc.right, rc.bottom, m_fg, m_bg, szText, 718 textEditWindow.GetFont(), style); 719 } 720 721 void quit() 722 { 723 if (textEditWindow.IsWindow()) 724 textEditWindow.ShowWindow(SW_HIDE); 725 selectionModel.HideSelection(); 726 } 727 728 BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override 729 { 730 POINT pt = { x, y }; 731 imageModel.Clamp(pt); 732 selectionModel.SetRectFromPoints(g_ptStart, pt); 733 734 BOOL bTextBoxShown = ::IsWindowVisible(textEditWindow); 735 if (bTextBoxShown) 736 { 737 if (textEditWindow.GetWindowTextLength() > 0) 738 { 739 imageModel.PushImageForUndo(); 740 draw(m_hdc); 741 } 742 if (::IsRectEmpty(&selectionModel.m_rc)) 743 { 744 quit(); 745 return TRUE; 746 } 747 } 748 749 if (registrySettings.ShowTextTool) 750 { 751 if (!fontsDialog.IsWindow()) 752 fontsDialog.Create(mainWindow); 753 754 fontsDialog.ShowWindow(SW_SHOWNOACTIVATE); 755 } 756 757 RECT rc = selectionModel.m_rc; 758 759 // Enlarge if tool small 760 INT cxMin = CX_MINTEXTEDIT, cyMin = CY_MINTEXTEDIT; 761 if (selectionModel.m_rc.IsRectEmpty()) 762 { 763 SetRect(&rc, x, y, x + cxMin, y + cyMin); 764 } 765 else 766 { 767 if (rc.right - rc.left < cxMin) 768 rc.right = rc.left + cxMin; 769 if (rc.bottom - rc.top < cyMin) 770 rc.bottom = rc.top + cyMin; 771 } 772 773 if (!textEditWindow.IsWindow()) 774 textEditWindow.Create(canvasWindow); 775 776 textEditWindow.SetWindowText(NULL); 777 textEditWindow.ValidateEditRect(&rc); 778 textEditWindow.ShowWindow(SW_SHOWNOACTIVATE); 779 textEditWindow.SetFocus(); 780 return TRUE; 781 } 782 783 void OnEndDraw(BOOL bCancel) override 784 { 785 if (!bCancel) 786 { 787 if (::IsWindowVisible(textEditWindow) && 788 textEditWindow.GetWindowTextLength() > 0) 789 { 790 imageModel.PushImageForUndo(); 791 draw(m_hdc); 792 } 793 } 794 quit(); 795 ToolBase::OnEndDraw(bCancel); 796 } 797 }; 798 799 // TOOL_LINE 800 struct LineTool : TwoPointDrawTool 801 { 802 LineTool() : TwoPointDrawTool(TOOL_LINE) 803 { 804 } 805 806 void OnDrawOverlayOnImage(HDC hdc) override 807 { 808 if (!m_bDrawing) 809 return; 810 if (GetAsyncKeyState(VK_SHIFT) < 0) 811 roundTo8Directions(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y); 812 COLORREF rgb = m_bLeftButton ? m_fg : m_bg; 813 Line(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, rgb, toolsModel.GetLineWidth()); 814 } 815 }; 816 817 // TOOL_BEZIER 818 struct BezierTool : ToolBase 819 { 820 BOOL m_bLeftButton = FALSE; 821 BOOL m_bDrawing = FALSE; 822 823 BezierTool() : ToolBase(TOOL_BEZIER) 824 { 825 } 826 827 void OnDrawOverlayOnImage(HDC hdc) 828 { 829 if (!m_bDrawing) 830 return; 831 832 COLORREF rgb = (m_bLeftButton ? m_fg : m_bg); 833 switch (s_pointSP) 834 { 835 case 1: 836 Line(hdc, s_pointStack[0].x, s_pointStack[0].y, s_pointStack[1].x, s_pointStack[1].y, rgb, 837 toolsModel.GetLineWidth()); 838 break; 839 case 2: 840 Bezier(hdc, s_pointStack[0], s_pointStack[2], s_pointStack[2], s_pointStack[1], rgb, toolsModel.GetLineWidth()); 841 break; 842 case 3: 843 Bezier(hdc, s_pointStack[0], s_pointStack[2], s_pointStack[3], s_pointStack[1], rgb, toolsModel.GetLineWidth()); 844 break; 845 } 846 } 847 848 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override 849 { 850 m_bLeftButton = bLeftButton; 851 852 if (!m_bDrawing) 853 { 854 m_bDrawing = TRUE; 855 s_pointStack[s_pointSP].x = s_pointStack[s_pointSP + 1].x = x; 856 s_pointStack[s_pointSP].y = s_pointStack[s_pointSP + 1].y = y; 857 ++s_pointSP; 858 } 859 else 860 { 861 ++s_pointSP; 862 s_pointStack[s_pointSP].x = x; 863 s_pointStack[s_pointSP].y = y; 864 } 865 866 imageModel.NotifyImageChanged(); 867 } 868 869 BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override 870 { 871 s_pointStack[s_pointSP].x = x; 872 s_pointStack[s_pointSP].y = y; 873 imageModel.NotifyImageChanged(); 874 return TRUE; 875 } 876 877 BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override 878 { 879 s_pointStack[s_pointSP].x = x; 880 s_pointStack[s_pointSP].y = y; 881 if (s_pointSP >= 3) 882 { 883 OnEndDraw(FALSE); 884 return TRUE; 885 } 886 imageModel.NotifyImageChanged(); 887 return TRUE; 888 } 889 890 void OnEndDraw(BOOL bCancel) override 891 { 892 if (!bCancel) 893 { 894 imageModel.PushImageForUndo(); 895 OnDrawOverlayOnImage(m_hdc); 896 } 897 m_bDrawing = FALSE; 898 ToolBase::OnEndDraw(bCancel); 899 } 900 901 void OnSpecialTweak(BOOL bMinus) override 902 { 903 toolsModel.MakeLineThickerOrThinner(bMinus); 904 } 905 }; 906 907 // TOOL_RECT 908 struct RectTool : TwoPointDrawTool 909 { 910 RectTool() : TwoPointDrawTool(TOOL_RECT) 911 { 912 } 913 914 void OnDrawOverlayOnImage(HDC hdc) override 915 { 916 if (!m_bDrawing) 917 return; 918 if (GetAsyncKeyState(VK_SHIFT) < 0) 919 regularize(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y); 920 if (m_bLeftButton) 921 Rect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle()); 922 else 923 Rect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle()); 924 } 925 }; 926 927 // TOOL_SHAPE 928 struct ShapeTool : ToolBase 929 { 930 BOOL m_bLeftButton = FALSE; 931 BOOL m_bClosed = FALSE; 932 933 ShapeTool() : ToolBase(TOOL_SHAPE) 934 { 935 } 936 937 void OnDrawOverlayOnImage(HDC hdc) 938 { 939 if (s_pointSP <= 0) 940 return; 941 942 if (m_bLeftButton) 943 Poly(hdc, s_pointStack, s_pointSP + 1, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), m_bClosed, FALSE); 944 else 945 Poly(hdc, s_pointStack, s_pointSP + 1, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), m_bClosed, FALSE); 946 } 947 948 void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override 949 { 950 m_bLeftButton = bLeftButton; 951 m_bClosed = FALSE; 952 953 if ((s_pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0)) 954 roundTo8Directions(s_pointStack[s_pointSP - 1].x, s_pointStack[s_pointSP - 1].y, x, y); 955 956 s_pointStack[s_pointSP].x = x; 957 s_pointStack[s_pointSP].y = y; 958 959 if (s_pointSP && bDoubleClick) 960 { 961 OnEndDraw(FALSE); 962 return; 963 } 964 965 if (s_pointSP == 0) 966 { 967 s_pointSP++; 968 s_pointStack[s_pointSP].x = x; 969 s_pointStack[s_pointSP].y = y; 970 } 971 972 imageModel.NotifyImageChanged(); 973 } 974 975 BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override 976 { 977 if ((s_pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0)) 978 roundTo8Directions(s_pointStack[s_pointSP - 1].x, s_pointStack[s_pointSP - 1].y, x, y); 979 980 s_pointStack[s_pointSP].x = x; 981 s_pointStack[s_pointSP].y = y; 982 983 imageModel.NotifyImageChanged(); 984 return TRUE; 985 } 986 987 BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override 988 { 989 if ((s_pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0)) 990 roundTo8Directions(s_pointStack[s_pointSP - 1].x, s_pointStack[s_pointSP - 1].y, x, y); 991 992 m_bClosed = FALSE; 993 if (nearlyEqualPoints(x, y, s_pointStack[0].x, s_pointStack[0].y)) 994 { 995 OnEndDraw(FALSE); 996 return TRUE; 997 } 998 else 999 { 1000 s_pointSP++; 1001 s_pointStack[s_pointSP].x = x; 1002 s_pointStack[s_pointSP].y = y; 1003 } 1004 1005 if (s_pointSP == _countof(s_pointStack)) 1006 s_pointSP--; 1007 1008 imageModel.NotifyImageChanged(); 1009 return TRUE; 1010 } 1011 1012 void OnEndDraw(BOOL bCancel) override 1013 { 1014 if (!bCancel) 1015 { 1016 if (s_pointSP) 1017 { 1018 --s_pointSP; 1019 m_bClosed = TRUE; 1020 1021 imageModel.PushImageForUndo(); 1022 OnDrawOverlayOnImage(m_hdc); 1023 } 1024 m_bClosed = FALSE; 1025 s_pointSP = 0; 1026 } 1027 ToolBase::OnEndDraw(bCancel); 1028 } 1029 1030 void OnSpecialTweak(BOOL bMinus) override 1031 { 1032 toolsModel.MakeLineThickerOrThinner(bMinus); 1033 } 1034 }; 1035 1036 // TOOL_ELLIPSE 1037 struct EllipseTool : TwoPointDrawTool 1038 { 1039 EllipseTool() : TwoPointDrawTool(TOOL_ELLIPSE) 1040 { 1041 } 1042 1043 void OnDrawOverlayOnImage(HDC hdc) override 1044 { 1045 if (!m_bDrawing) 1046 return; 1047 if (GetAsyncKeyState(VK_SHIFT) < 0) 1048 regularize(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y); 1049 if (m_bLeftButton) 1050 Ellp(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle()); 1051 else 1052 Ellp(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle()); 1053 } 1054 }; 1055 1056 // TOOL_RRECT 1057 struct RRectTool : TwoPointDrawTool 1058 { 1059 RRectTool() : TwoPointDrawTool(TOOL_RRECT) 1060 { 1061 } 1062 1063 void OnDrawOverlayOnImage(HDC hdc) override 1064 { 1065 if (!m_bDrawing) 1066 return; 1067 if (GetAsyncKeyState(VK_SHIFT) < 0) 1068 regularize(g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y); 1069 if (m_bLeftButton) 1070 RRect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle()); 1071 else 1072 RRect(hdc, g_ptStart.x, g_ptStart.y, g_ptEnd.x, g_ptEnd.y, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle()); 1073 } 1074 }; 1075 1076 /*static*/ ToolBase* 1077 ToolBase::createToolObject(TOOLTYPE type) 1078 { 1079 switch (type) 1080 { 1081 case TOOL_FREESEL: return new FreeSelTool(); 1082 case TOOL_RECTSEL: return new RectSelTool(); 1083 case TOOL_RUBBER: return new RubberTool(); 1084 case TOOL_FILL: return new FillTool(); 1085 case TOOL_COLOR: return new ColorTool(); 1086 case TOOL_ZOOM: return new ZoomTool(); 1087 case TOOL_PEN: return new PenTool(); 1088 case TOOL_BRUSH: return new BrushTool(); 1089 case TOOL_AIRBRUSH: return new AirBrushTool(); 1090 case TOOL_TEXT: return new TextTool(); 1091 case TOOL_LINE: return new LineTool(); 1092 case TOOL_BEZIER: return new BezierTool(); 1093 case TOOL_RECT: return new RectTool(); 1094 case TOOL_SHAPE: return new ShapeTool(); 1095 case TOOL_ELLIPSE: return new EllipseTool(); 1096 case TOOL_RRECT: return new RRectTool(); 1097 } 1098 UNREACHABLE; 1099 return NULL; 1100 } 1101 1102 void ToolsModel::OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) 1103 { 1104 m_pToolObject->beginEvent(); 1105 g_ptStart.x = g_ptEnd.x = x; 1106 g_ptStart.y = g_ptEnd.y = y; 1107 m_pToolObject->OnButtonDown(bLeftButton, x, y, bDoubleClick); 1108 m_pToolObject->endEvent(); 1109 } 1110 1111 void ToolsModel::OnMouseMove(BOOL bLeftButton, LONG x, LONG y) 1112 { 1113 m_pToolObject->beginEvent(); 1114 if (m_pToolObject->OnMouseMove(bLeftButton, x, y)) 1115 { 1116 g_ptEnd.x = x; 1117 g_ptEnd.y = y; 1118 } 1119 m_pToolObject->endEvent(); 1120 } 1121 1122 void ToolsModel::OnButtonUp(BOOL bLeftButton, LONG x, LONG y) 1123 { 1124 m_pToolObject->beginEvent(); 1125 if (m_pToolObject->OnButtonUp(bLeftButton, x, y)) 1126 { 1127 g_ptEnd.x = x; 1128 g_ptEnd.y = y; 1129 } 1130 m_pToolObject->endEvent(); 1131 } 1132 1133 void ToolsModel::OnEndDraw(BOOL bCancel) 1134 { 1135 ATLTRACE("ToolsModel::OnEndDraw(%d)\n", bCancel); 1136 m_pToolObject->beginEvent(); 1137 m_pToolObject->OnEndDraw(bCancel); 1138 m_pToolObject->endEvent(); 1139 } 1140 1141 void ToolsModel::OnDrawOverlayOnImage(HDC hdc) 1142 { 1143 m_pToolObject->OnDrawOverlayOnImage(hdc); 1144 } 1145 1146 void ToolsModel::OnDrawOverlayOnCanvas(HDC hdc) 1147 { 1148 m_pToolObject->OnDrawOverlayOnCanvas(hdc); 1149 } 1150 1151 void ToolsModel::SpecialTweak(BOOL bMinus) 1152 { 1153 m_pToolObject->OnSpecialTweak(bMinus); 1154 } 1155 1156 void ToolsModel::DrawWithMouseTool(POINT pt, WPARAM wParam) 1157 { 1158 LONG xRel = pt.x - g_ptStart.x, yRel = pt.y - g_ptStart.y; 1159 1160 switch (m_activeTool) 1161 { 1162 // freesel, rectsel and text tools always show numbers limited to fit into image area 1163 case TOOL_FREESEL: 1164 case TOOL_RECTSEL: 1165 case TOOL_TEXT: 1166 if (xRel < 0) 1167 xRel = (pt.x < 0) ? -g_ptStart.x : xRel; 1168 else if (pt.x > imageModel.GetWidth()) 1169 xRel = imageModel.GetWidth() - g_ptStart.x; 1170 if (yRel < 0) 1171 yRel = (pt.y < 0) ? -g_ptStart.y : yRel; 1172 else if (pt.y > imageModel.GetHeight()) 1173 yRel = imageModel.GetHeight() - g_ptStart.y; 1174 break; 1175 1176 // while drawing, update cursor coordinates only for tools 3, 7, 8, 9, 14 1177 case TOOL_RUBBER: 1178 case TOOL_PEN: 1179 case TOOL_BRUSH: 1180 case TOOL_AIRBRUSH: 1181 case TOOL_SHAPE: 1182 { 1183 CStringW strCoord; 1184 strCoord.Format(L"%ld, %ld", pt.x, pt.y); 1185 ::SendMessageW(g_hStatusBar, SB_SETTEXT, 1, (LPARAM)(LPCWSTR)strCoord); 1186 break; 1187 } 1188 default: 1189 break; 1190 } 1191 1192 // rectsel and shape tools always show non-negative numbers when drawing 1193 if (m_activeTool == TOOL_RECTSEL || m_activeTool == TOOL_SHAPE) 1194 { 1195 xRel = labs(xRel); 1196 yRel = labs(yRel); 1197 } 1198 1199 if (wParam & MK_LBUTTON) 1200 { 1201 OnMouseMove(TRUE, pt.x, pt.y); 1202 canvasWindow.Invalidate(FALSE); 1203 if ((m_activeTool >= TOOL_TEXT) || IsSelection()) 1204 { 1205 CStringW strSize; 1206 if ((m_activeTool >= TOOL_LINE) && (GetAsyncKeyState(VK_SHIFT) < 0)) 1207 yRel = xRel; 1208 strSize.Format(L"%ld x %ld", xRel, yRel); 1209 ::SendMessageW(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)(LPCWSTR)strSize); 1210 } 1211 } 1212 1213 if (wParam & MK_RBUTTON) 1214 { 1215 OnMouseMove(FALSE, pt.x, pt.y); 1216 canvasWindow.Invalidate(FALSE); 1217 if (m_activeTool >= TOOL_TEXT) 1218 { 1219 CStringW strSize; 1220 if ((m_activeTool >= TOOL_LINE) && (GetAsyncKeyState(VK_SHIFT) < 0)) 1221 yRel = xRel; 1222 strSize.Format(L"%ld x %ld", xRel, yRel); 1223 ::SendMessageW(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)(LPCWSTR)strSize); 1224 } 1225 } 1226 } 1227