Fixed definitions of TTTOOLINFOA/W_V1_SIZE and
[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  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * NOTE
22  * 
23  * This code was audited for completeness against the documented features
24  * of Comctl32.dll version 6.0 on Sep. 9, 2002, by Dimitrie O. Paun.
25  * 
26  * Unless otherwise noted, we belive this code to be complete, as per
27  * the specification mentioned above.
28  * If you discover missing features, or bugs, please note them below.
29  * 
30  * TODO
31  *   --support PBS_MARQUE
32  *
33  */
34
35 #include <stdarg.h>
36 #include <string.h>
37 #include "windef.h"
38 #include "winbase.h"
39 #include "wingdi.h"
40 #include "winuser.h"
41 #include "winnls.h"
42 #include "commctrl.h"
43 #include "wine/debug.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(progress);
46
47 typedef struct
48 {
49     HWND      Self;         /* The window handle for this control */
50     INT       CurVal;       /* Current progress value */
51     INT       MinVal;       /* Minimum progress value */
52     INT       MaxVal;       /* Maximum progress value */
53     INT       Step;         /* Step to use on PMB_STEPIT */
54     COLORREF  ColorBar;     /* Bar color */
55     COLORREF  ColorBk;      /* Background color */
56     HFONT     Font;         /* Handle to font (not unused) */
57 } PROGRESS_INFO;
58
59 /* Control configuration constants */
60
61 #define LED_GAP    2
62
63 /***********************************************************************
64  * PROGRESS_Invalidate
65  *
66  * Invalide the range between old and new pos.
67  */
68 static void PROGRESS_Invalidate( PROGRESS_INFO *infoPtr, INT old, INT new )
69 {
70     LONG style = GetWindowLongW (infoPtr->Self, GWL_STYLE);
71     RECT rect;
72     int oldPos, newPos, ledWidth;
73
74     GetClientRect (infoPtr->Self, &rect);
75     InflateRect(&rect, -1, -1);
76
77     if (style & PBS_VERTICAL)
78     {
79         oldPos = rect.bottom - MulDiv (old - infoPtr->MinVal, rect.bottom - rect.top,
80                                        infoPtr->MaxVal - infoPtr->MinVal);
81         newPos = rect.bottom - MulDiv (new - infoPtr->MinVal, rect.bottom - rect.top,
82                                        infoPtr->MaxVal - infoPtr->MinVal);
83         ledWidth = MulDiv (rect.right - rect.left, 2, 3);
84         rect.top = min( oldPos, newPos );
85         rect.bottom = max( oldPos, newPos );
86         if (!(style & PBS_SMOOTH)) rect.top -= ledWidth;
87         InvalidateRect( infoPtr->Self, &rect, oldPos < newPos );
88     }
89     else
90     {
91         oldPos = rect.left + MulDiv (old - infoPtr->MinVal, rect.right - rect.left,
92                                      infoPtr->MaxVal - infoPtr->MinVal);
93         newPos = rect.left + MulDiv (new - infoPtr->MinVal, rect.right - rect.left,
94                                      infoPtr->MaxVal - infoPtr->MinVal);
95         ledWidth = MulDiv (rect.bottom - rect.top, 2, 3);
96         rect.left = min( oldPos, newPos );
97         rect.right = max( oldPos, newPos );
98         if (!(style & PBS_SMOOTH)) rect.right += ledWidth;
99         InvalidateRect( infoPtr->Self, &rect, oldPos > newPos );
100     }
101 }
102
103
104 /***********************************************************************
105  * PROGRESS_Draw
106  * Draws the progress bar.
107  */
108 static LRESULT PROGRESS_Draw (PROGRESS_INFO *infoPtr, HDC hdc)
109 {
110     HBRUSH hbrBar, hbrBk;
111     int rightBar, rightMost, ledWidth;
112     RECT rect;
113     DWORD dwStyle;
114
115     TRACE("(infoPtr=%p, hdc=%p)\n", infoPtr, hdc);
116
117     /* get the required bar brush */
118     if (infoPtr->ColorBar == CLR_DEFAULT)
119         hbrBar = GetSysColorBrush(COLOR_HIGHLIGHT);
120     else
121         hbrBar = CreateSolidBrush (infoPtr->ColorBar);
122
123     if (infoPtr->ColorBk == CLR_DEFAULT)
124         hbrBk = GetSysColorBrush(COLOR_3DFACE);
125     else
126         hbrBk = CreateSolidBrush(infoPtr->ColorBk);
127
128     /* get client rectangle */
129     GetClientRect (infoPtr->Self, &rect);
130     FrameRect( hdc, &rect, hbrBk );
131     InflateRect(&rect, -1, -1);
132
133     /* get the window style */
134     dwStyle = GetWindowLongW (infoPtr->Self, GWL_STYLE);
135
136     /* compute extent of progress bar */
137     if (dwStyle & PBS_VERTICAL) {
138         rightBar  = rect.bottom -
139                     MulDiv (infoPtr->CurVal - infoPtr->MinVal,
140                             rect.bottom - rect.top,
141                             infoPtr->MaxVal - infoPtr->MinVal);
142         ledWidth  = MulDiv (rect.right - rect.left, 2, 3);
143         rightMost = rect.top;
144     } else {
145         rightBar = rect.left +
146                    MulDiv (infoPtr->CurVal - infoPtr->MinVal,
147                            rect.right - rect.left,
148                            infoPtr->MaxVal - infoPtr->MinVal);
149         ledWidth = MulDiv (rect.bottom - rect.top, 2, 3);
150         rightMost = rect.right;
151     }
152
153     /* now draw the bar */
154     if (dwStyle & PBS_SMOOTH)
155     {
156         if (dwStyle & PBS_VERTICAL)
157         {
158             INT old_top = rect.top;
159             rect.top = rightBar;
160             FillRect(hdc, &rect, hbrBar);
161             rect.bottom = rect.top;
162             rect.top = old_top;
163             FillRect(hdc, &rect, hbrBk);
164         }
165         else
166         {
167             INT old_right = rect.right;
168             rect.right = rightBar;
169             FillRect(hdc, &rect, hbrBar);
170             rect.left = rect.right;
171             rect.right = old_right;
172             FillRect(hdc, &rect, hbrBk);
173         }
174     } else {
175         if (dwStyle & PBS_VERTICAL) {
176             while(rect.bottom > rightBar) {
177                 rect.top = rect.bottom - ledWidth;
178                 if (rect.top < rightMost)
179                     rect.top = rightMost;
180                 FillRect(hdc, &rect, hbrBar);
181                 rect.bottom = rect.top;
182                 rect.top -= LED_GAP;
183                 if (rect.top <= rightBar) break;
184                 FillRect(hdc, &rect, hbrBk);
185                 rect.bottom = rect.top;
186             }
187             rect.top = rightMost;
188             FillRect(hdc, &rect, hbrBk);
189         } else {
190             while(rect.left < rightBar) {
191                 rect.right = rect.left + ledWidth;
192                 if (rect.right > rightMost)
193                     rect.right = rightMost;
194                 FillRect(hdc, &rect, hbrBar);
195                 rect.left = rect.right;
196                 rect.right += LED_GAP;
197                 if (rect.right >= rightBar) break;
198                 FillRect(hdc, &rect, hbrBk);
199                 rect.left = rect.right;
200             }
201             rect.right = rightMost;
202             FillRect(hdc, &rect, hbrBk);
203         }
204     }
205
206     /* delete bar brush */
207     if (infoPtr->ColorBar != CLR_DEFAULT) DeleteObject (hbrBar);
208     if (infoPtr->ColorBk != CLR_DEFAULT) DeleteObject (hbrBk);
209
210     return 0;
211 }
212
213
214 /***********************************************************************
215  * PROGRESS_Paint
216  * Draw the progress bar. The background need not be erased.
217  * If dc!=0, it draws on it
218  */
219 static LRESULT PROGRESS_Paint (PROGRESS_INFO *infoPtr, HDC hdc)
220 {
221     PAINTSTRUCT ps;
222     if (hdc) return PROGRESS_Draw (infoPtr, hdc);
223     hdc = BeginPaint (infoPtr->Self, &ps);
224     PROGRESS_Draw (infoPtr, hdc);
225     EndPaint (infoPtr->Self, &ps);
226     return 0;
227 }
228
229
230 /***********************************************************************
231  *           PROGRESS_CoercePos
232  * Makes sure the current position (CurVal) is within bounds.
233  */
234 static void PROGRESS_CoercePos(PROGRESS_INFO *infoPtr)
235 {
236     if(infoPtr->CurVal < infoPtr->MinVal)
237         infoPtr->CurVal = infoPtr->MinVal;
238     if(infoPtr->CurVal > infoPtr->MaxVal)
239         infoPtr->CurVal = infoPtr->MaxVal;
240 }
241
242
243 /***********************************************************************
244  *           PROGRESS_SetFont
245  * Set new Font for progress bar
246  */
247 static HFONT PROGRESS_SetFont (PROGRESS_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
248 {
249     HFONT hOldFont = infoPtr->Font;
250     infoPtr->Font = hFont;
251     /* Since infoPtr->Font is not used, there is no need for repaint */
252     return hOldFont;
253 }
254
255 static DWORD PROGRESS_SetRange (PROGRESS_INFO *infoPtr, int low, int high)
256 {
257     DWORD res = MAKELONG(LOWORD(infoPtr->MinVal), LOWORD(infoPtr->MaxVal));
258
259     /* if nothing changes, simply return */
260     if(infoPtr->MinVal == low && infoPtr->MaxVal == high) return res;
261
262     infoPtr->MinVal = low;
263     infoPtr->MaxVal = high;
264     PROGRESS_CoercePos(infoPtr);
265     InvalidateRect(infoPtr->Self, NULL, TRUE);
266     return res;
267 }
268
269 /***********************************************************************
270  *           ProgressWindowProc
271  */
272 static LRESULT WINAPI ProgressWindowProc(HWND hwnd, UINT message,
273                                   WPARAM wParam, LPARAM lParam)
274 {
275     PROGRESS_INFO *infoPtr;
276
277     TRACE("hwnd=%p msg=%04x wparam=%x lParam=%lx\n", hwnd, message, wParam, lParam);
278
279     infoPtr = (PROGRESS_INFO *)GetWindowLongW(hwnd, 0);
280
281     if (!infoPtr && message != WM_CREATE)
282         return DefWindowProcW( hwnd, message, wParam, lParam );
283
284     switch(message) {
285     case WM_CREATE:
286     {
287         DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE);
288         dwExStyle &= ~(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
289         dwExStyle |= WS_EX_STATICEDGE;
290         SetWindowLongW (hwnd, GWL_EXSTYLE, dwExStyle);
291         /* Force recalculation of a non-client area */
292         SetWindowPos(hwnd, 0, 0, 0, 0, 0,
293             SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
294
295         /* allocate memory for info struct */
296         infoPtr = (PROGRESS_INFO *)COMCTL32_Alloc (sizeof(PROGRESS_INFO));
297         if (!infoPtr) return -1;
298         SetWindowLongW (hwnd, 0, (DWORD)infoPtr);
299
300         /* initialize the info struct */
301         infoPtr->Self = hwnd;
302         infoPtr->MinVal = 0;
303         infoPtr->MaxVal = 100;
304         infoPtr->CurVal = 0;
305         infoPtr->Step = 10;
306         infoPtr->ColorBar = CLR_DEFAULT;
307         infoPtr->ColorBk = CLR_DEFAULT;
308         infoPtr->Font = 0;
309         TRACE("Progress Ctrl creation, hwnd=%p\n", hwnd);
310         return 0;
311     }
312
313     case WM_DESTROY:
314         TRACE("Progress Ctrl destruction, hwnd=%p\n", hwnd);
315         COMCTL32_Free (infoPtr);
316         SetWindowLongW(hwnd, 0, 0);
317         return 0;
318
319     case WM_GETFONT:
320         return (LRESULT)infoPtr->Font;
321
322     case WM_SETFONT:
323         return (LRESULT)PROGRESS_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
324
325     case WM_PAINT:
326         return PROGRESS_Paint (infoPtr, (HDC)wParam);
327
328     case PBM_DELTAPOS:
329     {
330         INT oldVal;
331         oldVal = infoPtr->CurVal;
332         if(wParam != 0) {
333             infoPtr->CurVal += (INT)wParam;
334             PROGRESS_CoercePos (infoPtr);
335             TRACE("PBM_DELTAPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
336             PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
337         }
338         return oldVal;
339     }
340
341     case PBM_SETPOS:
342     {
343         UINT oldVal;
344         oldVal = infoPtr->CurVal;
345         if(oldVal != wParam) {
346             infoPtr->CurVal = (INT)wParam;
347             PROGRESS_CoercePos(infoPtr);
348             TRACE("PBM_SETPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
349             PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
350         }
351         return oldVal;
352     }
353
354     case PBM_SETRANGE:
355         return PROGRESS_SetRange (infoPtr, (int)LOWORD(lParam), (int)HIWORD(lParam));
356
357     case PBM_SETSTEP:
358     {
359         INT oldStep;
360         oldStep = infoPtr->Step;
361         infoPtr->Step = (INT)wParam;
362         return oldStep;
363     }
364
365     case PBM_STEPIT:
366     {
367         INT oldVal;
368         oldVal = infoPtr->CurVal;
369         infoPtr->CurVal += infoPtr->Step;
370         if(infoPtr->CurVal > infoPtr->MaxVal)
371             infoPtr->CurVal = infoPtr->MinVal;
372         if(oldVal != infoPtr->CurVal)
373         {
374             TRACE("PBM_STEPIT: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
375             PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
376         }
377         return oldVal;
378     }
379
380     case PBM_SETRANGE32:
381         return PROGRESS_SetRange (infoPtr, (int)wParam, (int)lParam);
382
383     case PBM_GETRANGE:
384         if (lParam) {
385             ((PPBRANGE)lParam)->iLow = infoPtr->MinVal;
386             ((PPBRANGE)lParam)->iHigh = infoPtr->MaxVal;
387         }
388         return wParam ? infoPtr->MinVal : infoPtr->MaxVal;
389
390     case PBM_GETPOS:
391         return infoPtr->CurVal;
392
393     case PBM_SETBARCOLOR:
394         infoPtr->ColorBar = (COLORREF)lParam;
395         InvalidateRect(hwnd, NULL, TRUE);
396         return 0;
397
398     case PBM_SETBKCOLOR:
399         infoPtr->ColorBk = (COLORREF)lParam;
400         InvalidateRect(hwnd, NULL, TRUE);
401         return 0;
402
403     default:
404         if ((message >= WM_USER) && (message < WM_APP))
405             ERR("unknown msg %04x wp=%04x lp=%08lx\n", message, wParam, lParam );
406         return DefWindowProcW( hwnd, message, wParam, lParam );
407     }
408 }
409
410
411 /***********************************************************************
412  * PROGRESS_Register [Internal]
413  *
414  * Registers the progress bar window class.
415  */
416 VOID PROGRESS_Register (void)
417 {
418     WNDCLASSW wndClass;
419
420     ZeroMemory (&wndClass, sizeof(wndClass));
421     wndClass.style         = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
422     wndClass.lpfnWndProc   = (WNDPROC)ProgressWindowProc;
423     wndClass.cbClsExtra    = 0;
424     wndClass.cbWndExtra    = sizeof (PROGRESS_INFO *);
425     wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
426     wndClass.lpszClassName = PROGRESS_CLASSW;
427
428     RegisterClassW (&wndClass);
429 }
430
431
432 /***********************************************************************
433  * PROGRESS_Unregister [Internal]
434  *
435  * Unregisters the progress bar window class.
436  */
437 VOID PROGRESS_Unregister (void)
438 {
439     UnregisterClassW (PROGRESS_CLASSW, NULL);
440 }