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