d3dx9_36/tests: Add a '\n' to a trace() call.
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 believe 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  *
32  * Styles:
33  *    -- PBS_SMOOTHREVERSE
34  *
35  */
36
37 #include <stdarg.h>
38 #include <string.h>
39 #include "windef.h"
40 #include "winbase.h"
41 #include "wingdi.h"
42 #include "winuser.h"
43 #include "winnls.h"
44 #include "commctrl.h"
45 #include "comctl32.h"
46 #include "uxtheme.h"
47 #include "tmschema.h"
48 #include "wine/debug.h"
49
50 WINE_DEFAULT_DEBUG_CHANNEL(progress);
51
52 typedef struct
53 {
54     HWND      Self;         /* The window handle for this control */
55     INT       CurVal;       /* Current progress value */
56     INT       MinVal;       /* Minimum progress value */
57     INT       MaxVal;       /* Maximum progress value */
58     INT       Step;         /* Step to use on PMB_STEPIT */
59     INT       MarqueePos;   /* Marquee animation position */
60     BOOL      Marquee;      /* Whether the marquee animation is enabled */
61     COLORREF  ColorBar;     /* Bar color */
62     COLORREF  ColorBk;      /* Background color */
63     HFONT     Font;         /* Handle to font (not unused) */
64 } PROGRESS_INFO;
65
66 /* Control configuration constants */
67
68 #define LED_GAP           2
69 #define MARQUEE_LEDS      5
70 #define ID_MARQUEE_TIMER  1
71
72 /* Helper to obtain size of a progress bar chunk ("led"). */
73 static inline int get_led_size ( const PROGRESS_INFO *infoPtr, LONG style,
74                                  const RECT* rect )
75 {
76     HTHEME theme = GetWindowTheme (infoPtr->Self);
77     if (theme)
78     {
79         int chunkSize;
80         if (SUCCEEDED( GetThemeInt( theme, 0, 0, TMT_PROGRESSCHUNKSIZE, &chunkSize )))
81             return chunkSize;
82     }
83
84     if (style & PBS_VERTICAL)
85         return MulDiv (rect->right - rect->left, 2, 3);
86     else
87         return MulDiv (rect->bottom - rect->top, 2, 3);
88 }
89
90 /* Helper to obtain gap between progress bar chunks */
91 static inline int get_led_gap ( const PROGRESS_INFO *infoPtr )
92 {
93     HTHEME theme = GetWindowTheme (infoPtr->Self);
94     if (theme)
95     {
96         int spaceSize;
97         if (SUCCEEDED( GetThemeInt( theme, 0, 0, TMT_PROGRESSSPACESIZE, &spaceSize )))
98             return spaceSize;
99     }
100
101     return LED_GAP;
102 }
103
104 /* Get client rect. Takes into account that theming needs no adjustment. */
105 static inline void get_client_rect (HWND hwnd, RECT* rect)
106 {
107     HTHEME theme = GetWindowTheme (hwnd);
108     GetClientRect (hwnd, rect);
109     if (!theme)
110         InflateRect(rect, -1, -1);
111     else
112     {
113         DWORD dwStyle = GetWindowLongW (hwnd, GWL_STYLE);
114         int part = (dwStyle & PBS_VERTICAL) ? PP_BARVERT : PP_BAR;
115         GetThemeBackgroundContentRect (theme, 0, part, 0, rect, rect);
116     }
117 }
118
119 /* Compute the extend of the bar */
120 static inline int get_bar_size( LONG style, const RECT* rect )
121 {
122     if (style & PBS_VERTICAL)
123         return rect->bottom - rect->top;
124     else
125         return rect->right - rect->left;
126 }
127
128 /* Compute the pixel position of a progress value */
129 static inline int get_bar_position( const PROGRESS_INFO *infoPtr, LONG style,
130                                     const RECT* rect, INT value )
131 {
132     return MulDiv (value - infoPtr->MinVal, get_bar_size (style, rect),
133                       infoPtr->MaxVal - infoPtr->MinVal);
134 }
135
136 /***********************************************************************
137  * PROGRESS_Invalidate
138  *
139  * Don't be too clever about invalidating the progress bar.
140  * InstallShield depends on this simple behaviour.
141  */
142 static void PROGRESS_Invalidate( const PROGRESS_INFO *infoPtr, INT old, INT new )
143 {
144     InvalidateRect( infoPtr->Self, NULL, old > new );
145 }
146
147 /* Information for a progress bar drawing helper */
148 typedef struct tagProgressDrawInfo
149 {
150     HDC hdc;
151     RECT rect;
152     HBRUSH hbrBar;
153     HBRUSH hbrBk;
154     int ledW, ledGap;
155     HTHEME theme;
156     RECT bgRect;
157 } ProgressDrawInfo;
158
159 typedef void (*ProgressDrawProc)(const ProgressDrawInfo* di, int start, int end);
160
161 /* draw solid horizontal bar from 'start' to 'end' */
162 static void draw_solid_bar_H (const ProgressDrawInfo* di, int start, int end)
163 {
164     RECT r;
165     r.left = di->rect.left + start;
166     r.top = di->rect.top;
167     r.right = di->rect.left + end;
168     r.bottom = di->rect.bottom;
169     FillRect (di->hdc, &r, di->hbrBar);
170 }
171
172 /* draw solid horizontal background from 'start' to 'end' */
173 static void draw_solid_bkg_H (const ProgressDrawInfo* di, int start, int end)
174 {
175     RECT r;
176     r.left = di->rect.left + start;
177     r.top = di->rect.top;
178     r.right = di->rect.left + end;
179     r.bottom = di->rect.bottom;
180     FillRect (di->hdc, &r, di->hbrBk);
181 }
182
183 /* draw solid vertical bar from 'start' to 'end' */
184 static void draw_solid_bar_V (const ProgressDrawInfo* di, int start, int end)
185 {
186     RECT r;
187     r.left = di->rect.left;
188     r.top = di->rect.bottom - end;
189     r.right = di->rect.right;
190     r.bottom = di->rect.bottom - start;
191     FillRect (di->hdc, &r, di->hbrBar);
192 }
193
194 /* draw solid vertical background from 'start' to 'end' */
195 static void draw_solid_bkg_V (const ProgressDrawInfo* di, int start, int end)
196 {
197     RECT r;
198     r.left = di->rect.left;
199     r.top = di->rect.bottom - end;
200     r.right = di->rect.right;
201     r.bottom = di->rect.bottom - start;
202     FillRect (di->hdc, &r, di->hbrBk);
203 }
204
205 /* draw chunky horizontal bar from 'start' to 'end' */
206 static void draw_chunk_bar_H (const ProgressDrawInfo* di, int start, int end)
207 {
208     RECT r;
209     int right = di->rect.left + end;
210     r.left = di->rect.left + start;
211     r.top = di->rect.top;
212     r.bottom = di->rect.bottom;
213     while (r.left < right)
214     {
215         r.right = min (r.left + di->ledW, right);
216         FillRect (di->hdc, &r, di->hbrBar);
217         r.left = r.right;
218         r.right = min (r.left + di->ledGap, right);
219         FillRect (di->hdc, &r, di->hbrBk);
220         r.left = r.right;
221     }
222 }
223
224 /* draw chunky vertical bar from 'start' to 'end' */
225 static void draw_chunk_bar_V (const ProgressDrawInfo* di, int start, int end)
226 {
227     RECT r;
228     int top = di->rect.bottom - end;
229     r.left = di->rect.left;
230     r.right = di->rect.right;
231     r.bottom = di->rect.bottom - start;
232     while (r.bottom > top)
233     {
234         r.top = max (r.bottom - di->ledW, top);
235         FillRect (di->hdc, &r, di->hbrBar);
236         r.bottom = r.top;
237         r.top = max (r.bottom - di->ledGap, top);
238         FillRect (di->hdc, &r, di->hbrBk);
239         r.bottom = r.top;
240     }
241 }
242
243 /* drawing functions for "classic" style */
244 static const ProgressDrawProc drawProcClassic[8] = {
245   /* Smooth */
246     /* Horizontal */
247     draw_solid_bar_H, draw_solid_bkg_H,
248     /* Vertical */
249     draw_solid_bar_V, draw_solid_bkg_V,
250   /* Chunky */
251     /* Horizontal */
252     draw_chunk_bar_H, draw_solid_bkg_H,
253     /* Vertical */
254     draw_chunk_bar_V, draw_solid_bkg_V,
255 };
256
257 /* draw themed horizontal bar from 'start' to 'end' */
258 static void draw_theme_bar_H (const ProgressDrawInfo* di, int start, int end)
259 {
260     RECT r;
261     r.left = di->rect.left + start;
262     r.top = di->rect.top;
263     r.bottom = di->rect.bottom;
264     r.right = di->rect.left + end;
265     DrawThemeBackground (di->theme, di->hdc, PP_CHUNK, 0, &r, NULL);
266 }
267
268 /* draw themed vertical bar from 'start' to 'end' */
269 static void draw_theme_bar_V (const ProgressDrawInfo* di, int start, int end)
270 {
271     RECT r;
272     r.left = di->rect.left;
273     r.right = di->rect.right;
274     r.bottom = di->rect.bottom - start;
275     r.top = di->rect.bottom - end;
276     DrawThemeBackground (di->theme, di->hdc, PP_CHUNKVERT, 0, &r, NULL);
277 }
278
279 /* draw themed horizontal background from 'start' to 'end' */
280 static void draw_theme_bkg_H (const ProgressDrawInfo* di, int start, int end)
281 {
282     RECT r;
283     r.left = di->rect.left + start;
284     r.top = di->rect.top;
285     r.right = di->rect.left + end;
286     r.bottom = di->rect.bottom;
287     DrawThemeBackground (di->theme, di->hdc, PP_BAR, 0, &di->bgRect, &r);
288 }
289
290 /* draw themed vertical background from 'start' to 'end' */
291 static void draw_theme_bkg_V (const ProgressDrawInfo* di, int start, int end)
292 {
293     RECT r;
294     r.left = di->rect.left;
295     r.top = di->rect.bottom - end;
296     r.right = di->rect.right;
297     r.bottom = di->rect.bottom - start;
298     DrawThemeBackground (di->theme, di->hdc, PP_BARVERT, 0, &di->bgRect, &r);
299 }
300
301 /* drawing functions for themed style */
302 static const ProgressDrawProc drawProcThemed[8] = {
303   /* Smooth */
304     /* Horizontal */
305     draw_theme_bar_H, draw_theme_bkg_H,
306     /* Vertical */
307     draw_theme_bar_V, draw_theme_bkg_V,
308   /* Chunky */
309     /* Horizontal */
310     draw_theme_bar_H, draw_theme_bkg_H,
311     /* Vertical */
312     draw_theme_bar_V, draw_theme_bkg_V,
313 };
314
315 /***********************************************************************
316  * PROGRESS_Draw
317  * Draws the progress bar.
318  */
319 static LRESULT PROGRESS_Draw (PROGRESS_INFO *infoPtr, HDC hdc)
320 {
321     int barSize;
322     DWORD dwStyle;
323     BOOL barSmooth;
324     const ProgressDrawProc* drawProcs;
325     ProgressDrawInfo pdi;
326
327     TRACE("(infoPtr=%p, hdc=%p)\n", infoPtr, hdc);
328
329     pdi.hdc = hdc;
330     pdi.theme = GetWindowTheme (infoPtr->Self);
331
332     /* get the required bar brush */
333     if (infoPtr->ColorBar == CLR_DEFAULT)
334         pdi.hbrBar = GetSysColorBrush(COLOR_HIGHLIGHT);
335     else
336         pdi.hbrBar = CreateSolidBrush (infoPtr->ColorBar);
337
338     if (infoPtr->ColorBk == CLR_DEFAULT)
339         pdi.hbrBk = GetSysColorBrush(COLOR_3DFACE);
340     else
341         pdi.hbrBk = CreateSolidBrush(infoPtr->ColorBk);
342
343     /* get the window style */
344     dwStyle = GetWindowLongW (infoPtr->Self, GWL_STYLE);
345
346     /* get client rectangle */
347     GetClientRect (infoPtr->Self, &pdi.rect);
348     if (!pdi.theme) {
349         FrameRect( hdc, &pdi.rect, pdi.hbrBk );
350         InflateRect(&pdi.rect, -1, -1);
351     }
352     else
353     {
354         RECT cntRect;
355         int part = (dwStyle & PBS_VERTICAL) ? PP_BARVERT : PP_BAR;
356         
357         GetThemeBackgroundContentRect (pdi.theme, hdc, part, 0, &pdi.rect, 
358             &cntRect);
359         
360         /* Exclude content rect - content background will be drawn later */
361         ExcludeClipRect (hdc, cntRect.left, cntRect.top, 
362             cntRect.right, cntRect.bottom);
363         if (IsThemeBackgroundPartiallyTransparent (pdi.theme, part, 0))
364             DrawThemeParentBackground (infoPtr->Self, hdc, NULL);
365         DrawThemeBackground (pdi.theme, hdc, part, 0, &pdi.rect, NULL);
366         SelectClipRgn (hdc, NULL);
367         CopyRect (&pdi.rect, &cntRect);
368     }
369
370     /* compute some drawing parameters */
371     barSmooth = (dwStyle & PBS_SMOOTH) && !pdi.theme;
372     drawProcs = &((pdi.theme ? drawProcThemed : drawProcClassic)[(barSmooth ? 0 : 4)
373         + ((dwStyle & PBS_VERTICAL) ? 2 : 0)]);
374     barSize = get_bar_size( dwStyle, &pdi.rect );
375     if (pdi.theme)
376     {
377         GetWindowRect( infoPtr->Self, &pdi.bgRect );
378         MapWindowPoints( infoPtr->Self, 0, (POINT*)&pdi.bgRect, 2 );
379     }
380
381     if (!barSmooth)
382         pdi.ledW = get_led_size( infoPtr, dwStyle, &pdi.rect);
383     pdi.ledGap = get_led_gap( infoPtr );
384
385     if (dwStyle & PBS_MARQUEE)
386     {
387         const int ledW = !barSmooth ? (pdi.ledW + pdi.ledGap) : 1;
388         const int leds = (barSize + ledW - 1) / ledW;
389         const int ledMEnd = infoPtr->MarqueePos + MARQUEE_LEDS;
390
391         if (ledMEnd > leds)
392         {
393             /* case 1: the marquee bar extends over the end and wraps around to 
394              * the start */
395             const int gapStart = max((ledMEnd - leds) * ledW, 0);
396             const int gapEnd = min(infoPtr->MarqueePos * ledW, barSize);
397
398             drawProcs[0]( &pdi, 0, gapStart);
399             drawProcs[1]( &pdi, gapStart, gapEnd);
400             drawProcs[0]( &pdi, gapEnd, barSize);
401         }
402         else
403         {
404             /* case 2: the marquee bar is between start and end */
405             const int barStart = infoPtr->MarqueePos * ledW;
406             const int barEnd = min (ledMEnd * ledW, barSize);
407
408             drawProcs[1]( &pdi, 0, barStart);
409             drawProcs[0]( &pdi, barStart, barEnd);
410             drawProcs[1]( &pdi, barEnd, barSize);
411         }
412     }
413     else
414     {
415         int barEnd = get_bar_position( infoPtr, dwStyle, &pdi.rect,
416             infoPtr->CurVal);
417         if (!barSmooth)
418         {
419             const int ledW = pdi.ledW + pdi.ledGap;
420             barEnd = min (((barEnd + ledW - 1) / ledW) * ledW, barSize);
421         }
422         drawProcs[0]( &pdi, 0, barEnd);
423         drawProcs[1]( &pdi, barEnd, barSize);
424     }
425
426     /* delete bar brush */
427     if (infoPtr->ColorBar != CLR_DEFAULT) DeleteObject (pdi.hbrBar);
428     if (infoPtr->ColorBk != CLR_DEFAULT) DeleteObject (pdi.hbrBk);
429
430     return 0;
431 }
432
433 /***********************************************************************
434  * PROGRESS_Paint
435  * Draw the progress bar. The background need not be erased.
436  * If dc!=0, it draws on it
437  */
438 static LRESULT PROGRESS_Paint (PROGRESS_INFO *infoPtr, HDC hdc)
439 {
440     PAINTSTRUCT ps;
441     if (hdc) return PROGRESS_Draw (infoPtr, hdc);
442     hdc = BeginPaint (infoPtr->Self, &ps);
443     PROGRESS_Draw (infoPtr, hdc);
444     EndPaint (infoPtr->Self, &ps);
445     return 0;
446 }
447
448
449 /***********************************************************************
450  * PROGRESS_Timer
451  * Handle the marquee timer messages
452  */
453 static LRESULT PROGRESS_Timer (PROGRESS_INFO *infoPtr, INT idTimer)
454 {
455     if(idTimer == ID_MARQUEE_TIMER)
456     {
457         LONG style = GetWindowLongW (infoPtr->Self, GWL_STYLE);
458         RECT rect;
459         int ledWidth, leds;
460         HTHEME theme = GetWindowTheme (infoPtr->Self);
461         BOOL barSmooth = (style & PBS_SMOOTH) && !theme;
462
463         get_client_rect (infoPtr->Self, &rect);
464
465         if(!barSmooth)
466             ledWidth = get_led_size( infoPtr, style, &rect ) + 
467                 get_led_gap( infoPtr );
468         else
469             ledWidth = 1;
470
471         leds = (get_bar_size( style, &rect ) + ledWidth - 1) / 
472             ledWidth;
473
474         /* increment the marquee progress */
475         if(++infoPtr->MarqueePos >= leds)
476         {
477             infoPtr->MarqueePos = 0;
478         }
479
480         InvalidateRect(infoPtr->Self, &rect, FALSE);
481         UpdateWindow(infoPtr->Self);
482     }
483     return 0;
484 }
485
486
487 /***********************************************************************
488  *           PROGRESS_CoercePos
489  * Makes sure the current position (CurVal) is within bounds.
490  */
491 static void PROGRESS_CoercePos(PROGRESS_INFO *infoPtr)
492 {
493     if(infoPtr->CurVal < infoPtr->MinVal)
494         infoPtr->CurVal = infoPtr->MinVal;
495     if(infoPtr->CurVal > infoPtr->MaxVal)
496         infoPtr->CurVal = infoPtr->MaxVal;
497 }
498
499
500 /***********************************************************************
501  *           PROGRESS_SetFont
502  * Set new Font for progress bar
503  */
504 static HFONT PROGRESS_SetFont (PROGRESS_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
505 {
506     HFONT hOldFont = infoPtr->Font;
507     infoPtr->Font = hFont;
508     /* Since infoPtr->Font is not used, there is no need for repaint */
509     return hOldFont;
510 }
511
512 static DWORD PROGRESS_SetRange (PROGRESS_INFO *infoPtr, int low, int high)
513 {
514     DWORD res = MAKELONG(LOWORD(infoPtr->MinVal), LOWORD(infoPtr->MaxVal));
515
516     /* if nothing changes, simply return */
517     if(infoPtr->MinVal == low && infoPtr->MaxVal == high) return res;
518
519     infoPtr->MinVal = low;
520     infoPtr->MaxVal = high;
521     PROGRESS_CoercePos(infoPtr);
522     InvalidateRect(infoPtr->Self, NULL, TRUE);
523     return res;
524 }
525
526 /***********************************************************************
527  *           ProgressWindowProc
528  */
529 static LRESULT WINAPI ProgressWindowProc(HWND hwnd, UINT message,
530                                          WPARAM wParam, LPARAM lParam)
531 {
532     PROGRESS_INFO *infoPtr;
533     static const WCHAR themeClass[] = {'P','r','o','g','r','e','s','s',0};
534     HTHEME theme;
535
536     TRACE("hwnd=%p msg=%04x wparam=%lx lParam=%lx\n", hwnd, message, wParam, lParam);
537
538     infoPtr = (PROGRESS_INFO *)GetWindowLongPtrW(hwnd, 0);
539
540     if (!infoPtr && message != WM_CREATE)
541         return DefWindowProcW( hwnd, message, wParam, lParam );
542
543     switch(message) {
544     case WM_CREATE:
545     {
546         DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE);
547         
548         theme = OpenThemeData (hwnd, themeClass);
549
550         dwExStyle &= ~(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
551         if (!theme) dwExStyle |= WS_EX_STATICEDGE;
552         SetWindowLongW (hwnd, GWL_EXSTYLE, dwExStyle);
553         /* Force recalculation of a non-client area */
554         SetWindowPos(hwnd, 0, 0, 0, 0, 0,
555             SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
556
557         /* allocate memory for info struct */
558         infoPtr = Alloc (sizeof(PROGRESS_INFO));
559         if (!infoPtr) return -1;
560         SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
561
562         /* initialize the info struct */
563         infoPtr->Self = hwnd;
564         infoPtr->MinVal = 0;
565         infoPtr->MaxVal = 100;
566         infoPtr->CurVal = 0;
567         infoPtr->Step = 10;
568         infoPtr->MarqueePos = 0;
569         infoPtr->Marquee = FALSE;
570         infoPtr->ColorBar = CLR_DEFAULT;
571         infoPtr->ColorBk = CLR_DEFAULT;
572         infoPtr->Font = 0;
573
574         TRACE("Progress Ctrl creation, hwnd=%p\n", hwnd);
575         return 0;
576     }
577
578     case WM_DESTROY:
579         TRACE("Progress Ctrl destruction, hwnd=%p\n", hwnd);
580         Free (infoPtr);
581         SetWindowLongPtrW(hwnd, 0, 0);
582         theme = GetWindowTheme (hwnd);
583         CloseThemeData (theme);
584         return 0;
585
586     case WM_ERASEBKGND:
587         return 1;
588
589     case WM_GETFONT:
590         return (LRESULT)infoPtr->Font;
591
592     case WM_SETFONT:
593         return (LRESULT)PROGRESS_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
594
595     case WM_PRINTCLIENT:
596     case WM_PAINT:
597         return PROGRESS_Paint (infoPtr, (HDC)wParam);
598
599     case WM_TIMER:
600         return PROGRESS_Timer (infoPtr, (INT)wParam);
601
602     case WM_THEMECHANGED:
603     {
604         DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE);
605         
606         theme = GetWindowTheme (hwnd);
607         CloseThemeData (theme);
608         theme = OpenThemeData (hwnd, themeClass);
609         
610         /* WS_EX_STATICEDGE disappears when the control is themed */
611         if (theme)
612             dwExStyle &= ~WS_EX_STATICEDGE;
613         else
614             dwExStyle |= WS_EX_STATICEDGE;
615         SetWindowLongW (hwnd, GWL_EXSTYLE, dwExStyle);
616         
617         InvalidateRect (hwnd, NULL, FALSE);
618         return 0;
619     }
620
621     case PBM_DELTAPOS:
622     {
623         INT oldVal;
624         oldVal = infoPtr->CurVal;
625         if(wParam != 0) {
626             infoPtr->CurVal += (INT)wParam;
627             PROGRESS_CoercePos (infoPtr);
628             TRACE("PBM_DELTAPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
629             PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
630             UpdateWindow( infoPtr->Self );
631         }
632         return oldVal;
633     }
634
635     case PBM_SETPOS:
636     {
637         UINT oldVal;
638         oldVal = infoPtr->CurVal;
639         if(oldVal != wParam) {
640             infoPtr->CurVal = (INT)wParam;
641             PROGRESS_CoercePos(infoPtr);
642             TRACE("PBM_SETPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
643             PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
644             UpdateWindow( infoPtr->Self );
645         }
646         return oldVal;
647     }
648
649     case PBM_SETRANGE:
650         return PROGRESS_SetRange (infoPtr, (int)LOWORD(lParam), (int)HIWORD(lParam));
651
652     case PBM_SETSTEP:
653     {
654         INT oldStep;
655         oldStep = infoPtr->Step;
656         infoPtr->Step = (INT)wParam;
657         return oldStep;
658     }
659
660     case PBM_GETSTEP:
661         return infoPtr->Step;
662
663     case PBM_STEPIT:
664     {
665         INT oldVal;
666         oldVal = infoPtr->CurVal;
667         infoPtr->CurVal += infoPtr->Step;
668         if(infoPtr->CurVal > infoPtr->MaxVal)
669             infoPtr->CurVal = infoPtr->MinVal;
670         if(oldVal != infoPtr->CurVal)
671         {
672             TRACE("PBM_STEPIT: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
673             PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
674             UpdateWindow( infoPtr->Self );
675         }
676         return oldVal;
677     }
678
679     case PBM_SETRANGE32:
680         return PROGRESS_SetRange (infoPtr, (int)wParam, (int)lParam);
681
682     case PBM_GETRANGE:
683         if (lParam) {
684             ((PPBRANGE)lParam)->iLow = infoPtr->MinVal;
685             ((PPBRANGE)lParam)->iHigh = infoPtr->MaxVal;
686         }
687         return wParam ? infoPtr->MinVal : infoPtr->MaxVal;
688
689     case PBM_GETPOS:
690         return infoPtr->CurVal;
691
692     case PBM_SETBARCOLOR:
693         infoPtr->ColorBar = (COLORREF)lParam;
694         InvalidateRect(hwnd, NULL, TRUE);
695         return 0;
696
697     case PBM_GETBARCOLOR:
698         return infoPtr->ColorBar;
699
700     case PBM_SETBKCOLOR:
701         infoPtr->ColorBk = (COLORREF)lParam;
702         InvalidateRect(hwnd, NULL, TRUE);
703         return 0;
704
705     case PBM_GETBKCOLOR:
706         return infoPtr->ColorBk;
707
708     case PBM_SETSTATE:
709         if(wParam != PBST_NORMAL)
710             FIXME("state %04lx not yet handled\n", wParam);
711         return PBST_NORMAL;
712
713     case PBM_GETSTATE:
714         return PBST_NORMAL;
715
716     case PBM_SETMARQUEE:
717         if(wParam != 0)
718         {
719             infoPtr->Marquee = TRUE;
720             SetTimer(infoPtr->Self, ID_MARQUEE_TIMER, (UINT)lParam, NULL);
721         }
722         else
723         {
724             infoPtr->Marquee = FALSE;
725             KillTimer(infoPtr->Self, ID_MARQUEE_TIMER);
726         }
727         return infoPtr->Marquee;
728
729     default:
730         if ((message >= WM_USER) && (message < WM_APP) && !COMCTL32_IsReflectedMessage(message))
731             ERR("unknown msg %04x wp=%04lx lp=%08lx\n", message, wParam, lParam );
732         return DefWindowProcW( hwnd, message, wParam, lParam );
733     }
734 }
735
736
737 /***********************************************************************
738  * PROGRESS_Register [Internal]
739  *
740  * Registers the progress bar window class.
741  */
742 void PROGRESS_Register (void)
743 {
744     WNDCLASSW wndClass;
745
746     ZeroMemory (&wndClass, sizeof(wndClass));
747     wndClass.style         = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
748     wndClass.lpfnWndProc   = (WNDPROC)ProgressWindowProc;
749     wndClass.cbClsExtra    = 0;
750     wndClass.cbWndExtra    = sizeof (PROGRESS_INFO *);
751     wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
752     wndClass.lpszClassName = PROGRESS_CLASSW;
753
754     RegisterClassW (&wndClass);
755 }
756
757
758 /***********************************************************************
759  * PROGRESS_Unregister [Internal]
760  *
761  * Unregisters the progress bar window class.
762  */
763 void PROGRESS_Unregister (void)
764 {
765     UnregisterClassW (PROGRESS_CLASSW, NULL);
766 }