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