Started a unit test case for the updown control.
[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 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  */
31
32 #include <stdarg.h>
33 #include <string.h>
34 #include "windef.h"
35 #include "winbase.h"
36 #include "wingdi.h"
37 #include "winuser.h"
38 #include "winnls.h"
39 #include "commctrl.h"
40 #include "comctl32.h"
41 #include "wine/debug.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(progress);
44
45 typedef struct
46 {
47     HWND      Self;         /* The window handle for this control */
48     INT       CurVal;       /* Current progress value */
49     INT       MinVal;       /* Minimum progress value */
50     INT       MaxVal;       /* Maximum progress value */
51     INT       Step;         /* Step to use on PMB_STEPIT */
52     INT       MarqueePos;   /* Marquee animation position */
53     BOOL      Marquee;      /* Whether the marquee animation is enabled */
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 #define MARQUEE_LEDS      5
63 #define ID_MARQUEE_TIMER  1
64
65 /***********************************************************************
66  * PROGRESS_Invalidate
67  *
68  * Invalide the range between old and new pos.
69  */
70 static void PROGRESS_Invalidate( PROGRESS_INFO *infoPtr, INT old, INT new )
71 {
72     LONG style = GetWindowLongW (infoPtr->Self, GWL_STYLE);
73     RECT rect;
74     int oldPos, newPos, ledWidth;
75
76     GetClientRect (infoPtr->Self, &rect);
77     InflateRect(&rect, -1, -1);
78
79     if (style & PBS_VERTICAL)
80     {
81         oldPos = rect.bottom - MulDiv (old - infoPtr->MinVal, rect.bottom - rect.top,
82                                        infoPtr->MaxVal - infoPtr->MinVal);
83         newPos = rect.bottom - MulDiv (new - infoPtr->MinVal, rect.bottom - rect.top,
84                                        infoPtr->MaxVal - infoPtr->MinVal);
85         ledWidth = MulDiv (rect.right - rect.left, 2, 3);
86         rect.top = min( oldPos, newPos );
87         rect.bottom = max( oldPos, newPos );
88         if (!(style & PBS_SMOOTH)) rect.top -= ledWidth;
89         InvalidateRect( infoPtr->Self, &rect, oldPos < newPos );
90     }
91     else
92     {
93         oldPos = rect.left + MulDiv (old - infoPtr->MinVal, rect.right - rect.left,
94                                      infoPtr->MaxVal - infoPtr->MinVal);
95         newPos = rect.left + MulDiv (new - infoPtr->MinVal, rect.right - rect.left,
96                                      infoPtr->MaxVal - infoPtr->MinVal);
97         ledWidth = MulDiv (rect.bottom - rect.top, 2, 3);
98         rect.left = min( oldPos, newPos );
99         rect.right = max( oldPos, newPos );
100         if (!(style & PBS_SMOOTH)) rect.right += ledWidth;
101         InvalidateRect( infoPtr->Self, &rect, oldPos > newPos );
102     }
103 }
104
105
106 /***********************************************************************
107  * PROGRESS_Draw
108  * Draws the progress bar.
109  */
110 static LRESULT PROGRESS_Draw (PROGRESS_INFO *infoPtr, HDC hdc)
111 {
112     HBRUSH hbrBar, hbrBk;
113     int rightBar, rightMost, ledWidth;
114     RECT rect;
115     DWORD dwStyle;
116
117     TRACE("(infoPtr=%p, hdc=%p)\n", infoPtr, hdc);
118
119     /* get the required bar brush */
120     if (infoPtr->ColorBar == CLR_DEFAULT)
121         hbrBar = GetSysColorBrush(COLOR_HIGHLIGHT);
122     else
123         hbrBar = CreateSolidBrush (infoPtr->ColorBar);
124
125     if (infoPtr->ColorBk == CLR_DEFAULT)
126         hbrBk = GetSysColorBrush(COLOR_3DFACE);
127     else
128         hbrBk = CreateSolidBrush(infoPtr->ColorBk);
129
130     /* get client rectangle */
131     GetClientRect (infoPtr->Self, &rect);
132     FrameRect( hdc, &rect, hbrBk );
133     InflateRect(&rect, -1, -1);
134
135     /* get the window style */
136     dwStyle = GetWindowLongW (infoPtr->Self, GWL_STYLE);
137
138     /* compute extent of progress bar */
139     if (dwStyle & PBS_VERTICAL) {
140         rightBar  = rect.bottom -
141                     MulDiv (infoPtr->CurVal - infoPtr->MinVal,
142                             rect.bottom - rect.top,
143                             infoPtr->MaxVal - infoPtr->MinVal);
144         ledWidth  = MulDiv (rect.right - rect.left, 2, 3);
145         rightMost = rect.top;
146     } else {
147         rightBar = rect.left +
148                    MulDiv (infoPtr->CurVal - infoPtr->MinVal,
149                            rect.right - rect.left,
150                            infoPtr->MaxVal - infoPtr->MinVal);
151         ledWidth = MulDiv (rect.bottom - rect.top, 2, 3);
152         rightMost = rect.right;
153     }
154
155     /* now draw the bar */
156     if (dwStyle & PBS_SMOOTH)
157     {
158         if (dwStyle & PBS_VERTICAL)
159         {
160             if (dwStyle & PBS_MARQUEE)
161             {
162                 INT old_top, old_bottom, ledMStart, leds;
163                 old_top = rect.top;
164                 old_bottom = rect.bottom;
165
166                 leds = rect.bottom - rect.top;
167                 ledMStart = (infoPtr->MarqueePos + MARQUEE_LEDS) - leds;
168                 
169                 if(ledMStart > 0)
170                 {
171                     rect.top = max(rect.bottom - ledMStart, old_top);
172                     FillRect(hdc, &rect, hbrBar);
173                     rect.bottom = rect.top;
174                 }
175                 if(infoPtr->MarqueePos > 0)
176                 {
177                     rect.top = max(old_bottom - infoPtr->MarqueePos, old_top);
178                     FillRect(hdc, &rect, hbrBk);
179                     rect.bottom = rect.top;
180                 }
181                 if(rect.top >= old_top)
182                 {
183                     rect.top = max(rect.bottom - MARQUEE_LEDS, old_top);
184                     FillRect(hdc, &rect, hbrBar);
185                     rect.bottom = rect.top;
186                 }
187                 if(rect.top >= old_top)
188                 {
189                     rect.top = old_top;
190                     FillRect(hdc, &rect, hbrBk);
191                 }
192             }
193             else
194             {
195                 INT old_top = rect.top;
196                 rect.top = rightBar;
197                 FillRect(hdc, &rect, hbrBar);
198                 rect.bottom = rect.top;
199                 rect.top = old_top;
200                 FillRect(hdc, &rect, hbrBk);
201             }
202         }
203         else
204         {
205             if (dwStyle & PBS_MARQUEE)
206             {
207                 INT old_left, old_right, ledMStart, leds;
208                 old_left = rect.left;
209                 old_right = rect.right;
210
211                 leds = rect.right - rect.left;
212                 ledMStart = (infoPtr->MarqueePos + MARQUEE_LEDS) - leds;
213                 rect.right = rect.left;
214                 
215                 if(ledMStart > 0)
216                 {
217                     rect.right = min(rect.left + ledMStart, old_right);
218                     FillRect(hdc, &rect, hbrBar);
219                     rect.left = rect.right;
220                 }
221                 if(infoPtr->MarqueePos > 0)
222                 {
223                     rect.right = min(old_left + infoPtr->MarqueePos, old_right);
224                     FillRect(hdc, &rect, hbrBk);
225                     rect.left = rect.right;
226                 }
227                 if(rect.right < old_right)
228                 {
229                     rect.right = min(rect.left + MARQUEE_LEDS, old_right);
230                     FillRect(hdc, &rect, hbrBar);
231                     rect.left = rect.right;
232                 }
233                 if(rect.right < old_right)
234                 {
235                     rect.right = old_right;
236                     FillRect(hdc, &rect, hbrBk);
237                 }
238             }
239             else
240             {
241                 INT old_right = rect.right;
242                 rect.right = rightBar;
243                 FillRect(hdc, &rect, hbrBar);
244                 rect.left = rect.right;
245                 rect.right = old_right;
246                 FillRect(hdc, &rect, hbrBk);
247             }
248         }
249     } else {
250         if (dwStyle & PBS_VERTICAL) {
251             if (dwStyle & PBS_MARQUEE)
252             {
253                 INT i, old_top, old_bottom, ledMStart, leds;
254                 old_top = rect.top;
255                 old_bottom = rect.bottom;
256
257                 leds = ((rect.bottom - rect.top) + (ledWidth + LED_GAP) - 1) / (ledWidth + LED_GAP);
258                 ledMStart = (infoPtr->MarqueePos + MARQUEE_LEDS) - leds;
259                 
260                 while(ledMStart > 0)
261                 {
262                     rect.top = max(rect.bottom - ledWidth, old_top);
263                     FillRect(hdc, &rect, hbrBar);
264                     rect.bottom = rect.top;
265                     rect.top -= LED_GAP;
266                     if (rect.top <= old_top) break;
267                     FillRect(hdc, &rect, hbrBk);
268                     rect.bottom = rect.top;
269                     ledMStart--;
270                 }
271                 if(infoPtr->MarqueePos > 0)
272                 {
273                     rect.top = max(old_bottom - (infoPtr->MarqueePos * (ledWidth + LED_GAP)), old_top);
274                     FillRect(hdc, &rect, hbrBk);
275                     rect.bottom = rect.top;
276                 }
277                 for(i = 0; i < MARQUEE_LEDS && rect.top >= old_top; i++)
278                 {
279                     rect.top = max(rect.bottom - ledWidth, old_top);
280                     FillRect(hdc, &rect, hbrBar);
281                     rect.bottom = rect.top;
282                     rect.top -= LED_GAP;
283                     if (rect.top <= old_top) break;
284                     FillRect(hdc, &rect, hbrBk);
285                     rect.bottom = rect.top;
286                 }
287                 if(rect.top >= old_top)
288                 {
289                     rect.top = old_top;
290                     FillRect(hdc, &rect, hbrBk);
291                 }
292             }
293             else
294             {
295                 while(rect.bottom > rightBar) {
296                     rect.top = rect.bottom - ledWidth;
297                     if (rect.top < rightMost)
298                         rect.top = rightMost;
299                     FillRect(hdc, &rect, hbrBar);
300                     rect.bottom = rect.top;
301                     rect.top -= LED_GAP;
302                     if (rect.top <= rightBar) break;
303                     FillRect(hdc, &rect, hbrBk);
304                     rect.bottom = rect.top;
305                 }
306             }
307             rect.top = rightMost;
308             FillRect(hdc, &rect, hbrBk);
309         } else {
310             if (dwStyle & PBS_MARQUEE)
311             {
312                 INT i, old_right, old_left, ledMStart, leds;
313                 old_left = rect.left;
314                 old_right = rect.right;
315
316                 leds = ((rect.right - rect.left) + ledWidth - 1) / (ledWidth + LED_GAP);
317                 ledMStart = (infoPtr->MarqueePos + MARQUEE_LEDS) - leds;
318                 rect.right = rect.left;
319                 
320                 while(ledMStart > 0)
321                 {
322                     rect.right = min(rect.left + ledWidth, old_right);
323                     FillRect(hdc, &rect, hbrBar);
324                     rect.left = rect.right;
325                     rect.right += LED_GAP;
326                     if (rect.right > old_right) break;
327                     FillRect(hdc, &rect, hbrBk);
328                     rect.left = rect.right;
329                     ledMStart--;
330                 }
331                 if(infoPtr->MarqueePos > 0)
332                 {
333                     rect.right = min(old_left + (infoPtr->MarqueePos * (ledWidth + LED_GAP)), old_right);
334                     FillRect(hdc, &rect, hbrBk);
335                     rect.left = rect.right;
336                 }
337                 for(i = 0; i < MARQUEE_LEDS && rect.right < old_right; i++)
338                 {
339                     rect.right = min(rect.left + ledWidth, old_right);
340                     FillRect(hdc, &rect, hbrBar);
341                     rect.left = rect.right;
342                     rect.right += LED_GAP;
343                     if (rect.right > old_right) break;
344                     FillRect(hdc, &rect, hbrBk);
345                     rect.left = rect.right;
346                 }
347                 if(rect.right < old_right)
348                 {
349                     rect.right = old_right;
350                     FillRect(hdc, &rect, hbrBk);
351                 }
352             }
353             else
354             {
355                 while(rect.left < rightBar) {
356                     rect.right = rect.left + ledWidth;
357                     if (rect.right > rightMost)
358                         rect.right = rightMost;
359                     FillRect(hdc, &rect, hbrBar);
360                     rect.left = rect.right;
361                     rect.right += LED_GAP;
362                     if (rect.right >= rightBar) break;
363                     FillRect(hdc, &rect, hbrBk);
364                     rect.left = rect.right;
365                 }
366                 rect.right = rightMost;
367                 FillRect(hdc, &rect, hbrBk);
368             }
369         }
370     }
371
372     /* delete bar brush */
373     if (infoPtr->ColorBar != CLR_DEFAULT) DeleteObject (hbrBar);
374     if (infoPtr->ColorBk != CLR_DEFAULT) DeleteObject (hbrBk);
375
376     return 0;
377 }
378
379
380 /***********************************************************************
381  * PROGRESS_Paint
382  * Draw the progress bar. The background need not be erased.
383  * If dc!=0, it draws on it
384  */
385 static LRESULT PROGRESS_Paint (PROGRESS_INFO *infoPtr, HDC hdc)
386 {
387     PAINTSTRUCT ps;
388     if (hdc) return PROGRESS_Draw (infoPtr, hdc);
389     hdc = BeginPaint (infoPtr->Self, &ps);
390     PROGRESS_Draw (infoPtr, hdc);
391     EndPaint (infoPtr->Self, &ps);
392     return 0;
393 }
394
395
396 /***********************************************************************
397  * PROGRESS_Timer
398  * Handle the marquee timer messages
399  */
400 static LRESULT PROGRESS_Timer (PROGRESS_INFO *infoPtr, INT idTimer)
401 {
402     if(idTimer == ID_MARQUEE_TIMER)
403     {
404         LONG style = GetWindowLongW (infoPtr->Self, GWL_STYLE);
405         RECT rect;
406         int ledWidth, leds;
407
408         GetClientRect (infoPtr->Self, &rect);
409         InflateRect(&rect, -1, -1);
410
411         if(!(style & PBS_SMOOTH))
412         {
413             int width, height;
414
415             if(style & PBS_VERTICAL)
416             {
417                 width = rect.bottom - rect.top;
418                 height = rect.right - rect.left;
419             }
420             else
421             {
422                 height = rect.bottom - rect.top;
423                 width = rect.right - rect.left;
424             }
425             ledWidth = MulDiv (height, 2, 3);
426             leds = (width + ledWidth - 1) / (ledWidth + LED_GAP);
427         }
428         else
429         {
430             ledWidth = 1;
431             if(style & PBS_VERTICAL)
432             {
433                 leds = rect.bottom - rect.top;
434             }
435             else
436             {
437                 leds = rect.right - rect.left;
438             }
439         }
440
441         /* increment the marquee progress */
442         if(++infoPtr->MarqueePos >= leds)
443         {
444             infoPtr->MarqueePos = 0;
445         }
446
447         InvalidateRect(infoPtr->Self, &rect, TRUE);
448     }
449     return 0;
450 }
451
452
453 /***********************************************************************
454  *           PROGRESS_CoercePos
455  * Makes sure the current position (CurVal) is within bounds.
456  */
457 static void PROGRESS_CoercePos(PROGRESS_INFO *infoPtr)
458 {
459     if(infoPtr->CurVal < infoPtr->MinVal)
460         infoPtr->CurVal = infoPtr->MinVal;
461     if(infoPtr->CurVal > infoPtr->MaxVal)
462         infoPtr->CurVal = infoPtr->MaxVal;
463 }
464
465
466 /***********************************************************************
467  *           PROGRESS_SetFont
468  * Set new Font for progress bar
469  */
470 static HFONT PROGRESS_SetFont (PROGRESS_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
471 {
472     HFONT hOldFont = infoPtr->Font;
473     infoPtr->Font = hFont;
474     /* Since infoPtr->Font is not used, there is no need for repaint */
475     return hOldFont;
476 }
477
478 static DWORD PROGRESS_SetRange (PROGRESS_INFO *infoPtr, int low, int high)
479 {
480     DWORD res = MAKELONG(LOWORD(infoPtr->MinVal), LOWORD(infoPtr->MaxVal));
481
482     /* if nothing changes, simply return */
483     if(infoPtr->MinVal == low && infoPtr->MaxVal == high) return res;
484
485     infoPtr->MinVal = low;
486     infoPtr->MaxVal = high;
487     PROGRESS_CoercePos(infoPtr);
488     InvalidateRect(infoPtr->Self, NULL, TRUE);
489     return res;
490 }
491
492 /***********************************************************************
493  *           ProgressWindowProc
494  */
495 static LRESULT WINAPI ProgressWindowProc(HWND hwnd, UINT message,
496                                          WPARAM wParam, LPARAM lParam)
497 {
498     PROGRESS_INFO *infoPtr;
499
500     TRACE("hwnd=%p msg=%04x wparam=%x lParam=%lx\n", hwnd, message, wParam, lParam);
501
502     infoPtr = (PROGRESS_INFO *)GetWindowLongPtrW(hwnd, 0);
503
504     if (!infoPtr && message != WM_CREATE)
505         return DefWindowProcW( hwnd, message, wParam, lParam );
506
507     switch(message) {
508     case WM_CREATE:
509     {
510         DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE);
511         dwExStyle &= ~(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
512         dwExStyle |= WS_EX_STATICEDGE;
513         SetWindowLongW (hwnd, GWL_EXSTYLE, dwExStyle);
514         /* Force recalculation of a non-client area */
515         SetWindowPos(hwnd, 0, 0, 0, 0, 0,
516             SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
517
518         /* allocate memory for info struct */
519         infoPtr = (PROGRESS_INFO *)Alloc (sizeof(PROGRESS_INFO));
520         if (!infoPtr) return -1;
521         SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
522
523         /* initialize the info struct */
524         infoPtr->Self = hwnd;
525         infoPtr->MinVal = 0;
526         infoPtr->MaxVal = 100;
527         infoPtr->CurVal = 0;
528         infoPtr->Step = 10;
529         infoPtr->MarqueePos = 0;
530         infoPtr->Marquee = FALSE;
531         infoPtr->ColorBar = CLR_DEFAULT;
532         infoPtr->ColorBk = CLR_DEFAULT;
533         infoPtr->Font = 0;
534         TRACE("Progress Ctrl creation, hwnd=%p\n", hwnd);
535         return 0;
536     }
537
538     case WM_DESTROY:
539         TRACE("Progress Ctrl destruction, hwnd=%p\n", hwnd);
540         Free (infoPtr);
541         SetWindowLongPtrW(hwnd, 0, 0);
542         return 0;
543
544     case WM_GETFONT:
545         return (LRESULT)infoPtr->Font;
546
547     case WM_SETFONT:
548         return (LRESULT)PROGRESS_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
549
550     case WM_PAINT:
551         return PROGRESS_Paint (infoPtr, (HDC)wParam);
552
553     case WM_TIMER:
554         return PROGRESS_Timer (infoPtr, (INT)wParam);
555
556     case PBM_DELTAPOS:
557     {
558         INT oldVal;
559         oldVal = infoPtr->CurVal;
560         if(wParam != 0) {
561             infoPtr->CurVal += (INT)wParam;
562             PROGRESS_CoercePos (infoPtr);
563             TRACE("PBM_DELTAPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
564             PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
565         }
566         return oldVal;
567     }
568
569     case PBM_SETPOS:
570     {
571         UINT oldVal;
572         oldVal = infoPtr->CurVal;
573         if(oldVal != wParam) {
574             infoPtr->CurVal = (INT)wParam;
575             PROGRESS_CoercePos(infoPtr);
576             TRACE("PBM_SETPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
577             PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
578         }
579         return oldVal;
580     }
581
582     case PBM_SETRANGE:
583         return PROGRESS_SetRange (infoPtr, (int)LOWORD(lParam), (int)HIWORD(lParam));
584
585     case PBM_SETSTEP:
586     {
587         INT oldStep;
588         oldStep = infoPtr->Step;
589         infoPtr->Step = (INT)wParam;
590         return oldStep;
591     }
592
593     case PBM_STEPIT:
594     {
595         INT oldVal;
596         oldVal = infoPtr->CurVal;
597         infoPtr->CurVal += infoPtr->Step;
598         if(infoPtr->CurVal > infoPtr->MaxVal)
599             infoPtr->CurVal = infoPtr->MinVal;
600         if(oldVal != infoPtr->CurVal)
601         {
602             TRACE("PBM_STEPIT: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
603             PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
604         }
605         return oldVal;
606     }
607
608     case PBM_SETRANGE32:
609         return PROGRESS_SetRange (infoPtr, (int)wParam, (int)lParam);
610
611     case PBM_GETRANGE:
612         if (lParam) {
613             ((PPBRANGE)lParam)->iLow = infoPtr->MinVal;
614             ((PPBRANGE)lParam)->iHigh = infoPtr->MaxVal;
615         }
616         return wParam ? infoPtr->MinVal : infoPtr->MaxVal;
617
618     case PBM_GETPOS:
619         return infoPtr->CurVal;
620
621     case PBM_SETBARCOLOR:
622         infoPtr->ColorBar = (COLORREF)lParam;
623         InvalidateRect(hwnd, NULL, TRUE);
624         return 0;
625
626     case PBM_SETBKCOLOR:
627         infoPtr->ColorBk = (COLORREF)lParam;
628         InvalidateRect(hwnd, NULL, TRUE);
629         return 0;
630
631     case PBM_SETMARQUEE:
632         if(wParam != 0)
633         {
634             infoPtr->Marquee = TRUE;
635             SetTimer(infoPtr->Self, ID_MARQUEE_TIMER, (UINT)lParam, NULL);
636         }
637         else
638         {
639             infoPtr->Marquee = FALSE;
640             KillTimer(infoPtr->Self, ID_MARQUEE_TIMER);
641         }
642         return infoPtr->Marquee;
643
644     default:
645         if ((message >= WM_USER) && (message < WM_APP))
646             ERR("unknown msg %04x wp=%04x lp=%08lx\n", message, wParam, lParam );
647         return DefWindowProcW( hwnd, message, wParam, lParam );
648     }
649 }
650
651
652 /***********************************************************************
653  * PROGRESS_Register [Internal]
654  *
655  * Registers the progress bar window class.
656  */
657 void PROGRESS_Register (void)
658 {
659     WNDCLASSW wndClass;
660
661     ZeroMemory (&wndClass, sizeof(wndClass));
662     wndClass.style         = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
663     wndClass.lpfnWndProc   = (WNDPROC)ProgressWindowProc;
664     wndClass.cbClsExtra    = 0;
665     wndClass.cbWndExtra    = sizeof (PROGRESS_INFO *);
666     wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
667     wndClass.lpszClassName = PROGRESS_CLASSW;
668
669     RegisterClassW (&wndClass);
670 }
671
672
673 /***********************************************************************
674  * PROGRESS_Unregister [Internal]
675  *
676  * Unregisters the progress bar window class.
677  */
678 void PROGRESS_Unregister (void)
679 {
680     UnregisterClassW (PROGRESS_CLASSW, NULL);
681 }