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