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