1 /* 2 * PROJECT: PAINT for ReactOS 3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) 4 * PURPOSE: Window procedure of the tool settings window 5 * COPYRIGHT: Copyright 2015 Benedikt Freisen <b.freisen@gmx.net> 6 * Copyright 2018 Stanislav Motylkov <x86corez@gmail.com> 7 * Copyright 2021-2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 8 */ 9 10 #include "precomp.h" 11 12 #define X_TOOLSETTINGS 0 13 #define Y_TOOLSETTINGS (CY_TOOLBAR + 3) 14 #define CX_TOOLSETTINGS CX_TOOLBAR 15 #define CY_TOOLSETTINGS 140 16 17 #define CX_TRANS_ICON 40 18 #define CY_TRANS_ICON 30 19 #define MARGIN1 3 20 #define MARGIN2 2 21 22 #define MAX_ZOOM_TRACK 6 23 #define MIN_ZOOM_TRACK 0 24 #define DEFAULT_ZOOM_TRACK 3 25 26 static const BYTE s_AirRadius[4] = { 5, 8, 3, 12 }; 27 28 CToolSettingsWindow toolSettingsWindow; 29 30 /* FUNCTIONS ********************************************************/ 31 32 BOOL CToolSettingsWindow::DoCreate(HWND hwndParent) 33 { 34 RECT toolSettingsWindowPos = 35 { 36 X_TOOLSETTINGS, Y_TOOLSETTINGS, 37 X_TOOLSETTINGS + CX_TOOLSETTINGS, Y_TOOLSETTINGS + CY_TOOLSETTINGS 38 }; 39 return !!Create(toolBoxContainer, toolSettingsWindowPos, NULL, WS_CHILD | WS_VISIBLE); 40 } 41 42 static INT 43 getSplitRects(RECT *rects, INT cColumns, INT cRows, LPCRECT prc, LPPOINT ppt) 44 { 45 INT cx = prc->right - prc->left, cy = prc->bottom - prc->top; 46 for (INT i = 0, iRow = 0; iRow < cRows; ++iRow) 47 { 48 for (INT iColumn = 0; iColumn < cColumns; ++iColumn) 49 { 50 RECT& rc = rects[i]; 51 rc.left = prc->left + (iColumn * cx / cColumns); 52 rc.top = prc->top + (iRow * cy / cRows); 53 rc.right = prc->left + ((iColumn + 1) * cx / cColumns); 54 rc.bottom = prc->top + ((iRow + 1) * cy / cRows); 55 if (ppt && ::PtInRect(&rc, *ppt)) 56 return i; 57 ++i; 58 } 59 } 60 return -1; 61 } 62 63 static inline INT getTransRects(RECT rects[2], LPCRECT prc, LPPOINT ppt = NULL) 64 { 65 return getSplitRects(rects, 1, 2, prc, ppt); 66 } 67 68 VOID CToolSettingsWindow::drawTrans(HDC hdc, LPCRECT prc) 69 { 70 RECT rc[2]; 71 getTransRects(rc, prc); 72 73 ::FillRect(hdc, &rc[toolsModel.IsBackgroundTransparent()], (HBRUSH)(COLOR_HIGHLIGHT + 1)); 74 ::DrawIconEx(hdc, rc[0].left, rc[0].top, m_hNontranspIcon, 75 CX_TRANS_ICON, CY_TRANS_ICON, 0, NULL, DI_NORMAL); 76 ::DrawIconEx(hdc, rc[1].left, rc[1].top, m_hTranspIcon, 77 CX_TRANS_ICON, CY_TRANS_ICON, 0, NULL, DI_NORMAL); 78 } 79 80 static inline INT getRubberRects(RECT rects[4], LPCRECT prc, LPPOINT ppt = NULL) 81 { 82 return getSplitRects(rects, 1, 4, prc, ppt); 83 } 84 85 VOID CToolSettingsWindow::drawRubber(HDC hdc, LPCRECT prc) 86 { 87 RECT rects[4], rcRubber; 88 getRubberRects(rects, prc); 89 INT xCenter = (prc->left + prc->right) / 2; 90 for (INT i = 0; i < 4; i++) 91 { 92 INT iColor, radius = i + 2; 93 if (toolsModel.GetRubberRadius() == radius) 94 { 95 ::FillRect(hdc, &rects[i], ::GetSysColorBrush(COLOR_HIGHLIGHT)); 96 iColor = COLOR_HIGHLIGHTTEXT; 97 } 98 else 99 { 100 iColor = COLOR_WINDOWTEXT; 101 } 102 103 INT yCenter = (rects[i].top + rects[i].bottom) / 2; 104 rcRubber.left = xCenter - radius; 105 rcRubber.top = yCenter - radius; 106 rcRubber.right = rcRubber.left + radius * 2; 107 rcRubber.bottom = rcRubber.top + radius * 2; 108 ::FillRect(hdc, &rcRubber, GetSysColorBrush(iColor)); 109 } 110 } 111 112 static inline INT getBrushRects(RECT rects[12], LPCRECT prc, LPPOINT ppt = NULL) 113 { 114 return getSplitRects(rects, 3, 4, prc, ppt); 115 } 116 117 struct BrushStyleAndWidth 118 { 119 BrushStyle style; 120 INT width; 121 }; 122 123 static const BrushStyleAndWidth c_BrushPresets[] = 124 { 125 { BrushStyleRound, 7 }, { BrushStyleRound, 4 }, { BrushStyleRound, 1 }, 126 { BrushStyleSquare, 8 }, { BrushStyleSquare, 5 }, { BrushStyleSquare, 2 }, 127 { BrushStyleForeSlash, 8 }, { BrushStyleForeSlash, 5 }, { BrushStyleForeSlash, 2 }, 128 { BrushStyleBackSlash, 8 }, { BrushStyleBackSlash, 5 }, { BrushStyleBackSlash, 2 }, 129 }; 130 131 VOID CToolSettingsWindow::drawBrush(HDC hdc, LPCRECT prc) 132 { 133 RECT rects[12]; 134 getBrushRects(rects, prc); 135 136 for (INT i = 0; i < 12; i++) 137 { 138 RECT rcItem = rects[i]; 139 INT x = (rcItem.left + rcItem.right) / 2, y = (rcItem.top + rcItem.bottom) / 2; 140 141 INT iColor; 142 const BrushStyleAndWidth& data = c_BrushPresets[i]; 143 if (data.width == toolsModel.GetBrushWidth() && data.style == toolsModel.GetBrushStyle()) 144 { 145 iColor = COLOR_HIGHLIGHTTEXT; 146 ::FillRect(hdc, &rcItem, (HBRUSH)(COLOR_HIGHLIGHT + 1)); 147 } 148 else 149 { 150 iColor = COLOR_WINDOWTEXT; 151 } 152 153 Brush(hdc, x, y, x, y, ::GetSysColor(iColor), data.style, data.width); 154 } 155 } 156 157 static inline INT getLineRects(RECT rects[5], LPCRECT prc, LPPOINT ppt = NULL) 158 { 159 return getSplitRects(rects, 1, 5, prc, ppt); 160 } 161 162 VOID CToolSettingsWindow::drawLine(HDC hdc, LPCRECT prc) 163 { 164 RECT rects[5]; 165 getLineRects(rects, prc); 166 167 for (INT i = 0; i < 5; i++) 168 { 169 INT penWidth = i + 1; 170 CRect rcLine = rects[i]; 171 rcLine.InflateRect(-2, 0); 172 rcLine.top = (rcLine.top + rcLine.bottom - penWidth) / 2; 173 rcLine.bottom = rcLine.top + penWidth; 174 if (toolsModel.GetLineWidth() == penWidth) 175 { 176 ::FillRect(hdc, &rects[i], ::GetSysColorBrush(COLOR_HIGHLIGHT)); 177 ::FillRect(hdc, &rcLine, ::GetSysColorBrush(COLOR_HIGHLIGHTTEXT)); 178 } 179 else 180 { 181 ::FillRect(hdc, &rcLine, ::GetSysColorBrush(COLOR_WINDOWTEXT)); 182 } 183 } 184 } 185 186 static INT getAirBrushRects(RECT rects[4], LPCRECT prc, LPPOINT ppt = NULL) 187 { 188 INT cx = (prc->right - prc->left), cy = (prc->bottom - prc->top); 189 190 rects[0] = rects[1] = rects[2] = rects[3] = *prc; 191 192 rects[0].right = rects[1].left = prc->left + cx * 3 / 8; 193 rects[0].bottom = rects[1].bottom = prc->top + cy / 2; 194 195 rects[2].top = rects[3].top = prc->top + cy / 2; 196 rects[2].right = rects[3].left = prc->left + cx * 2 / 8; 197 198 if (ppt) 199 { 200 for (INT i = 0; i < 4; ++i) 201 { 202 if (::PtInRect(&rects[i], *ppt)) 203 return i; 204 } 205 } 206 return -1; 207 } 208 209 VOID CToolSettingsWindow::drawAirBrush(HDC hdc, LPCRECT prc) 210 { 211 RECT rects[4]; 212 getAirBrushRects(rects, prc); 213 214 srand(0); 215 for (size_t i = 0; i < 4; ++i) 216 { 217 RECT& rc = rects[i]; 218 INT x = (rc.left + rc.right) / 2; 219 INT y = (rc.top + rc.bottom) / 2; 220 BOOL bHigh = (s_AirRadius[i] == toolsModel.GetAirBrushRadius()); 221 if (bHigh) 222 { 223 ::FillRect(hdc, &rc, ::GetSysColorBrush(COLOR_HIGHLIGHT)); 224 225 for (int k = 0; k < 3; ++k) 226 Airbrush(hdc, x, y, ::GetSysColor(COLOR_HIGHLIGHTTEXT), s_AirRadius[i]); 227 } 228 else 229 { 230 for (int k = 0; k < 3; ++k) 231 Airbrush(hdc, x, y, ::GetSysColor(COLOR_WINDOWTEXT), s_AirRadius[i]); 232 } 233 } 234 } 235 236 static inline INT getBoxRects(RECT rects[3], LPCRECT prc, LPPOINT ppt = NULL) 237 { 238 return getSplitRects(rects, 1, 3, prc, ppt); 239 } 240 241 VOID CToolSettingsWindow::drawBox(HDC hdc, LPCRECT prc) 242 { 243 CRect rects[3]; 244 getBoxRects(rects, prc); 245 246 for (INT iItem = 0; iItem < 3; ++iItem) 247 { 248 CRect& rcItem = rects[iItem]; 249 250 if (toolsModel.GetShapeStyle() == iItem) 251 ::FillRect(hdc, &rcItem, ::GetSysColorBrush(COLOR_HIGHLIGHT)); 252 253 rcItem.InflateRect(-5, -5); 254 255 if (iItem <= 1) 256 { 257 COLORREF rgbPen; 258 if (toolsModel.GetShapeStyle() == iItem) 259 rgbPen = ::GetSysColor(COLOR_HIGHLIGHTTEXT); 260 else 261 rgbPen = ::GetSysColor(COLOR_WINDOWTEXT); 262 HGDIOBJ hOldBrush; 263 if (iItem == 0) 264 hOldBrush = ::SelectObject(hdc, ::GetStockObject(NULL_BRUSH)); 265 else 266 hOldBrush = ::SelectObject(hdc, ::GetSysColorBrush(COLOR_APPWORKSPACE)); 267 HGDIOBJ hOldPen = ::SelectObject(hdc, ::CreatePen(PS_SOLID, 1, rgbPen)); 268 ::Rectangle(hdc, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom); 269 ::DeleteObject(::SelectObject(hdc, hOldPen)); 270 ::SelectObject(hdc, hOldBrush); 271 } 272 else 273 { 274 if (toolsModel.GetShapeStyle() == iItem) 275 ::FillRect(hdc, &rcItem, ::GetSysColorBrush(COLOR_HIGHLIGHTTEXT)); 276 else 277 ::FillRect(hdc, &rcItem, ::GetSysColorBrush(COLOR_WINDOWTEXT)); 278 } 279 } 280 } 281 282 LRESULT CToolSettingsWindow::OnCreate(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 283 { 284 /* preloading the draw transparent/nontransparent icons for later use */ 285 m_hNontranspIcon = (HICON)LoadImageW(g_hinstExe, MAKEINTRESOURCEW(IDI_NONTRANSPARENT), 286 IMAGE_ICON, CX_TRANS_ICON, CY_TRANS_ICON, LR_DEFAULTCOLOR); 287 m_hTranspIcon = (HICON)LoadImageW(g_hinstExe, MAKEINTRESOURCEW(IDI_TRANSPARENT), 288 IMAGE_ICON, CX_TRANS_ICON, CY_TRANS_ICON, LR_DEFAULTCOLOR); 289 290 CRect trackbarZoomPos, rect2; 291 calculateTwoBoxes(trackbarZoomPos, rect2); 292 trackbarZoomPos.InflateRect(-1, -1); 293 294 trackbarZoom.Create(TRACKBAR_CLASS, m_hWnd, trackbarZoomPos, NULL, WS_CHILD | TBS_VERT | TBS_AUTOTICKS); 295 trackbarZoom.SendMessage(TBM_SETRANGE, TRUE, MAKELPARAM(MIN_ZOOM_TRACK, MAX_ZOOM_TRACK)); 296 trackbarZoom.SendMessage(TBM_SETPOS, TRUE, DEFAULT_ZOOM_TRACK); 297 return 0; 298 } 299 300 LRESULT CToolSettingsWindow::OnDestroy(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 301 { 302 ::DestroyIcon(m_hNontranspIcon); 303 ::DestroyIcon(m_hTranspIcon); 304 return 0; 305 } 306 307 LRESULT CToolSettingsWindow::OnVScroll(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 308 { 309 INT trackPos = MAX_ZOOM_TRACK - (INT)trackbarZoom.SendMessage(TBM_GETPOS, 0, 0); 310 canvasWindow.zoomTo(MIN_ZOOM << trackPos); 311 312 INT zoomRate = toolsModel.GetZoom(); 313 314 CStringW strZoom; 315 if (zoomRate % 10 == 0) 316 strZoom.Format(L"%d%%", zoomRate / 10); 317 else 318 strZoom.Format(L"%d.%d%%", zoomRate / 10, zoomRate % 10); 319 320 ::SendMessageW(g_hStatusBar, SB_SETTEXT, 1, (LPARAM)(LPCWSTR)strZoom); 321 322 OnToolsModelZoomChanged(nMsg, wParam, lParam, bHandled); 323 return 0; 324 } 325 326 LRESULT CToolSettingsWindow::OnNotify(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 327 { 328 NMHDR *pnmhdr = (NMHDR*)lParam; 329 if (pnmhdr->code == NM_CUSTOMDRAW) 330 { 331 NMCUSTOMDRAW *pCustomDraw = (NMCUSTOMDRAW*)pnmhdr; 332 pCustomDraw->uItemState &= ~CDIS_FOCUS; // Do not draw the focus 333 } 334 return 0; 335 } 336 337 VOID CToolSettingsWindow::calculateTwoBoxes(CRect& rect1, CRect& rect2) 338 { 339 CRect rcClient; 340 GetClientRect(&rcClient); 341 rcClient.InflateRect(-MARGIN1, -MARGIN1); 342 343 INT yCenter = (rcClient.top + rcClient.bottom) / 2; 344 rect1.SetRect(rcClient.left, rcClient.top, rcClient.right, yCenter); 345 rect2.SetRect(rcClient.left, yCenter, rcClient.right, rcClient.bottom); 346 347 rect1.InflateRect(-MARGIN2, -MARGIN2); 348 rect2.InflateRect(-MARGIN2, -MARGIN2); 349 } 350 351 LRESULT CToolSettingsWindow::OnPaint(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 352 { 353 CRect rect1, rect2; 354 calculateTwoBoxes(rect1, rect2); 355 356 PAINTSTRUCT ps; 357 HDC hdc = BeginPaint(&ps); 358 359 if (toolsModel.GetActiveTool() != TOOL_ZOOM) 360 ::DrawEdge(hdc, &rect1, BDR_SUNKENOUTER, BF_RECT | BF_MIDDLE); 361 362 if (toolsModel.GetActiveTool() >= TOOL_RECT) 363 ::DrawEdge(hdc, &rect2, BDR_SUNKENOUTER, BF_RECT | BF_MIDDLE); 364 365 rect1.InflateRect(-MARGIN2, -MARGIN2); 366 rect2.InflateRect(-MARGIN2, -MARGIN2); 367 switch (toolsModel.GetActiveTool()) 368 { 369 case TOOL_FREESEL: 370 case TOOL_RECTSEL: 371 case TOOL_TEXT: 372 drawTrans(hdc, &rect1); 373 break; 374 case TOOL_RUBBER: 375 drawRubber(hdc, &rect1); 376 break; 377 case TOOL_BRUSH: 378 drawBrush(hdc, &rect1); 379 break; 380 case TOOL_AIRBRUSH: 381 drawAirBrush(hdc, &rect1); 382 break; 383 case TOOL_LINE: 384 case TOOL_BEZIER: 385 drawLine(hdc, &rect1); 386 break; 387 case TOOL_RECT: 388 case TOOL_SHAPE: 389 case TOOL_ELLIPSE: 390 case TOOL_RRECT: 391 drawBox(hdc, &rect1); 392 drawLine(hdc, &rect2); 393 break; 394 case TOOL_FILL: 395 case TOOL_COLOR: 396 case TOOL_ZOOM: 397 case TOOL_PEN: 398 break; 399 } 400 EndPaint(&ps); 401 return 0; 402 } 403 404 LRESULT CToolSettingsWindow::OnLButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 405 { 406 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; 407 408 CRect rect1, rect2; 409 calculateTwoBoxes(rect1, rect2); 410 RECT rects[12]; 411 412 INT iItem; 413 switch (toolsModel.GetActiveTool()) 414 { 415 case TOOL_FREESEL: 416 case TOOL_RECTSEL: 417 case TOOL_TEXT: 418 iItem = getTransRects(rects, &rect1, &pt); 419 if (iItem != -1) 420 toolsModel.SetBackgroundTransparent(iItem); 421 break; 422 case TOOL_RUBBER: 423 iItem = getRubberRects(rects, &rect1, &pt); 424 if (iItem != -1) 425 toolsModel.SetRubberRadius(iItem + 2); 426 break; 427 case TOOL_BRUSH: 428 iItem = getBrushRects(rects, &rect1, &pt); 429 if (iItem != -1) 430 { 431 const BrushStyleAndWidth& data = c_BrushPresets[iItem]; 432 toolsModel.SetBrushStyle(data.style); 433 toolsModel.SetBrushWidth(data.width); 434 } 435 break; 436 case TOOL_AIRBRUSH: 437 iItem = getAirBrushRects(rects, &rect1, &pt); 438 if (iItem != -1) 439 toolsModel.SetAirBrushRadius(s_AirRadius[iItem]); 440 break; 441 case TOOL_LINE: 442 case TOOL_BEZIER: 443 iItem = getLineRects(rects, &rect1, &pt); 444 if (iItem != -1) 445 toolsModel.SetLineWidth(iItem + 1); 446 break; 447 case TOOL_RECT: 448 case TOOL_SHAPE: 449 case TOOL_ELLIPSE: 450 case TOOL_RRECT: 451 iItem = getBoxRects(rects, &rect1, &pt); 452 if (iItem != -1) 453 toolsModel.SetShapeStyle(iItem); 454 455 iItem = getLineRects(rects, &rect2, &pt); 456 if (iItem != -1) 457 toolsModel.SetLineWidth(iItem + 1); 458 break; 459 case TOOL_FILL: 460 case TOOL_COLOR: 461 case TOOL_ZOOM: 462 case TOOL_PEN: 463 break; 464 } 465 466 ::SetCapture(::GetParent(m_hWnd)); 467 return 0; 468 } 469 470 LRESULT CToolSettingsWindow::OnToolsModelToolChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 471 { 472 Invalidate(); 473 trackbarZoom.ShowWindow((wParam == TOOL_ZOOM) ? SW_SHOW : SW_HIDE); 474 return 0; 475 } 476 477 LRESULT CToolSettingsWindow::OnToolsModelSettingsChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 478 { 479 Invalidate(); 480 return 0; 481 } 482 483 LRESULT CToolSettingsWindow::OnToolsModelZoomChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 484 { 485 int tbPos = MIN_ZOOM_TRACK; 486 int tempZoom = toolsModel.GetZoom(); 487 488 while (tempZoom > MIN_ZOOM) 489 { 490 tbPos++; 491 tempZoom = tempZoom >> 1; 492 } 493 494 trackbarZoom.SendMessage(TBM_SETPOS, TRUE, MAX_ZOOM_TRACK - tbPos); 495 return 0; 496 } 497