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