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