Authors: Chris Morgan <cmorgan@wpi.edu>, James Abbatiello <abbeyj@wpi.edu>
[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 "debug.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(progress, \
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(progress, "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 LRESULT WINAPI ProgressWindowProc(HWND hwnd, UINT message, 
197                                   WPARAM wParam, LPARAM lParam)
198 {
199     PROGRESS_INFO *infoPtr = PROGRESS_GetInfoPtr(hwnd); 
200   UINT temp;
201
202   switch(message)
203     {
204     case WM_NCCREATE:
205             {
206                 DWORD dwExStyle;
207                 dwExStyle = GetWindowLongA (hwnd, GWL_EXSTYLE);
208                 SetWindowLongA (hwnd, GWL_EXSTYLE, dwExStyle | WS_EX_STATICEDGE);
209             }
210       return TRUE;
211
212     case WM_CREATE:
213       /* allocate memory for info struct */
214       infoPtr = 
215         (PROGRESS_INFO *)COMCTL32_Alloc (sizeof(PROGRESS_INFO));
216       SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
217
218       /* initialize the info struct */
219       infoPtr->MinVal=0; 
220       infoPtr->MaxVal=100;
221       infoPtr->CurVal=0; 
222       infoPtr->Step=10;
223       infoPtr->ColorBar=CLR_DEFAULT;
224       infoPtr->ColorBk=CLR_DEFAULT;
225       infoPtr->hFont=(HANDLE)NULL;
226       TRACE(progress, "Progress Ctrl creation, hwnd=%04x\n", hwnd);
227       break;
228     
229     case WM_DESTROY:
230       TRACE (progress, "Progress Ctrl destruction, hwnd=%04x\n", hwnd);
231       COMCTL32_Free (infoPtr);
232       break;
233
234     case WM_ERASEBKGND:
235       /* pretend to erase it here, but we will do it in the paint
236          function to avoid flicker */
237       return 1;
238         
239     case WM_GETFONT:
240       return (LRESULT)infoPtr->hFont;
241
242     case WM_SETFONT:
243       return PROGRESS_SetFont (hwnd, wParam, lParam);
244
245     case WM_PAINT:
246       PROGRESS_Paint (hwnd);
247       break;
248     
249     case PBM_DELTAPOS:
250       if(lParam)
251         UNKNOWN_PARAM(PBM_DELTAPOS, wParam, lParam);
252       temp = infoPtr->CurVal;
253       if(wParam != 0){
254         infoPtr->CurVal += (UINT16)wParam;
255         PROGRESS_CoercePos (hwnd);
256         PROGRESS_Refresh (hwnd);
257       }
258       return temp;
259
260     case PBM_SETPOS:
261       if (lParam)
262         UNKNOWN_PARAM(PBM_SETPOS, wParam, lParam);
263       temp = infoPtr->CurVal;
264       if(temp != wParam){
265         infoPtr->CurVal = (UINT16)wParam;
266         PROGRESS_CoercePos(hwnd);
267         PROGRESS_Refresh (hwnd);
268       }
269       return temp;          
270       
271     case PBM_SETRANGE:
272       if (wParam)
273         UNKNOWN_PARAM(PBM_SETRANGE, wParam, lParam);
274       temp = MAKELONG(infoPtr->MinVal, infoPtr->MaxVal);
275       if(temp != lParam){
276         infoPtr->MinVal = LOWORD(lParam); 
277         infoPtr->MaxVal = HIWORD(lParam);
278         if(infoPtr->MaxVal <= infoPtr->MinVal)
279           infoPtr->MaxVal = infoPtr->MinVal+1;
280         PROGRESS_CoercePos(hwnd);
281         PROGRESS_Refresh (hwnd);
282       }
283       return temp;
284
285     case PBM_SETSTEP:
286       if (lParam)
287         UNKNOWN_PARAM(PBM_SETSTEP, wParam, lParam);
288       temp = infoPtr->Step;   
289       infoPtr->Step = (UINT16)wParam;   
290       return temp;
291
292     case PBM_STEPIT:
293       if (wParam || lParam)
294         UNKNOWN_PARAM(PBM_STEPIT, wParam, lParam);
295       temp = infoPtr->CurVal;   
296       infoPtr->CurVal += infoPtr->Step;
297       if(infoPtr->CurVal > infoPtr->MaxVal)
298         infoPtr->CurVal = infoPtr->MinVal;
299       if(temp != infoPtr->CurVal)
300         PROGRESS_Refresh (hwnd);
301       return temp;
302
303     case PBM_SETRANGE32:
304       temp = MAKELONG(infoPtr->MinVal, infoPtr->MaxVal);
305       if((infoPtr->MinVal != (INT)wParam) ||
306          (infoPtr->MaxVal != (INT)lParam)) {
307         infoPtr->MinVal = (INT)wParam;
308         infoPtr->MaxVal = (INT)lParam;
309         if(infoPtr->MaxVal <= infoPtr->MinVal)
310           infoPtr->MaxVal = infoPtr->MinVal+1;
311         PROGRESS_CoercePos(hwnd);
312         PROGRESS_Refresh (hwnd);
313       }
314       return temp;
315     
316     case PBM_GETRANGE:
317       if (lParam){
318         ((PPBRANGE)lParam)->iLow = infoPtr->MinVal;
319         ((PPBRANGE)lParam)->iHigh = infoPtr->MaxVal;
320       }
321       return (wParam) ? infoPtr->MinVal : infoPtr->MaxVal;
322
323     case PBM_GETPOS:
324       if (wParam || lParam)
325         UNKNOWN_PARAM(PBM_STEPIT, wParam, lParam);
326       return (infoPtr->CurVal);
327
328     case PBM_SETBARCOLOR:
329       if (wParam)
330         UNKNOWN_PARAM(PBM_SETBARCOLOR, wParam, lParam);
331       infoPtr->ColorBar = (COLORREF)lParam;     
332       PROGRESS_Refresh (hwnd);
333       break;
334
335     case PBM_SETBKCOLOR:
336       if (wParam)
337         UNKNOWN_PARAM(PBM_SETBKCOLOR, wParam, lParam);
338       infoPtr->ColorBk = (COLORREF)lParam;
339       PROGRESS_Refresh (hwnd);
340       break;
341
342     default: 
343       if (message >= WM_USER) 
344         ERR(progress, "unknown msg %04x wp=%04x lp=%08lx\n", 
345                     message, wParam, lParam );
346       return DefWindowProcA( hwnd, message, wParam, lParam ); 
347     } 
348
349     return 0;
350 }
351
352
353 /***********************************************************************
354  * PROGRESS_Register [Internal]
355  *
356  * Registers the progress bar window class.
357  */
358
359 VOID
360 PROGRESS_Register (VOID)
361 {
362     WNDCLASSA wndClass;
363
364     if (GlobalFindAtomA (PROGRESS_CLASSA)) return;
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     if (GlobalFindAtomA (PROGRESS_CLASSA))
388         UnregisterClassA (PROGRESS_CLASSA, (HINSTANCE)NULL);
389 }
390