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