text: /*
   * Progress control
   *
   * Copyright 1997, 2002 Dimitrie O. Paun
   * Copyright 1998, 1999 Eric Kohl
   *
   * 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
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
   *
   * 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.
   * 
   * Unless otherwise noted, we believe this code to be complete, as per
   * the specification mentioned above.
   * If you discover missing features, or bugs, please note them below.
   *
   * TODO:
   *
   * Styles:
   *    -- PBS_SMOOTHREVERSE
   *
   */
  
  #include <stdarg.h>
  #include <string.h>
  #include "windef.h"
  #include "winbase.h"
  #include "wingdi.h"
  #include "winuser.h"
  #include "winnls.h"
  #include "commctrl.h"
  #include "comctl32.h"
  #include "uxtheme.h"
  #include "vssym32.h"
  #include "wine/debug.h"
  
  WINE_DEFAULT_DEBUG_CHANNEL(progress);
  
  typedef struct
  {
      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 */
      INT       MarqueePos;   /* Marquee animation position */
      BOOL      Marquee;      /* Whether the marquee animation is enabled */
      COLORREF  ColorBar;     /* Bar color */
      COLORREF  ColorBk;      /* Background color */
      HFONT     Font;         /* Handle to font (not unused) */
  } PROGRESS_INFO;
  
  /* Control configuration constants */
  
  #define LED_GAP           2
  #define MARQUEE_LEDS      5
  #define ID_MARQUEE_TIMER  1
  #define DEFAULT_MARQUEE_PERIOD 30
  
  /* Helper to obtain size of a progress bar chunk ("led"). */
  static inline int get_led_size ( const PROGRESS_INFO *infoPtr, LONG style,
                                   const RECT* rect )
  {
      HTHEME theme = GetWindowTheme (infoPtr->Self);
      if (theme)
      {
          int chunkSize;
          if (SUCCEEDED( GetThemeInt( theme, 0, 0, TMT_PROGRESSCHUNKSIZE, &chunkSize )))
              return chunkSize;
      }
  
      if (style & PBS_VERTICAL)
          return MulDiv (rect->right - rect->left, 2, 3);
      else
          return MulDiv (rect->bottom - rect->top, 2, 3);
  }
  
  /* Helper to obtain gap between progress bar chunks */
  static inline int get_led_gap ( const PROGRESS_INFO *infoPtr )
  {
      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)
  {
      HTHEME theme = GetWindowTheme (hwnd);
      GetClientRect (hwnd, rect);
      if (!theme)
          InflateRect(rect, -1, -1);
      else
      {
          DWORD dwStyle = GetWindowLongW (hwnd, GWL_STYLE);
          int part = (dwStyle & PBS_VERTICAL) ? PP_BARVERT : PP_BAR;
          GetThemeBackgroundContentRect (theme, 0, part, 0, rect, rect);
      }
  }
  
  /* 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 */
  static inline int get_bar_position( const PROGRESS_INFO *infoPtr, LONG style,
                                      const RECT* rect, INT value )
  {
      return MulDiv (value - infoPtr->MinVal, get_bar_size (style, rect),
                        infoPtr->MaxVal - infoPtr->MinVal);
  }
  
  /***********************************************************************
   * PROGRESS_Invalidate
   *
   * Don't be too clever about invalidating the progress bar.
   * InstallShield depends on this simple behaviour.
   */
  static void PROGRESS_Invalidate( const PROGRESS_INFO *infoPtr, INT old, INT new )
  {
      InvalidateRect( infoPtr->Self, NULL, old > new );
  }
  
  /* Information for a progress bar drawing helper */
  typedef struct tagProgressDrawInfo
  {
      HDC hdc;
      RECT rect;
      HBRUSH hbrBar;
      HBRUSH hbrBk;
      int ledW, ledGap;
      HTHEME theme;
      RECT bgRect;
  } 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;
          r.right = min (r.left + di->ledGap, right);
          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;
          r.top = max (r.bottom - di->ledGap, top);
          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,
  };
  
  /* 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;
      r.right = di->rect.left + end;
      DrawThemeBackground (di->theme, di->hdc, PP_CHUNK, 0, &r, NULL);
  }
  
  /* draw themed vertical bar from 'start' to 'end' */
  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;
      r.top = di->rect.bottom - end;
      DrawThemeBackground (di->theme, di->hdc, PP_CHUNKVERT, 0, &r, NULL);
  }
  
  /* 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,
  };
  
  /***********************************************************************
   * PROGRESS_Draw
   * Draws the progress bar.
   */
  static LRESULT PROGRESS_Draw (PROGRESS_INFO *infoPtr, HDC hdc)
  {
      int barSize;
      DWORD dwStyle;
      BOOL barSmooth;
      const ProgressDrawProc* drawProcs;
      ProgressDrawInfo pdi;
  
      TRACE("(infoPtr=%p, hdc=%p)\n", infoPtr, hdc);
  
      pdi.hdc = hdc;
      pdi.theme = GetWindowTheme (infoPtr->Self);
  
      /* get the required bar brush */
      if (infoPtr->ColorBar == CLR_DEFAULT)
          pdi.hbrBar = GetSysColorBrush(COLOR_HIGHLIGHT);
      else
          pdi.hbrBar = CreateSolidBrush (infoPtr->ColorBar);
  
      if (infoPtr->ColorBk == CLR_DEFAULT)
          pdi.hbrBk = GetSysColorBrush(COLOR_3DFACE);
      else
          pdi.hbrBk = CreateSolidBrush(infoPtr->ColorBk);
  
      /* get the window style */
      dwStyle = GetWindowLongW (infoPtr->Self, GWL_STYLE);
  
      /* get client rectangle */
      GetClientRect (infoPtr->Self, &pdi.rect);
      if (!pdi.theme) {
          FrameRect( hdc, &pdi.rect, pdi.hbrBk );
          InflateRect(&pdi.rect, -1, -1);
      }
      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);
      }
  
      /* compute some drawing parameters */
      barSmooth = (dwStyle & PBS_SMOOTH) && !pdi.theme;
      drawProcs = &((pdi.theme ? drawProcThemed : drawProcClassic)[(barSmooth ? 0 : 4)
          + ((dwStyle & PBS_VERTICAL) ? 2 : 0)]);
      barSize = get_bar_size( dwStyle, &pdi.rect );
      if (pdi.theme)
      {
          GetWindowRect( infoPtr->Self, &pdi.bgRect );
          MapWindowPoints( infoPtr->Self, 0, (POINT*)&pdi.bgRect, 2 );
      }
  
      if (!barSmooth)
          pdi.ledW = get_led_size( infoPtr, dwStyle, &pdi.rect);
      pdi.ledGap = get_led_gap( infoPtr );
  
      if (dwStyle & PBS_MARQUEE)
      {
          const int ledW = !barSmooth ? (pdi.ledW + pdi.ledGap) : 1;
          const int leds = (barSize + ledW - 1) / ledW;
          const int ledMEnd = infoPtr->MarqueePos + MARQUEE_LEDS;
  
          if (ledMEnd > leds)
          {
              /* 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);
          }
          else
          {
              /* 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);
          }
      }
      else
      {
          int barEnd = get_bar_position( infoPtr, dwStyle, &pdi.rect,
              infoPtr->CurVal);
          if (!barSmooth)
          {
              const int ledW = pdi.ledW + pdi.ledGap;
              barEnd = min (((barEnd + ledW - 1) / ledW) * ledW, barSize);
          }
          drawProcs[0]( &pdi, 0, barEnd);
          drawProcs[1]( &pdi, barEnd, barSize);
      }
  
      /* delete bar brush */
      if (infoPtr->ColorBar != CLR_DEFAULT) DeleteObject (pdi.hbrBar);
      if (infoPtr->ColorBk != CLR_DEFAULT) DeleteObject (pdi.hbrBk);
  
      return 0;
  }
  
  /***********************************************************************
   * PROGRESS_Paint
   * Draw the progress bar. The background need not be erased.
   * If dc!=0, it draws on it
   */
  static LRESULT PROGRESS_Paint (PROGRESS_INFO *infoPtr, HDC hdc)
  {
      PAINTSTRUCT ps;
      if (hdc) return PROGRESS_Draw (infoPtr, hdc);
      hdc = BeginPaint (infoPtr->Self, &ps);
      PROGRESS_Draw (infoPtr, hdc);
      EndPaint (infoPtr->Self, &ps);
      return 0;
  }
  
  
  /***********************************************************************
   * Advance marquee progress by one step.
   */
  static void PROGRESS_UpdateMarquee (PROGRESS_INFO *infoPtr)
  {
      LONG style = GetWindowLongW (infoPtr->Self, GWL_STYLE);
      RECT rect;
      int ledWidth, leds;
      HTHEME theme = GetWindowTheme (infoPtr->Self);
      BOOL smooth = (style & PBS_SMOOTH) && !theme;
  
      get_client_rect (infoPtr->Self, &rect);
  
      if (smooth)
          ledWidth = 1;
      else
          ledWidth = get_led_size( infoPtr, style, &rect ) + get_led_gap( infoPtr );
  
      leds = (get_bar_size( style, &rect ) + ledWidth - 1) /
          ledWidth;
  
      /* increment the marquee progress */
      if (++infoPtr->MarqueePos >= leds)
          infoPtr->MarqueePos = 0;
  
      InvalidateRect(infoPtr->Self, &rect, TRUE);
      UpdateWindow(infoPtr->Self);
  }
  
  
  /***********************************************************************
   *           PROGRESS_CoercePos
   * Makes sure the current position (CurVal) is within bounds.
   */
  static void PROGRESS_CoercePos(PROGRESS_INFO *infoPtr)
  {
      if(infoPtr->CurVal < infoPtr->MinVal)
          infoPtr->CurVal = infoPtr->MinVal;
      if(infoPtr->CurVal > infoPtr->MaxVal)
          infoPtr->CurVal = infoPtr->MaxVal;
  }
  
  
  /***********************************************************************
   *           PROGRESS_SetFont
   * Set new Font for progress bar
   */
  static HFONT PROGRESS_SetFont (PROGRESS_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
  {
      HFONT hOldFont = infoPtr->Font;
      infoPtr->Font = hFont;
      /* Since infoPtr->Font is not used, there is no need for repaint */
      return hOldFont;
  }
  
  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;
  
      infoPtr->MinVal = low;
      infoPtr->MaxVal = high;
      PROGRESS_CoercePos(infoPtr);
      InvalidateRect(infoPtr->Self, NULL, TRUE);
      return res;
  }
  
  static UINT PROGRESS_SetPos (PROGRESS_INFO *infoPtr, INT pos)
  {
      DWORD style = GetWindowLongW(infoPtr->Self, GWL_STYLE);
  
      if (style & PBS_MARQUEE)
      {
          PROGRESS_UpdateMarquee(infoPtr);
          return 1;
      }
      else
      {
          UINT oldVal;
          oldVal = infoPtr->CurVal;
          if (oldVal != pos) {
  	    infoPtr->CurVal = pos;
  	    PROGRESS_CoercePos(infoPtr);
  	    TRACE("PBM_SETPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
              PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
              UpdateWindow( infoPtr->Self );
          }
          return oldVal;
      }
  }
  
  /***********************************************************************
   *           ProgressWindowProc
   */
  static LRESULT WINAPI ProgressWindowProc(HWND hwnd, UINT message,
                                           WPARAM wParam, LPARAM lParam)
  {
      PROGRESS_INFO *infoPtr;
      static const WCHAR themeClass[] = {'P','r','o','g','r','e','s','s',0};
      HTHEME theme;
  
      TRACE("hwnd=%p msg=%04x wparam=%lx lParam=%lx\n", hwnd, message, wParam, lParam);
  
      infoPtr = (PROGRESS_INFO *)GetWindowLongPtrW(hwnd, 0);
  
      if (!infoPtr && message != WM_CREATE)
          return DefWindowProcW( hwnd, message, wParam, lParam );
  
      switch(message) {
      case WM_CREATE:
      {
  	DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE);
  
          theme = OpenThemeData (hwnd, themeClass);
  
  	dwExStyle &= ~(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
  	if (!theme) dwExStyle |= WS_EX_STATICEDGE;
          SetWindowLongW (hwnd, GWL_EXSTYLE, dwExStyle);
  	/* Force recalculation of a non-client area */
  	SetWindowPos(hwnd, 0, 0, 0, 0, 0,
  	    SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  
          /* allocate memory for info struct */
          infoPtr = Alloc (sizeof(PROGRESS_INFO));
          if (!infoPtr) return -1;
          SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
  
          /* initialize the info struct */
          infoPtr->Self = hwnd;
          infoPtr->MinVal = 0;
          infoPtr->MaxVal = 100;
          infoPtr->CurVal = 0;
          infoPtr->Step = 10;
          infoPtr->MarqueePos = 0;
          infoPtr->Marquee = FALSE;
          infoPtr->ColorBar = CLR_DEFAULT;
          infoPtr->ColorBk = CLR_DEFAULT;
          infoPtr->Font = 0;
  
          TRACE("Progress Ctrl creation, hwnd=%p\n", hwnd);
          return 0;
      }
  
      case WM_DESTROY:
          TRACE("Progress Ctrl destruction, hwnd=%p\n", hwnd);
          Free (infoPtr);
          SetWindowLongPtrW(hwnd, 0, 0);
          theme = GetWindowTheme (hwnd);
          CloseThemeData (theme);
          return 0;
  
      case WM_ERASEBKGND:
          return 1;
  
      case WM_GETFONT:
          return (LRESULT)infoPtr->Font;
  
      case WM_SETFONT:
          return (LRESULT)PROGRESS_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
  
      case WM_PRINTCLIENT:
      case WM_PAINT:
          return PROGRESS_Paint (infoPtr, (HDC)wParam);
  
      case WM_TIMER:
          if (wParam == ID_MARQUEE_TIMER)
              PROGRESS_UpdateMarquee (infoPtr);
          return 0;
  
      case WM_THEMECHANGED:
      {
          DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE);
          
          theme = GetWindowTheme (hwnd);
          CloseThemeData (theme);
          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);
          
          InvalidateRect (hwnd, NULL, FALSE);
          return 0;
      }
  
      case PBM_DELTAPOS:
      {
  	INT oldVal;
          oldVal = infoPtr->CurVal;
          if(wParam != 0) {
  	    infoPtr->CurVal += (INT)wParam;
  	    PROGRESS_CoercePos (infoPtr);
  	    TRACE("PBM_DELTAPOS: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
              PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
              UpdateWindow( infoPtr->Self );
          }
          return oldVal;
      }
  
      case PBM_SETPOS:
          return PROGRESS_SetPos(infoPtr, wParam);
  
      case PBM_SETRANGE:
          return PROGRESS_SetRange (infoPtr, (int)LOWORD(lParam), (int)HIWORD(lParam));
  
      case PBM_SETSTEP:
      {
  	INT oldStep;
          oldStep = infoPtr->Step;
          infoPtr->Step = (INT)wParam;
          return oldStep;
      }
  
      case PBM_GETSTEP:
          return infoPtr->Step;
  
      case PBM_STEPIT:
      {
  	INT oldVal;
          oldVal = infoPtr->CurVal;
          infoPtr->CurVal += infoPtr->Step;
          if(infoPtr->CurVal > infoPtr->MaxVal)
  	    infoPtr->CurVal = infoPtr->MinVal;
          if(oldVal != infoPtr->CurVal)
  	{
  	    TRACE("PBM_STEPIT: current pos changed from %d to %d\n", oldVal, infoPtr->CurVal);
              PROGRESS_Invalidate( infoPtr, oldVal, infoPtr->CurVal );
              UpdateWindow( infoPtr->Self );
  	}
          return oldVal;
      }
  
      case PBM_SETRANGE32:
          return PROGRESS_SetRange (infoPtr, (int)wParam, (int)lParam);
  
      case PBM_GETRANGE:
          if (lParam) {
              ((PPBRANGE)lParam)->iLow = infoPtr->MinVal;
              ((PPBRANGE)lParam)->iHigh = infoPtr->MaxVal;
          }
          return wParam ? infoPtr->MinVal : infoPtr->MaxVal;
  
      case PBM_GETPOS:
          return infoPtr->CurVal;
  
      case PBM_SETBARCOLOR:
      {
          COLORREF clr = infoPtr->ColorBar;
  
          infoPtr->ColorBar = (COLORREF)lParam;
  	InvalidateRect(hwnd, NULL, TRUE);
          return clr;
      }
  
      case PBM_GETBARCOLOR:
  	return infoPtr->ColorBar;
  
      case PBM_SETBKCOLOR:
      {
          COLORREF clr = infoPtr->ColorBk;
  
          infoPtr->ColorBk = (COLORREF)lParam;
  	InvalidateRect(hwnd, NULL, TRUE);
          return clr;
      }
  
      case PBM_GETBKCOLOR:
  	return infoPtr->ColorBk;
  
      case PBM_SETSTATE:
          if(wParam != PBST_NORMAL)
              FIXME("state %04lx not yet handled\n", wParam);
          return PBST_NORMAL;
  
      case PBM_GETSTATE:
          return PBST_NORMAL;
  
      case PBM_SETMARQUEE:
  	if(wParam != 0)
          {
              UINT period = lParam ? (UINT)lParam : DEFAULT_MARQUEE_PERIOD;
              infoPtr->Marquee = TRUE;
              SetTimer(infoPtr->Self, ID_MARQUEE_TIMER, period, NULL);
          }
          else
          {
              infoPtr->Marquee = FALSE;
              KillTimer(infoPtr->Self, ID_MARQUEE_TIMER);
          }
  	return infoPtr->Marquee;
  
      default:
          if ((message >= WM_USER) && (message < WM_APP) && !COMCTL32_IsReflectedMessage(message))
  	    ERR("unknown msg %04x wp=%04lx lp=%08lx\n", message, wParam, lParam );
          return DefWindowProcW( hwnd, message, wParam, lParam );
      }
  }
  
  
  /***********************************************************************
   * PROGRESS_Register [Internal]
   *
   * Registers the progress bar window class.
   */
  void PROGRESS_Register (void)
  {
      WNDCLASSW wndClass;
  
      ZeroMemory (&wndClass, sizeof(wndClass));
      wndClass.style         = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
      wndClass.lpfnWndProc   = ProgressWindowProc;
      wndClass.cbClsExtra    = 0;
      wndClass.cbWndExtra    = sizeof (PROGRESS_INFO *);
      wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
      wndClass.lpszClassName = PROGRESS_CLASSW;
  
      RegisterClassW (&wndClass);
  }
  
  
  /***********************************************************************
   * PROGRESS_Unregister [Internal]
   *
   * Unregisters the progress bar window class.
   */
  void PROGRESS_Unregister (void)
  {
      UnregisterClassW (PROGRESS_CLASSW, NULL);
  }
  