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 palette window
5  * COPYRIGHT:  Copyright 2015 Benedikt Freisen <b.freisen@gmx.net>
6  *             Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
7  */
8 
9 #include "precomp.h"
10 
11 /* The private metrics */
12 #define CXY_SELECTEDBOX     15 /* width / height of a selected color box */
13 #define X_MARGIN 4 /* horizontal margin */
14 #define Y_MARGIN ((rcClient.bottom / 2) - CXY_COLORBOX) /* center position minus one color box */
15 #define X_COLORBOX_OFFSET   (X_MARGIN + CXY_BIGBOX + X_MARGIN)
16 #define COLOR_COUNT         28
17 #define HALF_COLOR_COUNT    (COLOR_COUNT / 2)
18 
19 CPaletteWindow paletteWindow;
20 
21 /* FUNCTIONS ********************************************************/
22 
CPaletteWindow()23 CPaletteWindow::CPaletteWindow()
24     : m_hbmCached(NULL)
25 {
26 }
27 
~CPaletteWindow()28 CPaletteWindow::~CPaletteWindow()
29 {
30     if (m_hbmCached)
31         ::DeleteObject(m_hbmCached);
32 }
33 
drawColorBox(HDC hDC,LPCRECT prc,COLORREF rgbColor,UINT nBorder)34 static VOID drawColorBox(HDC hDC, LPCRECT prc, COLORREF rgbColor, UINT nBorder)
35 {
36     RECT rc = *prc;
37     ::FillRect(hDC, &rc, (HBRUSH)(COLOR_3DFACE + 1));
38     ::DrawEdge(hDC, &rc, nBorder, BF_RECT | BF_ADJUST);
39 
40     HBRUSH hbr = ::CreateSolidBrush(rgbColor);
41     ::FillRect(hDC, &rc, hbr);
42     ::DeleteObject(hbr);
43 }
44 
getColorBoxRect(LPRECT prc,const RECT & rcClient,INT iColor)45 static VOID getColorBoxRect(LPRECT prc, const RECT& rcClient, INT iColor)
46 {
47     INT dx = (iColor % HALF_COLOR_COUNT) * CXY_COLORBOX; /* delta x */
48     INT dy = (iColor / HALF_COLOR_COUNT) * CXY_COLORBOX; /* delta y */
49     prc->left   = X_COLORBOX_OFFSET + dx;
50     prc->right  = prc->left + CXY_COLORBOX;
51     prc->top    = Y_MARGIN + dy;
52     prc->bottom = prc->top + CXY_COLORBOX;
53 }
54 
DoHitTest(INT xPos,INT yPos) const55 INT CPaletteWindow::DoHitTest(INT xPos, INT yPos) const
56 {
57     RECT rcClient;
58     GetClientRect(&rcClient);
59 
60     /* delta x and y */
61     INT dx = (xPos - X_COLORBOX_OFFSET), dy = (yPos - Y_MARGIN);
62 
63     /* horizontal and vertical indexes */
64     INT ix = (dx / CXY_COLORBOX), iy = (dy / CXY_COLORBOX);
65 
66     /* Is it inside of a color box? */
67     if (0 <= ix && ix < HALF_COLOR_COUNT && 0 <= iy && iy < 2)
68         return ix + (iy * HALF_COLOR_COUNT); /* return the color index */
69 
70     return -1; /* Not found */
71 }
72 
OnEraseBkgnd(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)73 LRESULT CPaletteWindow::OnEraseBkgnd(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
74 {
75     return TRUE; /* Avoid flickering */
76 }
77 
OnPaint(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)78 LRESULT CPaletteWindow::OnPaint(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
79 {
80     RECT rc, rcClient;
81     GetClientRect(&rcClient);
82 
83     PAINTSTRUCT ps;
84     HDC hDC = BeginPaint(&ps);
85 
86     /* To avoid flickering, we use a memory bitmap.
87        The left and top values are zeros in client rectangle */
88     HDC hMemDC = ::CreateCompatibleDC(hDC);
89     m_hbmCached = CachedBufferDIB(m_hbmCached, rcClient.right, rcClient.bottom);
90     HGDIOBJ hbmOld = ::SelectObject(hMemDC, m_hbmCached);
91 
92     /* Fill the background (since WM_ERASEBKGND handling is disabled) */
93     ::FillRect(hMemDC, &rcClient, (HBRUSH)(COLOR_3DFACE + 1));
94 
95     /* Draw the big box that contains the black box and the white box */
96     rc = { X_MARGIN, Y_MARGIN, X_MARGIN + CXY_BIGBOX, Y_MARGIN + CXY_BIGBOX };
97     ::DrawEdge(hMemDC, &rc, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
98     COLORREF rgbLight = ::GetSysColor(COLOR_3DHIGHLIGHT);
99     for (INT y = rc.top; y < rc.bottom; ++y)
100     {
101         BOOL bLight = (y & 1);
102         for (INT x = rc.left; x < rc.right; ++x)
103         {
104             if (bLight)
105                 ::SetPixelV(hMemDC, x, y, rgbLight);
106             bLight = !bLight;
107         }
108     }
109 
110     /* Draw the white box in the big box, at 5/8 position */
111     rc.left = X_MARGIN + (CXY_BIGBOX * 5 / 8) - (CXY_SELECTEDBOX / 2);
112     rc.top = Y_MARGIN + (CXY_BIGBOX * 5 / 8) - (CXY_SELECTEDBOX / 2);
113     rc.right = rc.left + CXY_SELECTEDBOX;
114     rc.bottom = rc.top + CXY_SELECTEDBOX;
115     drawColorBox(hMemDC, &rc, paletteModel.GetBgColor(), BDR_RAISEDINNER);
116 
117     /* Draw the black box (overlapping the white box), at 3/8 position */
118     rc.left = X_MARGIN + (CXY_BIGBOX * 3 / 8) - (CXY_SELECTEDBOX / 2);
119     rc.top = Y_MARGIN + (CXY_BIGBOX * 3 / 8) - (CXY_SELECTEDBOX / 2);
120     rc.right = rc.left + CXY_SELECTEDBOX;
121     rc.bottom = rc.top + CXY_SELECTEDBOX;
122     drawColorBox(hMemDC, &rc, paletteModel.GetFgColor(), BDR_RAISEDINNER);
123 
124     /* Draw the normal color boxes */
125     for (INT i = 0; i < COLOR_COUNT; i++)
126     {
127         getColorBoxRect(&rc, rcClient, i);
128         drawColorBox(hMemDC, &rc, paletteModel.GetColor(i), BDR_SUNKENOUTER);
129     }
130 
131     /* Transfer bits (hDC <-- hMemDC) */
132     ::BitBlt(hDC, 0, 0, rcClient.right, rcClient.bottom, hMemDC, 0, 0, SRCCOPY);
133 
134     ::SelectObject(hMemDC, hbmOld);
135     ::DeleteDC(hMemDC);
136     EndPaint(&ps);
137     return 0;
138 }
139 
OnLButtonDown(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)140 LRESULT CPaletteWindow::OnLButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
141 {
142     INT iColor = DoHitTest(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
143     if (iColor != -1)
144         paletteModel.SetFgColor(paletteModel.GetColor(iColor));
145     SetCapture();
146     return 0;
147 }
148 
OnRButtonDown(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)149 LRESULT CPaletteWindow::OnRButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
150 {
151     INT iColor = DoHitTest(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
152     if (iColor != -1)
153         paletteModel.SetBgColor(paletteModel.GetColor(iColor));
154     return 0;
155 }
156 
OnLButtonDblClk(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)157 LRESULT CPaletteWindow::OnLButtonDblClk(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
158 {
159     INT iColor = DoHitTest(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
160     COLORREF rgbColor = paletteModel.GetFgColor();
161     if (iColor != -1 && mainWindow.ChooseColor(&rgbColor))
162     {
163         paletteModel.SetColor(iColor, rgbColor);
164         paletteModel.SetFgColor(rgbColor);
165     }
166     return 0;
167 }
168 
OnRButtonDblClk(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)169 LRESULT CPaletteWindow::OnRButtonDblClk(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
170 {
171     INT iColor = DoHitTest(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
172     COLORREF rgbColor = paletteModel.GetBgColor();
173     if (iColor != -1 && mainWindow.ChooseColor(&rgbColor))
174     {
175         paletteModel.SetColor(iColor, rgbColor);
176         paletteModel.SetBgColor(rgbColor);
177     }
178     return 0;
179 }
180 
OnPaletteModelColorChanged(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)181 LRESULT CPaletteWindow::OnPaletteModelColorChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
182 {
183     Invalidate(FALSE);
184     return 0;
185 }
186 
OnMouseMove(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)187 LRESULT CPaletteWindow::OnMouseMove(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
188 {
189     if (::GetCapture() != m_hWnd)
190         return 0;
191 
192     POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
193     ClientToScreen(&pt);
194 
195     RECT rc;
196     mainWindow.GetWindowRect(&rc);
197 
198     POINT ptCenter = { (rc.left + rc.right) / 2, (rc.top + rc.bottom) / 2 };
199 
200     DWORD dwExpectedBar1ID = ((pt.y < ptCenter.y) ? BAR1ID_TOP : BAR1ID_BOTTOM);
201 
202     if (registrySettings.Bar1ID != dwExpectedBar1ID)
203     {
204         registrySettings.Bar1ID = dwExpectedBar1ID;
205         mainWindow.PostMessage(WM_SIZE, 0, 0);
206     }
207 
208     return 0;
209 }
210 
OnLButtonUp(UINT nMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandled)211 LRESULT CPaletteWindow::OnLButtonUp(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
212 {
213     if (::GetCapture() != m_hWnd)
214         return 0;
215 
216     ::ReleaseCapture();
217     return 0;
218 }
219