progress.c 22.5 KB
Newer Older
1
/*
Alexandre Julliard's avatar
Alexandre Julliard committed
2 3
 * Progress control
 *
4
 * Copyright 1997, 2002 Dimitrie O. Paun
5
 * Copyright 1998, 1999 Eric Kohl
Alexandre Julliard's avatar
Alexandre Julliard committed
6
 *
7 8 9 10 11 12 13 14 15 16 17 18
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 22 23 24 25
 *
 * NOTE
 * 
 * This code was audited for completeness against the documented features
 * of Comctl32.dll version 6.0 on Sep. 9, 2002, by Dimitrie O. Paun.
 * 
Robert Shearman's avatar
Robert Shearman committed
26
 * Unless otherwise noted, we believe this code to be complete, as per
27 28 29
 * the specification mentioned above.
 * If you discover missing features, or bugs, please note them below.
 *
30 31 32 33 34
 * TODO:
 *
 * Styles:
 *    -- PBS_SMOOTHREVERSE
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
35 36
 */

37
#include <stdarg.h>
38
#include <string.h>
39
#include "windef.h"
40
#include "winbase.h"
41 42 43
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
44
#include "commctrl.h"
45
#include "comctl32.h"
46
#include "uxtheme.h"
47
#include "vssym32.h"
48
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
49

50
WINE_DEFAULT_DEBUG_CHANNEL(progress);
51 52 53

typedef struct
{
54 55 56 57 58
    HWND      Self;         /* The window handle for this control */
    INT       CurVal;       /* Current progress value */
    INT       MinVal;       /* Minimum progress value */
    INT       MaxVal;       /* Maximum progress value */
    INT       Step;         /* Step to use on PMB_STEPIT */
59 60
    INT       MarqueePos;   /* Marquee animation position */
    BOOL      Marquee;      /* Whether the marquee animation is enabled */
61 62 63
    COLORREF  ColorBar;     /* Bar color */
    COLORREF  ColorBk;      /* Background color */
    HFONT     Font;         /* Handle to font (not unused) */
64
} PROGRESS_INFO;
Alexandre Julliard's avatar
Alexandre Julliard committed
65

Alexandre Julliard's avatar
Alexandre Julliard committed
66 67
/* Control configuration constants */

68 69 70
#define LED_GAP           2
#define MARQUEE_LEDS      5
#define ID_MARQUEE_TIMER  1
Alexandre Julliard's avatar
Alexandre Julliard committed
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 77 78 79 80 81 82 83
    HTHEME theme = GetWindowTheme (infoPtr->Self);
    if (theme)
    {
        int chunkSize;
        if (SUCCEEDED( GetThemeInt( theme, 0, 0, TMT_PROGRESSCHUNKSIZE, &chunkSize )))
            return chunkSize;
    }

84 85 86 87 88 89
    if (style & PBS_VERTICAL)
        return MulDiv (rect->right - rect->left, 2, 3);
    else
        return MulDiv (rect->bottom - rect->top, 2, 3);
}

90
/* Helper to obtain gap between progress bar chunks */
91
static inline int get_led_gap ( const PROGRESS_INFO *infoPtr )
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
{
    HTHEME theme = GetWindowTheme (infoPtr->Self);
    if (theme)
    {
        int spaceSize;
        if (SUCCEEDED( GetThemeInt( theme, 0, 0, TMT_PROGRESSSPACESIZE, &spaceSize )))
            return spaceSize;
    }

    return LED_GAP;
}

/* Get client rect. Takes into account that theming needs no adjustment. */
static inline void get_client_rect (HWND hwnd, RECT* rect)
{
107
    HTHEME theme = GetWindowTheme (hwnd);
108
    GetClientRect (hwnd, rect);
109
    if (!theme)
110
        InflateRect(rect, -1, -1);
111 112 113 114 115 116
    else
    {
        DWORD dwStyle = GetWindowLongW (hwnd, GWL_STYLE);
        int part = (dwStyle & PBS_VERTICAL) ? PP_BARVERT : PP_BAR;
        GetThemeBackgroundContentRect (theme, 0, part, 0, rect, rect);
    }
117 118
}

119 120 121 122 123 124 125 126 127 128
/* Compute the extend of the bar */
static inline int get_bar_size( LONG style, const RECT* rect )
{
    if (style & PBS_VERTICAL)
        return rect->bottom - rect->top;
    else
        return rect->right - rect->left;
}

/* Compute the pixel position of a progress value */
129
static inline int get_bar_position( const PROGRESS_INFO *infoPtr, LONG style,
130 131 132 133 134 135
                                    const RECT* rect, INT value )
{
    return MulDiv (value - infoPtr->MinVal, get_bar_size (style, rect),
                      infoPtr->MaxVal - infoPtr->MinVal);
}

136
/***********************************************************************
137 138
 * PROGRESS_Invalidate
 *
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 148 149 150 151 152 153
/* Information for a progress bar drawing helper */
typedef struct tagProgressDrawInfo
{
    HDC hdc;
    RECT rect;
    HBRUSH hbrBar;
    HBRUSH hbrBk;
154 155 156
    int ledW, ledGap;
    HTHEME theme;
    RECT bgRect;
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
} ProgressDrawInfo;

typedef void (*ProgressDrawProc)(const ProgressDrawInfo* di, int start, int end);

/* draw solid horizontal bar from 'start' to 'end' */
static void draw_solid_bar_H (const ProgressDrawInfo* di, int start, int end)
{
    RECT r;
    r.left = di->rect.left + start;
    r.top = di->rect.top;
    r.right = di->rect.left + end;
    r.bottom = di->rect.bottom;
    FillRect (di->hdc, &r, di->hbrBar);
}

/* draw solid horizontal background from 'start' to 'end' */
static void draw_solid_bkg_H (const ProgressDrawInfo* di, int start, int end)
{
    RECT r;
    r.left = di->rect.left + start;
    r.top = di->rect.top;
    r.right = di->rect.left + end;
    r.bottom = di->rect.bottom;
    FillRect (di->hdc, &r, di->hbrBk);
}

/* draw solid vertical bar from 'start' to 'end' */
static void draw_solid_bar_V (const ProgressDrawInfo* di, int start, int end)
{
    RECT r;
    r.left = di->rect.left;
    r.top = di->rect.bottom - end;
    r.right = di->rect.right;
    r.bottom = di->rect.bottom - start;
    FillRect (di->hdc, &r, di->hbrBar);
}

/* draw solid vertical background from 'start' to 'end' */
static void draw_solid_bkg_V (const ProgressDrawInfo* di, int start, int end)
{
    RECT r;
    r.left = di->rect.left;
    r.top = di->rect.bottom - end;
    r.right = di->rect.right;
    r.bottom = di->rect.bottom - start;
    FillRect (di->hdc, &r, di->hbrBk);
}

/* draw chunky horizontal bar from 'start' to 'end' */
static void draw_chunk_bar_H (const ProgressDrawInfo* di, int start, int end)
{
    RECT r;
    int right = di->rect.left + end;
    r.left = di->rect.left + start;
    r.top = di->rect.top;
    r.bottom = di->rect.bottom;
    while (r.left < right)
    {
        r.right = min (r.left + di->ledW, right);
        FillRect (di->hdc, &r, di->hbrBar);
        r.left = r.right;
218
        r.right = min (r.left + di->ledGap, right);
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
        FillRect (di->hdc, &r, di->hbrBk);
        r.left = r.right;
    }
}

/* draw chunky vertical bar from 'start' to 'end' */
static void draw_chunk_bar_V (const ProgressDrawInfo* di, int start, int end)
{
    RECT r;
    int top = di->rect.bottom - end;
    r.left = di->rect.left;
    r.right = di->rect.right;
    r.bottom = di->rect.bottom - start;
    while (r.bottom > top)
    {
        r.top = max (r.bottom - di->ledW, top);
        FillRect (di->hdc, &r, di->hbrBar);
        r.bottom = r.top;
237
        r.top = max (r.bottom - di->ledGap, top);
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
        FillRect (di->hdc, &r, di->hbrBk);
        r.bottom = r.top;
    }
}

/* drawing functions for "classic" style */
static const ProgressDrawProc drawProcClassic[8] = {
  /* Smooth */
    /* Horizontal */
    draw_solid_bar_H, draw_solid_bkg_H,
    /* Vertical */
    draw_solid_bar_V, draw_solid_bkg_V,
  /* Chunky */
    /* Horizontal */
    draw_chunk_bar_H, draw_solid_bkg_H,
    /* Vertical */
    draw_chunk_bar_V, draw_solid_bkg_V,
};
256

257 258 259 260 261 262 263
/* draw themed horizontal bar from 'start' to 'end' */
static void draw_theme_bar_H (const ProgressDrawInfo* di, int start, int end)
{
    RECT r;
    r.left = di->rect.left + start;
    r.top = di->rect.top;
    r.bottom = di->rect.bottom;
264 265
    r.right = di->rect.left + end;
    DrawThemeBackground (di->theme, di->hdc, PP_CHUNK, 0, &r, NULL);
266 267
}

268
/* draw themed vertical bar from 'start' to 'end' */
269 270 271 272 273 274
static void draw_theme_bar_V (const ProgressDrawInfo* di, int start, int end)
{
    RECT r;
    r.left = di->rect.left;
    r.right = di->rect.right;
    r.bottom = di->rect.bottom - start;
275 276
    r.top = di->rect.bottom - end;
    DrawThemeBackground (di->theme, di->hdc, PP_CHUNKVERT, 0, &r, NULL);
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
}

/* draw themed horizontal background from 'start' to 'end' */
static void draw_theme_bkg_H (const ProgressDrawInfo* di, int start, int end)
{
    RECT r;
    r.left = di->rect.left + start;
    r.top = di->rect.top;
    r.right = di->rect.left + end;
    r.bottom = di->rect.bottom;
    DrawThemeBackground (di->theme, di->hdc, PP_BAR, 0, &di->bgRect, &r);
}

/* draw themed vertical background from 'start' to 'end' */
static void draw_theme_bkg_V (const ProgressDrawInfo* di, int start, int end)
{
    RECT r;
    r.left = di->rect.left;
    r.top = di->rect.bottom - end;
    r.right = di->rect.right;
    r.bottom = di->rect.bottom - start;
    DrawThemeBackground (di->theme, di->hdc, PP_BARVERT, 0, &di->bgRect, &r);
}

/* drawing functions for themed style */
static const ProgressDrawProc drawProcThemed[8] = {
  /* Smooth */
    /* Horizontal */
    draw_theme_bar_H, draw_theme_bkg_H,
    /* Vertical */
    draw_theme_bar_V, draw_theme_bkg_V,
  /* Chunky */
    /* Horizontal */
    draw_theme_bar_H, draw_theme_bkg_H,
    /* Vertical */
    draw_theme_bar_V, draw_theme_bkg_V,
};

Alexandre Julliard's avatar
Alexandre Julliard committed
315
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
316 317
 * PROGRESS_Draw
 * Draws the progress bar.
Alexandre Julliard's avatar
Alexandre Julliard committed
318
 */
319
static LRESULT PROGRESS_Draw (PROGRESS_INFO *infoPtr, HDC hdc)
Alexandre Julliard's avatar
Alexandre Julliard committed
320
{
321
    int barSize;
322
    DWORD dwStyle;
323 324 325
    BOOL barSmooth;
    const ProgressDrawProc* drawProcs;
    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 333
    /* get the required bar brush */
    if (infoPtr->ColorBar == CLR_DEFAULT)
334
        pdi.hbrBar = GetSysColorBrush(COLOR_HIGHLIGHT);
Alexandre Julliard's avatar
Alexandre Julliard committed
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 344 345

    /* get the window style */
    dwStyle = GetWindowLongW (infoPtr->Self, GWL_STYLE);

346 347
    /* get client rectangle */
    GetClientRect (infoPtr->Self, &pdi.rect);
348 349 350 351
    if (!pdi.theme) {
        FrameRect( hdc, &pdi.rect, pdi.hbrBk );
        InflateRect(&pdi.rect, -1, -1);
    }
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
    else
    {
        RECT cntRect;
        int part = (dwStyle & PBS_VERTICAL) ? PP_BARVERT : PP_BAR;
        
        GetThemeBackgroundContentRect (pdi.theme, hdc, part, 0, &pdi.rect, 
            &cntRect);
        
        /* Exclude content rect - content background will be drawn later */
        ExcludeClipRect (hdc, cntRect.left, cntRect.top, 
            cntRect.right, cntRect.bottom);
        if (IsThemeBackgroundPartiallyTransparent (pdi.theme, part, 0))
            DrawThemeParentBackground (infoPtr->Self, hdc, NULL);
        DrawThemeBackground (pdi.theme, hdc, part, 0, &pdi.rect, NULL);
        SelectClipRgn (hdc, NULL);
        CopyRect (&pdi.rect, &cntRect);
    }
369 370

    /* compute some drawing parameters */
371 372
    barSmooth = (dwStyle & PBS_SMOOTH) && !pdi.theme;
    drawProcs = &((pdi.theme ? drawProcThemed : drawProcClassic)[(barSmooth ? 0 : 4)
373 374
        + ((dwStyle & PBS_VERTICAL) ? 2 : 0)]);
    barSize = get_bar_size( dwStyle, &pdi.rect );
375 376 377
    if (pdi.theme)
    {
        GetWindowRect( infoPtr->Self, &pdi.bgRect );
378
        MapWindowPoints( infoPtr->Self, 0, (POINT*)&pdi.bgRect, 2 );
379
    }
380 381 382

    if (!barSmooth)
        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 389 390 391
        const int leds = (barSize + ledW - 1) / ledW;
        const int ledMEnd = infoPtr->MarqueePos + MARQUEE_LEDS;

        if (ledMEnd > leds)
392
        {
393 394 395 396 397 398 399 400
            /* case 1: the marquee bar extends over the end and wraps around to 
             * the start */
            const int gapStart = max((ledMEnd - leds) * ledW, 0);
            const int gapEnd = min(infoPtr->MarqueePos * ledW, barSize);

            drawProcs[0]( &pdi, 0, gapStart);
            drawProcs[1]( &pdi, gapStart, gapEnd);
            drawProcs[0]( &pdi, gapEnd, barSize);
401
        }
402
        else
403
        {
404 405 406 407 408 409 410
            /* case 2: the marquee bar is between start and end */
            const int barStart = infoPtr->MarqueePos * ledW;
            const int barEnd = min (ledMEnd * ledW, barSize);

            drawProcs[1]( &pdi, 0, barStart);
            drawProcs[0]( &pdi, barStart, barEnd);
            drawProcs[1]( &pdi, barEnd, barSize);
411
        }
412 413 414 415 416 417 418
    }
    else
    {
        int barEnd = get_bar_position( infoPtr, dwStyle, &pdi.rect,
            infoPtr->CurVal);
        if (!barSmooth)
        {
419
            const int ledW = pdi.ledW + pdi.ledGap;
420
            barEnd = min (((barEnd + ledW - 1) / ledW) * ledW, barSize);
421
        }
422 423
        drawProcs[0]( &pdi, 0, barEnd);
        drawProcs[1]( &pdi, barEnd, barSize);
424 425 426
    }

    /* delete bar brush */
427 428
    if (infoPtr->ColorBar != CLR_DEFAULT) DeleteObject (pdi.hbrBar);
    if (infoPtr->ColorBk != CLR_DEFAULT) DeleteObject (pdi.hbrBk);
429 430

    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
431
}
Alexandre Julliard's avatar
Alexandre Julliard committed
432

Alexandre Julliard's avatar
Alexandre Julliard committed
433 434 435 436 437
/***********************************************************************
 * PROGRESS_Paint
 * Draw the progress bar. The background need not be erased.
 * If dc!=0, it draws on it
 */
438
static LRESULT PROGRESS_Paint (PROGRESS_INFO *infoPtr, HDC hdc)
Alexandre Julliard's avatar
Alexandre Julliard committed
439
{
440
    PAINTSTRUCT ps;
441 442 443 444 445
    if (hdc) return PROGRESS_Draw (infoPtr, hdc);
    hdc = BeginPaint (infoPtr->Self, &ps);
    PROGRESS_Draw (infoPtr, hdc);
    EndPaint (infoPtr->Self, &ps);
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
446 447 448
}


449 450 451 452 453 454 455 456 457 458 459
/***********************************************************************
 * PROGRESS_Timer
 * Handle the marquee timer messages
 */
static LRESULT PROGRESS_Timer (PROGRESS_INFO *infoPtr, INT idTimer)
{
    if(idTimer == ID_MARQUEE_TIMER)
    {
        LONG style = GetWindowLongW (infoPtr->Self, GWL_STYLE);
        RECT rect;
        int ledWidth, leds;
460 461
        HTHEME theme = GetWindowTheme (infoPtr->Self);
        BOOL barSmooth = (style & PBS_SMOOTH) && !theme;
462

463
        get_client_rect (infoPtr->Self, &rect);
464

465 466 467
        if(!barSmooth)
            ledWidth = get_led_size( infoPtr, style, &rect ) + 
                get_led_gap( infoPtr );
468 469
        else
            ledWidth = 1;
470 471 472

        leds = (get_bar_size( style, &rect ) + ledWidth - 1) / 
            ledWidth;
473 474

        /* increment the marquee progress */
475
        if(++infoPtr->MarqueePos >= leds)
476 477 478 479
        {
            infoPtr->MarqueePos = 0;
        }

480
        InvalidateRect(infoPtr->Self, &rect, FALSE);
481
        UpdateWindow(infoPtr->Self);
482 483 484 485 486
    }
    return 0;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
487 488
/***********************************************************************
 *           PROGRESS_CoercePos
489
 * Makes sure the current position (CurVal) is within bounds.
Alexandre Julliard's avatar
Alexandre Julliard committed
490
 */
491
static void PROGRESS_CoercePos(PROGRESS_INFO *infoPtr)
Alexandre Julliard's avatar
Alexandre Julliard committed
492
{
493 494 495 496
    if(infoPtr->CurVal < infoPtr->MinVal)
        infoPtr->CurVal = infoPtr->MinVal;
    if(infoPtr->CurVal > infoPtr->MaxVal)
        infoPtr->CurVal = infoPtr->MaxVal;
Alexandre Julliard's avatar
Alexandre Julliard committed
497 498
}

499 500 501 502 503

/***********************************************************************
 *           PROGRESS_SetFont
 * Set new Font for progress bar
 */
504
static HFONT PROGRESS_SetFont (PROGRESS_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
505
{
506 507
    HFONT hOldFont = infoPtr->Font;
    infoPtr->Font = hFont;
508
    /* Since infoPtr->Font is not used, there is no need for repaint */
509
    return hOldFont;
510 511
}

512 513 514 515 516 517
static DWORD PROGRESS_SetRange (PROGRESS_INFO *infoPtr, int low, int high)
{
    DWORD res = MAKELONG(LOWORD(infoPtr->MinVal), LOWORD(infoPtr->MaxVal));

    /* if nothing changes, simply return */
    if(infoPtr->MinVal == low && infoPtr->MaxVal == high) return res;
518

519 520 521
    infoPtr->MinVal = low;
    infoPtr->MaxVal = high;
    PROGRESS_CoercePos(infoPtr);
522
    InvalidateRect(infoPtr->Self, NULL, TRUE);
523 524
    return res;
}
525

Alexandre Julliard's avatar
Alexandre Julliard committed
526 527 528
/***********************************************************************
 *           ProgressWindowProc
 */
529
static LRESULT WINAPI ProgressWindowProc(HWND hwnd, UINT message,
Dimitrie O. Paun's avatar
Dimitrie O. Paun committed
530
                                         WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
531
{
532
    PROGRESS_INFO *infoPtr;
533 534
    static const WCHAR themeClass[] = {'P','r','o','g','r','e','s','s',0};
    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 549
        
        theme = OpenThemeData (hwnd, themeClass);

550
	dwExStyle &= ~(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
551
	if (!theme) dwExStyle |= WS_EX_STATICEDGE;
Dmitry Timoshkov's avatar
Dmitry Timoshkov committed
552
        SetWindowLongW (hwnd, GWL_EXSTYLE, dwExStyle);
553 554 555
	/* Force recalculation of a non-client area */
	SetWindowPos(hwnd, 0, 0, 0, 0, 0,
	    SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
Alexandre Julliard's avatar
Alexandre Julliard committed
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 563

        /* initialize the info struct */
        infoPtr->Self = hwnd;
564
        infoPtr->MinVal = 0;
565
        infoPtr->MaxVal = 100;
566
        infoPtr->CurVal = 0;
567
        infoPtr->Step = 10;
568 569
        infoPtr->MarqueePos = 0;
        infoPtr->Marquee = FALSE;
570 571 572
        infoPtr->ColorBar = CLR_DEFAULT;
        infoPtr->ColorBk = CLR_DEFAULT;
        infoPtr->Font = 0;
573

574
        TRACE("Progress Ctrl creation, hwnd=%p\n", hwnd);
575
        return 0;
576
    }
577

Alexandre Julliard's avatar
Alexandre Julliard committed
578
    case WM_DESTROY:
579
        TRACE("Progress Ctrl destruction, hwnd=%p\n", hwnd);
580
        Free (infoPtr);
581
        SetWindowLongPtrW(hwnd, 0, 0);
582 583 584 585 586 587 588
        theme = GetWindowTheme (hwnd);
        CloseThemeData (theme);
        return 0;

    case WM_ERASEBKGND:
        return 1;

Alexandre Julliard's avatar
Alexandre Julliard committed
589
    case WM_GETFONT:
590
        return (LRESULT)infoPtr->Font;
Alexandre Julliard's avatar
Alexandre Julliard committed
591 592

    case WM_SETFONT:
593
        return (LRESULT)PROGRESS_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
594

595
    case WM_PRINTCLIENT:
Alexandre Julliard's avatar
Alexandre Julliard committed
596
    case WM_PAINT:
597
        return PROGRESS_Paint (infoPtr, (HDC)wParam);
598

599 600 601
    case WM_TIMER:
        return PROGRESS_Timer (infoPtr, (INT)wParam);

602
    case WM_THEMECHANGED:
603 604 605
    {
        DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE);
        
606 607
        theme = GetWindowTheme (hwnd);
        CloseThemeData (theme);
608 609 610 611 612 613 614 615 616
        theme = OpenThemeData (hwnd, themeClass);
        
        /* WS_EX_STATICEDGE disappears when the control is themed */
        if (theme)
            dwExStyle &= ~WS_EX_STATICEDGE;
        else
            dwExStyle |= WS_EX_STATICEDGE;
        SetWindowLongW (hwnd, GWL_EXSTYLE, dwExStyle);
        
617 618
        InvalidateRect (hwnd, NULL, FALSE);
        return 0;
619
    }
620

Alexandre Julliard's avatar
Alexandre Julliard committed
621
    case PBM_DELTAPOS:
622 623 624
    {
	INT oldVal;
        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 633
        return oldVal;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
634 635

    case PBM_SETPOS:
636
    {
637
        UINT oldVal;
638 639 640
        oldVal = infoPtr->CurVal;
        if(oldVal != wParam) {
	    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 647
        return oldVal;
    }
648

649
    case PBM_SETRANGE:
650
        return PROGRESS_SetRange (infoPtr, (int)LOWORD(lParam), (int)HIWORD(lParam));
Alexandre Julliard's avatar
Alexandre Julliard committed
651 652

    case PBM_SETSTEP:
653 654 655 656 657 658
    {
	INT oldStep;
        oldStep = infoPtr->Step;
        infoPtr->Step = (INT)wParam;
        return oldStep;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
659

660 661 662
    case PBM_GETSTEP:
        return infoPtr->Step;

Alexandre Julliard's avatar
Alexandre Julliard committed
663
    case PBM_STEPIT:
664 665
    {
	INT oldVal;
666
        oldVal = infoPtr->CurVal;
667 668 669
        infoPtr->CurVal += infoPtr->Step;
        if(infoPtr->CurVal > infoPtr->MaxVal)
	    infoPtr->CurVal = infoPtr->MinVal;
670 671 672
        if(oldVal != infoPtr->CurVal)
	{
	    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 677
	}
        return oldVal;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
678

679
    case PBM_SETRANGE32:
680
        return PROGRESS_SetRange (infoPtr, (int)wParam, (int)lParam);
681

Alexandre Julliard's avatar
Alexandre Julliard committed
682
    case PBM_GETRANGE:
683 684 685 686 687
        if (lParam) {
            ((PPBRANGE)lParam)->iLow = infoPtr->MinVal;
            ((PPBRANGE)lParam)->iHigh = infoPtr->MaxVal;
        }
        return wParam ? infoPtr->MinVal : infoPtr->MaxVal;
Alexandre Julliard's avatar
Alexandre Julliard committed
688 689

    case PBM_GETPOS:
690
        return infoPtr->CurVal;
Alexandre Julliard's avatar
Alexandre Julliard committed
691 692

    case PBM_SETBARCOLOR:
693 694 695
        infoPtr->ColorBar = (COLORREF)lParam;
	InvalidateRect(hwnd, NULL, TRUE);
	return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
696

697 698 699
    case PBM_GETBARCOLOR:
	return infoPtr->ColorBar;

Alexandre Julliard's avatar
Alexandre Julliard committed
700
    case PBM_SETBKCOLOR:
701
        infoPtr->ColorBk = (COLORREF)lParam;
702 703
	InvalidateRect(hwnd, NULL, TRUE);
	return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
704

705 706 707
    case PBM_GETBKCOLOR:
	return infoPtr->ColorBk;

708 709
    case PBM_SETSTATE:
        if(wParam != PBST_NORMAL)
710
            FIXME("state %04lx not yet handled\n", wParam);
711 712 713 714 715
        return PBST_NORMAL;

    case PBM_GETSTATE:
        return PBST_NORMAL;

716 717 718 719 720 721 722 723 724 725 726 727 728
    case PBM_SETMARQUEE:
	if(wParam != 0)
        {
            infoPtr->Marquee = TRUE;
            SetTimer(infoPtr->Self, ID_MARQUEE_TIMER, (UINT)lParam, NULL);
        }
        else
        {
            infoPtr->Marquee = FALSE;
            KillTimer(infoPtr->Self, ID_MARQUEE_TIMER);
        }
	return infoPtr->Marquee;

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 733
        return DefWindowProcW( hwnd, message, wParam, lParam );
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
734 735
}

Alexandre Julliard's avatar
Alexandre Julliard committed
736 737

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
738
 * PROGRESS_Register [Internal]
Alexandre Julliard's avatar
Alexandre Julliard committed
739 740 741
 *
 * Registers the progress bar window class.
 */
Dimitrie O. Paun's avatar
Dimitrie O. Paun committed
742
void PROGRESS_Register (void)
Alexandre Julliard's avatar
Alexandre Julliard committed
743
{
744
    WNDCLASSW wndClass;
Alexandre Julliard's avatar
Alexandre Julliard committed
745

746
    ZeroMemory (&wndClass, sizeof(wndClass));
Alexandre Julliard's avatar
Alexandre Julliard committed
747
    wndClass.style         = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
748
    wndClass.lpfnWndProc   = (WNDPROC)ProgressWindowProc;
Alexandre Julliard's avatar
Alexandre Julliard committed
749
    wndClass.cbClsExtra    = 0;
750
    wndClass.cbWndExtra    = sizeof (PROGRESS_INFO *);
751
    wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
752
    wndClass.lpszClassName = PROGRESS_CLASSW;
Alexandre Julliard's avatar
Alexandre Julliard committed
753

754
    RegisterClassW (&wndClass);
755 756 757 758 759 760 761 762
}


/***********************************************************************
 * PROGRESS_Unregister [Internal]
 *
 * Unregisters the progress bar window class.
 */
Dimitrie O. Paun's avatar
Dimitrie O. Paun committed
763
void PROGRESS_Unregister (void)
764
{
765
    UnregisterClassW (PROGRESS_CLASSW, NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
766
}