Added regedit unit test, a couple minor changes to regedit.
[wine] / dlls / comctl32 / progress.c
1 /*
2  * Progress control
3  *
4  * Copyright 1997, 2002 Dimitrie O. Paun
5  * Copyright 1998, 1999 Eric Kohl
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include <string.h>
23 #include "winbase.h"
24 #include "commctrl.h"
25 #include "wine/debug.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(progress);
28
29 typedef struct
30 {
31     HWND      Self;         /* The window handle for this control */
32     INT       CurVal;       /* Current progress value */
33     INT       MinVal;       /* Minimum progress value */
34     INT       MaxVal;       /* Maximum progress value */
35     INT       Step;         /* Step to use on PMB_STEPIT */
36     COLORREF  ColorBar;     /* Bar color */
37     COLORREF  ColorBk;      /* Background color */
38     HFONT     Font;         /* Handle to font (not unused) */
39 } PROGRESS_INFO;
40
41 /* Control configuration constants */
42
43 #define LED_GAP    2
44
45 #define UNKNOWN_PARAM(msg, wParam, lParam) WARN(       \
46    "Unknown parameter(s) for message " #msg            \
47    "(%04x): wp=%04x lp=%08lx\n", msg, wParam, lParam);
48
49 /***********************************************************************
50  * PROGRESS_EraseBackground
51  */
52 static void PROGRESS_EraseBackground(PROGRESS_INFO *infoPtr, WPARAM wParam)
53 {
54     RECT rect;
55     HBRUSH hbrBk;
56     HDC hdc = wParam ? (HDC)wParam : GetDC(infoPtr->Self);
57
58     /* get the required background brush */
59     if(infoPtr->ColorBk == CLR_DEFAULT)
60         hbrBk = GetSysColorBrush(COLOR_3DFACE);
61     else
62         hbrBk = CreateSolidBrush(infoPtr->ColorBk);
63
64     /* get client rectangle */
65     GetClientRect(infoPtr->Self, &rect);
66
67     /* draw the background */
68     FillRect(hdc, &rect, hbrBk);
69
70     /* delete background brush */
71     if(infoPtr->ColorBk != CLR_DEFAULT)
72         DeleteObject (hbrBk);
73
74     if(!wParam) ReleaseDC(infoPtr->Self, hdc);
75 }
76
77 /***********************************************************************
78  * PROGRESS_Draw
79  * Draws the progress bar.
80  */
81 static LRESULT PROGRESS_Draw (PROGRESS_INFO *infoPtr, HDC hdc)
82 {
83     HBRUSH hbrBar;
84     int rightBar, rightMost, ledWidth;
85     RECT rect;
86     DWORD dwStyle;
87
88     TRACE("(infoPtr=%p, hdc=%x)\n", infoPtr, hdc);
89
90     /* get the required bar brush */
91     if (infoPtr->ColorBar == CLR_DEFAULT)
92         hbrBar = GetSysColorBrush(COLOR_HIGHLIGHT);
93     else
94         hbrBar = CreateSolidBrush (infoPtr->ColorBar);
95
96     /* get client rectangle */
97     GetClientRect (infoPtr->Self, &rect);
98
99     InflateRect(&rect, -1, -1);
100
101     /* get the window style */
102     dwStyle = GetWindowLongW (infoPtr->Self, GWL_STYLE);
103
104     /* compute extent of progress bar */
105     if (dwStyle & PBS_VERTICAL) {
106         rightBar  = rect.bottom -
107                     MulDiv (infoPtr->CurVal - infoPtr->MinVal,
108                             rect.bottom - rect.top,
109                             infoPtr->MaxVal - infoPtr->MinVal);
110         ledWidth  = MulDiv (rect.right - rect.left, 2, 3);
111         rightMost = rect.top;
112     } else {
113         rightBar = rect.left +
114                    MulDiv (infoPtr->CurVal - infoPtr->MinVal,
115                            rect.right - rect.left,
116                            infoPtr->MaxVal - infoPtr->MinVal);
117         ledWidth = MulDiv (rect.bottom - rect.top, 2, 3);
118         rightMost = rect.right;
119     }
120
121     /* now draw the bar */
122     if (dwStyle & PBS_SMOOTH) {
123         if (dwStyle & PBS_VERTICAL)
124             rect.top = rightBar;
125         else
126             rect.right = rightBar;
127         FillRect(hdc, &rect, hbrBar);
128     } else {
129         if (dwStyle & PBS_VERTICAL) {
130             while(rect.bottom > rightBar) {
131                 rect.top = rect.bottom - ledWidth;
132                 if (rect.top < rightMost)
133                     rect.top = rightMost;
134                 FillRect(hdc, &rect, hbrBar);
135                 rect.bottom = rect.top - LED_GAP;
136             }
137         } else {
138             while(rect.left < rightBar) {
139                 rect.right = rect.left + ledWidth;
140                 if (rect.right > rightMost)
141                     rect.right = rightMost;
142                 FillRect(hdc, &rect, hbrBar);
143                 rect.left  = rect.right + LED_GAP;
144             }
145         }
146     }
147
148     /* delete bar brush */
149     if (infoPtr->ColorBar != CLR_DEFAULT)
150         DeleteObject (hbrBar);
151
152     return 0;
153 }
154
155
156 /***********************************************************************
157  * PROGRESS_Paint
158  * Draw the progress bar. The background need not be erased.
159  * If dc!=0, it draws on it
160  */
161 static LRESULT PROGRESS_Paint (PROGRESS_INFO *infoPtr, HDC hdc)
162 {
163     PAINTSTRUCT ps;
164     if (hdc) return PROGRESS_Draw (infoPtr, hdc);
165     hdc = BeginPaint (infoPtr->Self, &ps);
166     PROGRESS_Draw (infoPtr, hdc);
167     EndPaint (infoPtr->Self, &ps);
168     return 0;
169 }
170
171
172 /***********************************************************************
173  *           PROGRESS_CoercePos
174  * Makes sure the current position (CurVal) is within bounds.
175  */
176 static void PROGRESS_CoercePos(PROGRESS_INFO *infoPtr)
177 {
178     if(infoPtr->CurVal < infoPtr->MinVal)
179         infoPtr->CurVal = infoPtr->MinVal;
180     if(infoPtr->CurVal > infoPtr->MaxVal)
181         infoPtr->CurVal = infoPtr->MaxVal;
182 }
183
184
185 /***********************************************************************
186  *           PROGRESS_SetFont
187  * Set new Font for progress bar
188  */
189 static HFONT PROGRESS_SetFont (PROGRESS_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
190 {
191     HFONT hOldFont = infoPtr->Font;
192     infoPtr->Font = hFont;
193     /* Since infoPtr->Font is not used, there is no need for repaint */
194     return hOldFont;
195 }
196
197 static DWORD PROGRESS_SetRange (PROGRESS_INFO *infoPtr, int low, int high)
198 {
199     DWORD res = MAKELONG(LOWORD(infoPtr->MinVal), LOWORD(infoPtr->MaxVal));
200
201     /* if nothing changes, simply return */
202     if(infoPtr->MinVal == low && infoPtr->MaxVal == high) return res;
203
204     infoPtr->MinVal = low;
205     infoPtr->MaxVal = high;
206     PROGRESS_CoercePos(infoPtr);
207     return res;
208 }
209
210 /***********************************************************************
211  *           ProgressWindowProc
212  */
213 static LRESULT WINAPI ProgressWindowProc(HWND hwnd, UINT message,
214                                   WPARAM wParam, LPARAM lParam)
215 {
216     PROGRESS_INFO *infoPtr;
217
218     TRACE("hwnd=%x msg=%04x wparam=%x lParam=%lx\n", hwnd, message, wParam, lParam);
219
220     infoPtr = (PROGRESS_INFO *)GetWindowLongW(hwnd, 0);
221
222     if (!infoPtr && message != WM_CREATE)
223         return DefWindowProcW( hwnd, message, wParam, lParam );
224
225     switch(message) {
226     case WM_CREATE:
227     {
228         DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE);
229         dwExStyle &= ~(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
230         dwExStyle |= WS_EX_STATICEDGE;
231         SetWindowLongW (hwnd, GWL_EXSTYLE, dwExStyle);
232         /* Force recalculation of a non-client area */
233         SetWindowPos(hwnd, 0, 0, 0, 0, 0,
234             SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
235
236         /* allocate memory for info struct */
237         infoPtr = (PROGRESS_INFO *)COMCTL32_Alloc (sizeof(PROGRESS_INFO));
238         if (!infoPtr) return -1;
239         SetWindowLongW (hwnd, 0, (DWORD)infoPtr);
240
241         /* initialize the info struct */
242         infoPtr->Self = hwnd;
243         infoPtr->MinVal = 0;
244         infoPtr->MaxVal = 100;
245         infoPtr->CurVal = 0;
246         infoPtr->Step = 10;
247         infoPtr->ColorBar = CLR_DEFAULT;
248         infoPtr->ColorBk = CLR_DEFAULT;
249         infoPtr->Font = 0;
250         TRACE("Progress Ctrl creation, hwnd=%04x\n", hwnd);
251         return 0;
252     }
253
254     case WM_DESTROY:
255         TRACE("Progress Ctrl destruction, hwnd=%04x\n", hwnd);
256         COMCTL32_Free (infoPtr);
257         SetWindowLongW(hwnd, 0, 0);
258         return 0;
259
260     case WM_ERASEBKGND:
261         PROGRESS_EraseBackground(infoPtr, wParam);
262         return TRUE;
263
264     case WM_GETFONT:
265         return (LRESULT)infoPtr->Font;
266
267     case WM_SETFONT:
268         return PROGRESS_SetFont (infoPtr, (HFONT)wParam, (BOOL)lParam);
269
270     case WM_PAINT:
271         return PROGRESS_Paint (infoPtr, (HDC)wParam);
272
273     case PBM_DELTAPOS:
274     {
275         INT oldVal;
276         if(lParam) UNKNOWN_PARAM(PBM_DELTAPOS, wParam, lParam);
277         oldVal = infoPtr->CurVal;
278         if(wParam != 0) {
279             BOOL bErase;
280             infoPtr->CurVal += (INT)wParam;
281             PROGRESS_CoercePos (infoPtr);
282             TRACE("PBM_DELTAPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
283             bErase = (oldVal > infoPtr->CurVal);
284             InvalidateRect(hwnd, NULL, bErase);
285         }
286         return oldVal;
287     }
288
289     case PBM_SETPOS:
290     {
291         INT oldVal;
292         if (lParam) UNKNOWN_PARAM(PBM_SETPOS, wParam, lParam);
293         oldVal = infoPtr->CurVal;
294         if(oldVal != wParam) {
295             BOOL bErase;
296             infoPtr->CurVal = (INT)wParam;
297             PROGRESS_CoercePos(infoPtr);
298             TRACE("PBM_SETPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
299             bErase = (oldVal > infoPtr->CurVal);
300             InvalidateRect(hwnd, NULL, bErase);
301         }
302         return oldVal;
303     }
304
305     case PBM_SETRANGE:
306         if (wParam) UNKNOWN_PARAM(PBM_SETRANGE, wParam, lParam);
307         return PROGRESS_SetRange (infoPtr, (int)LOWORD(lParam), (int)HIWORD(lParam));
308
309     case PBM_SETSTEP:
310     {
311         INT oldStep;
312         if (lParam) UNKNOWN_PARAM(PBM_SETSTEP, wParam, lParam);
313         oldStep = infoPtr->Step;
314         infoPtr->Step = (INT)wParam;
315         return oldStep;
316     }
317
318     case PBM_STEPIT:
319     {
320         INT oldVal;
321         if (wParam || lParam) UNKNOWN_PARAM(PBM_STEPIT, wParam, lParam);
322         oldVal = infoPtr->CurVal;
323         infoPtr->CurVal += infoPtr->Step;
324         if(infoPtr->CurVal > infoPtr->MaxVal)
325             infoPtr->CurVal = infoPtr->MinVal;
326         if(oldVal != infoPtr->CurVal)
327         {
328             BOOL bErase;
329             TRACE("PBM_STEPIT: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
330             bErase = (oldVal > infoPtr->CurVal);
331             InvalidateRect(hwnd, NULL, bErase);
332         }
333         return oldVal;
334     }
335
336     case PBM_SETRANGE32:
337         return PROGRESS_SetRange (infoPtr, (int)wParam, (int)lParam);
338
339     case PBM_GETRANGE:
340         if (lParam) {
341             ((PPBRANGE)lParam)->iLow = infoPtr->MinVal;
342             ((PPBRANGE)lParam)->iHigh = infoPtr->MaxVal;
343         }
344         return wParam ? infoPtr->MinVal : infoPtr->MaxVal;
345
346     case PBM_GETPOS:
347         if (wParam || lParam) UNKNOWN_PARAM(PBM_STEPIT, wParam, lParam);
348         return infoPtr->CurVal;
349
350     case PBM_SETBARCOLOR:
351         if (wParam) UNKNOWN_PARAM(PBM_SETBARCOLOR, wParam, lParam);
352         infoPtr->ColorBar = (COLORREF)lParam;
353         InvalidateRect(hwnd, NULL, TRUE);
354         return 0;
355
356     case PBM_SETBKCOLOR:
357         if (wParam) UNKNOWN_PARAM(PBM_SETBKCOLOR, wParam, lParam);
358         infoPtr->ColorBk = (COLORREF)lParam;
359         InvalidateRect(hwnd, NULL, TRUE);
360         return 0;
361
362     default:
363         if (message >= WM_USER)
364             ERR("unknown msg %04x wp=%04x lp=%08lx\n", message, wParam, lParam );
365         return DefWindowProcW( hwnd, message, wParam, lParam );
366     }
367 }
368
369
370 /***********************************************************************
371  * PROGRESS_Register [Internal]
372  *
373  * Registers the progress bar window class.
374  */
375 VOID PROGRESS_Register (void)
376 {
377     WNDCLASSW wndClass;
378
379     ZeroMemory (&wndClass, sizeof(wndClass));
380     wndClass.style         = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
381     wndClass.lpfnWndProc   = (WNDPROC)ProgressWindowProc;
382     wndClass.cbClsExtra    = 0;
383     wndClass.cbWndExtra    = sizeof (PROGRESS_INFO *);
384     wndClass.hCursor       = LoadCursorW (0, IDC_ARROWW);
385     wndClass.lpszClassName = PROGRESS_CLASSW;
386
387     RegisterClassW (&wndClass);
388 }
389
390
391 /***********************************************************************
392  * PROGRESS_Unregister [Internal]
393  *
394  * Unregisters the progress bar window class.
395  */
396 VOID PROGRESS_Unregister (void)
397 {
398     UnregisterClassW (PROGRESS_CLASSW, (HINSTANCE)NULL);
399 }
400