wined3d: Don't crash in CreateAdditionalSwapChain() if the context array wasn't alloc...
[wine] / dlls / comctl32 / updown.c
index 2089648..38879db 100644 (file)
-/*             
+/*
  * Updown control
  *
- * Copyright 1997 Dimitrie O. Paun
+ * Copyright 1997, 2002 Dimitrie O. Paun
  *
- * TODO:
- *   - I think I do not handle correctly the WS_BORDER style.
- *     (Should be fixed. <ekohl@abo.rhein-zeitung.de>)
+ * 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.
  *
- * Testing:
- *   Not much. The following  have not been tested at all:
- *     - horizontal arrows
- *     - listbox as buddy window
- *     - acceleration
- *     - base 16
- *     - integers with thousand separators.
- *       (fixed bugs. <noel@macadamian.com>)
+ * 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.
  *
- *   Even though the above list seems rather large, the control seems to
- *   behave very well so I am confident it does work in most (all) of the
- *   untested cases.
+ * 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.
+ * 
  */
 
 #include <stdlib.h>
 #include <string.h>
+#include <stdarg.h>
 #include <stdio.h>
 
 #include "windef.h"
 #include "winbase.h"
 #include "wingdi.h"
 #include "winuser.h"
-#include "commctrl.h"
 #include "winnls.h"
-#include "debugtools.h"
-
-DEFAULT_DEBUG_CHANNEL(updown);
+#include "commctrl.h"
+#include "comctl32.h"
+#include "uxtheme.h"
+#include "tmschema.h"
+#include "wine/unicode.h"
+#include "wine/debug.h"
 
-#define UPDOWN_BUDDYCLASSNAMELEN 40
+WINE_DEFAULT_DEBUG_CHANNEL(updown);
 
 typedef struct
 {
-  UINT      AccelCount;   /* Number of elements in AccelVect */
-  UDACCEL*  AccelVect;    /* Vector containing AccelCount elements */
-  INT       Base;         /* Base to display nr in the buddy window */
-  INT       CurVal;       /* Current up-down value */
-  INT       MinVal;       /* Minimum up-down value */
-  INT       MaxVal;       /* Maximum up-down value */
-  HWND      Buddy;        /* Handle to the buddy window */
-  CHAR      szBuddyClass[UPDOWN_BUDDYCLASSNAMELEN]; /* Buddy window class name */
-  INT       Flags;        /* Internal Flags FLAG_* */
+    HWND      Self;            /* Handle to this up-down control */
+    HWND      Notify;          /* Handle to the parent window */
+    DWORD     dwStyle;         /* The GWL_STYLE for this window */
+    UINT      AccelCount;      /* Number of elements in AccelVect */
+    UDACCEL*  AccelVect;       /* Vector containing AccelCount elements */
+    INT       AccelIndex;      /* Current accel index, -1 if not accel'ing */
+    INT       Base;            /* Base to display nr in the buddy window */
+    INT       CurVal;          /* Current up-down value */
+    INT       MinVal;          /* Minimum up-down value */
+    INT       MaxVal;          /* Maximum up-down value */
+    HWND      Buddy;           /* Handle to the buddy window */
+    INT       BuddyType;       /* Remembers the buddy type BUDDY_TYPE_* */
+    INT       Flags;           /* Internal Flags FLAG_* */
+    BOOL      UnicodeFormat;   /* Marks the use of Unicode internally */
 } UPDOWN_INFO;
 
 /* Control configuration constants */
 
-#define INITIAL_DELAY    500 /* initial timer until auto-increment kicks in */
-#define REPEAT_DELAY     50  /* delay between auto-increments */
-
-#define DEFAULT_WIDTH       14  /* default width of the ctrl */
-#define DEFAULT_XSEP         0  /* default separation between buddy and crtl */
-#define DEFAULT_ADDTOP       0  /* amount to extend above the buddy window */
-#define DEFAULT_ADDBOT       0  /* amount to extend below the buddy window */
-#define DEFAULT_BUDDYBORDER  2  /* Width/height of the buddy border */
+#define INITIAL_DELAY  500    /* initial timer until auto-inc kicks in */
+#define AUTOPRESS_DELAY        250    /* time to keep arrow pressed on KEY_DOWN */
+#define REPEAT_DELAY   50     /* delay between auto-increments */
 
+#define DEFAULT_WIDTH      14 /* default width of the ctrl */
+#define DEFAULT_XSEP         0 /* default separation between buddy and ctrl */
+#define DEFAULT_ADDTOP       0 /* amount to extend above the buddy window */
+#define DEFAULT_ADDBOT       0 /* amount to extend below the buddy window */
+#define DEFAULT_BUDDYBORDER  2 /* Width/height of the buddy border */
+#define DEFAULT_BUDDYSPACER  2 /* Spacer between the buddy and the ctrl */
+#define DEFAULT_BUDDYBORDER_THEMED  1 /* buddy border when theming is enabled */
+#define DEFAULT_BUDDYSPACER_THEMED  0 /* buddy spacer when theming is enabled */
 
 /* Work constants */
 
-#define FLAG_INCR        0x01
-#define FLAG_DECR        0x02
-#define FLAG_MOUSEIN     0x04
-#define FLAG_CLICKED     (FLAG_INCR | FLAG_DECR)
+#define FLAG_INCR      0x01
+#define FLAG_DECR      0x02
+#define FLAG_MOUSEIN   0x04
+#define FLAG_PRESSED   0x08
+#define FLAG_ARROW     (FLAG_INCR | FLAG_DECR)
 
-#define TIMERID1         1
-#define TIMERID2         2
-#define BUDDY_UPDOWN_HWND        "buddyUpDownHWND"
-#define BUDDY_SUPERCLASS_WNDPROC "buddySupperClassWndProc"
+#define BUDDY_TYPE_UNKNOWN 0
+#define BUDDY_TYPE_LISTBOX 1
+#define BUDDY_TYPE_EDIT    2
 
-static int accelIndex = -1;
+#define TIMER_AUTOREPEAT   1
+#define TIMER_ACCEL        2
+#define TIMER_AUTOPRESS    3
 
-#define UNKNOWN_PARAM(msg, wParam, lParam) WARN(\
-        "Unknown parameter(s) for message " #msg \
-       "(%04x): wp=%04x lp=%08lx\n", msg, wParam, lParam);
+#define UPDOWN_GetInfoPtr(hwnd) ((UPDOWN_INFO *)GetWindowLongPtrW (hwnd,0))
+#define COUNT_OF(a) (sizeof(a)/sizeof(a[0]))
 
-#define UPDOWN_GetInfoPtr(hwnd) ((UPDOWN_INFO *)GetWindowLongA (hwnd,0))
+static const WCHAR BUDDY_UPDOWN_HWND[] = { 'b', 'u', 'd', 'd', 'y', 'U', 'p', 'D', 'o', 'w', 'n', 'H', 'W', 'N', 'D', 0 };
+static const WCHAR BUDDY_SUPERCLASS_WNDPROC[] = { 'b', 'u', 'd', 'd', 'y', 'S', 'u', 'p', 'p', 'e', 'r', 
+                                                 'C', 'l', 'a', 's', 's', 'W', 'n', 'd', 'P', 'r', 'o', 'c', 0 };
+static void UPDOWN_DoAction (UPDOWN_INFO *infoPtr, int delta, int action);
 
-static LRESULT CALLBACK
-UPDOWN_Buddy_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+/***********************************************************************
+ *           UPDOWN_IsBuddyEdit
+ * Tests if our buddy is an edit control.
+ */
+static inline BOOL UPDOWN_IsBuddyEdit(const UPDOWN_INFO *infoPtr)
+{
+    return infoPtr->BuddyType == BUDDY_TYPE_EDIT;
+}
+
+/***********************************************************************
+ *           UPDOWN_IsBuddyListbox
+ * Tests if our buddy is a listbox control.
+ */
+static inline BOOL UPDOWN_IsBuddyListbox(const UPDOWN_INFO *infoPtr)
+{
+    return infoPtr->BuddyType == BUDDY_TYPE_LISTBOX;
+}
 
 /***********************************************************************
  *           UPDOWN_InBounds
  * Tests if a given value 'val' is between the Min&Max limits
  */
-static BOOL UPDOWN_InBounds(HWND hwnd, int val)
+static BOOL UPDOWN_InBounds(const UPDOWN_INFO *infoPtr, int val)
 {
-  UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd);
-
-  if(infoPtr->MaxVal > infoPtr->MinVal)
-    return (infoPtr->MinVal <= val) && (val <= infoPtr->MaxVal);
-  else
-    return (infoPtr->MaxVal <= val) && (val <= infoPtr->MinVal);
+    if(infoPtr->MaxVal > infoPtr->MinVal)
+        return (infoPtr->MinVal <= val) && (val <= infoPtr->MaxVal);
+    else
+        return (infoPtr->MaxVal <= val) && (val <= infoPtr->MinVal);
 }
 
 /***********************************************************************
  *           UPDOWN_OffsetVal
- * Tests if we can change the current value by delta. If so, it changes
- * it and returns TRUE. Else, it leaves it unchanged and returns FALSE.
+ * Change the current value by delta.
+ * It returns TRUE is the value was changed successfully, or FALSE
+ * if the value was not changed, as it would go out of bounds.
  */
-static BOOL UPDOWN_OffsetVal(HWND hwnd, int delta)
+static BOOL UPDOWN_OffsetVal(UPDOWN_INFO *infoPtr, int delta)
 {
-  UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd);
-
-  /* check if we can do the modification first */
-  if(!UPDOWN_InBounds (hwnd, infoPtr->CurVal+delta)){
-    if (GetWindowLongA (hwnd, GWL_STYLE) & UDS_WRAP)
-    {
-      delta += (delta < 0 ? -1 : 1) *
-       (infoPtr->MaxVal < infoPtr->MinVal ? -1 : 1) *
-       (infoPtr->MinVal - infoPtr->MaxVal) +
-       (delta < 0 ? 1 : -1);
+    /* check if we can do the modification first */
+    if(!UPDOWN_InBounds (infoPtr, infoPtr->CurVal+delta)) {
+        if (infoPtr->dwStyle & UDS_WRAP) {
+            delta += (delta < 0 ? -1 : 1) *
+                    (infoPtr->MaxVal < infoPtr->MinVal ? -1 : 1) *
+                    (infoPtr->MinVal - infoPtr->MaxVal) +
+                    (delta < 0 ? 1 : -1);
+        } else return FALSE;
     }
-    else
-      return FALSE;
-  }
 
-  infoPtr->CurVal += delta;
-  return TRUE;
+    infoPtr->CurVal += delta;
+    return TRUE;
 }
 
 /***********************************************************************
- * UPDOWN_HasBuddyBorder [Internal]
+ * UPDOWN_HasBuddyBorder
  *
  * When we have a buddy set and that we are aligned on our buddy, we
  * want to draw a sunken edge to make like we are part of that control.
  */
-static BOOL UPDOWN_HasBuddyBorder(HWND hwnd)
-{  
-  UPDOWN_INFO* infoPtr = UPDOWN_GetInfoPtr (hwnd); 
-  DWORD        dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
-
-  return  ( ((dwStyle & (UDS_ALIGNLEFT | UDS_ALIGNRIGHT)) != 0) &&
-           (SendMessageA(hwnd, UDM_GETBUDDY, 0, 0) != 0) &&
-           (lstrcmpiA(infoPtr->szBuddyClass, "EDIT") == 0 ) );
+static BOOL UPDOWN_HasBuddyBorder(const UPDOWN_INFO *infoPtr)
+{
+    return  ( ((infoPtr->dwStyle & (UDS_ALIGNLEFT | UDS_ALIGNRIGHT)) != 0) &&
+             UPDOWN_IsBuddyEdit(infoPtr) );
 }
 
 /***********************************************************************
  *           UPDOWN_GetArrowRect
  * wndPtr   - pointer to the up-down wnd
  * rect     - will hold the rectangle
- * incr     - TRUE  get the "increment" rect (up or right)
- *            FALSE get the "decrement" rect (down or left)
- *          
+ * arrow    - FLAG_INCR to get the "increment" rect (up or right)
+ *            FLAG_DECR to get the "decrement" rect (down or left)
+ *            If both flags are present, the envelope is returned.
  */
-static void UPDOWN_GetArrowRect (HWND hwnd, RECT *rect, BOOL incr)
+static void UPDOWN_GetArrowRect (const UPDOWN_INFO* infoPtr, RECT *rect, int arrow)
 {
-  DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
-  int   len; /* will hold the width or height */
-
-  GetClientRect (hwnd, rect);
-
-  /*
-   * Make sure we calculate the rectangle to fit even if we draw the
-   * border.
-   */
-  if (UPDOWN_HasBuddyBorder(hwnd))       
-  {
-    if (dwStyle & UDS_ALIGNLEFT)
-      rect->left+=DEFAULT_BUDDYBORDER;
-    else
-      rect->right-=DEFAULT_BUDDYBORDER;
-    
-    InflateRect(rect, 0, -DEFAULT_BUDDYBORDER);
-  }
-
-  /*
-   * We're calculating the midpoint to figure-out where the
-   * separation between the buttons will lay. We make sure that we
-   * round the uneven numbers by adding 1.
-   */
-  if (dwStyle & UDS_HORZ) {
-    len = rect->right - rect->left + 1; /* compute the width */
-    if (incr)
-      rect->left = rect->left + len/2; 
-    else
-      rect->right =  rect->left + len/2;
-  }
-  else {
-    len = rect->bottom - rect->top + 1; /* compute the height */
-    if (incr)
-      rect->bottom =  rect->top + len/2;
-    else
-      rect->top =  rect->top + len/2;
-  }
+    HTHEME theme = GetWindowTheme (infoPtr->Self);
+    const int border = theme ? DEFAULT_BUDDYBORDER_THEMED : DEFAULT_BUDDYBORDER;
+    const int spacer = theme ? DEFAULT_BUDDYSPACER_THEMED : DEFAULT_BUDDYSPACER;
+    GetClientRect (infoPtr->Self, rect);
+
+    /*
+     * Make sure we calculate the rectangle to fit even if we draw the
+     * border.
+     */
+    if (UPDOWN_HasBuddyBorder(infoPtr)) {
+        if (infoPtr->dwStyle & UDS_ALIGNLEFT)
+            rect->left += border;
+        else
+            rect->right -= border;
+
+        InflateRect(rect, 0, -border);
+    }
+
+    /* now figure out if we need a space away from the buddy */
+    if (IsWindow(infoPtr->Buddy) ) {
+       if (infoPtr->dwStyle & UDS_ALIGNLEFT) rect->right -= spacer;
+       else rect->left += spacer;
+    }
+
+    /*
+     * We're calculating the midpoint to figure-out where the
+     * separation between the buttons will lay. We make sure that we
+     * round the uneven numbers by adding 1.
+     */
+    if (infoPtr->dwStyle & UDS_HORZ) {
+        int len = rect->right - rect->left + 1; /* compute the width */
+        if (arrow & FLAG_INCR)
+            rect->left = rect->left + len/2;
+        if (arrow & FLAG_DECR)
+            rect->right =  rect->left + len/2 - (theme ? 0 : 1);
+    } else {
+        int len = rect->bottom - rect->top + 1; /* compute the height */
+        if (arrow & FLAG_INCR)
+            rect->bottom =  rect->top + len/2 - (theme ? 0 : 1);
+        if (arrow & FLAG_DECR)
+            rect->top =  rect->top + len/2;
+    }
 }
 
 /***********************************************************************
  *           UPDOWN_GetArrowFromPoint
  * Returns the rectagle (for the up or down arrow) that contains pt.
- * If it returns the up rect, it returns TRUE.
- * If it returns the down rect, it returns FALSE.
+ * If it returns the up rect, it returns FLAG_INCR.
+ * If it returns the down rect, it returns FLAG_DECR.
  */
-static BOOL
-UPDOWN_GetArrowFromPoint (HWND hwnd, RECT *rect, POINT pt)
+static INT UPDOWN_GetArrowFromPoint (const UPDOWN_INFO *infoPtr, RECT *rect, POINT pt)
 {
-    UPDOWN_GetArrowRect (hwnd, rect, TRUE);
-  if(PtInRect(rect, pt))
-    return TRUE;
+    UPDOWN_GetArrowRect (infoPtr, rect, FLAG_INCR);
+    if(PtInRect(rect, pt)) return FLAG_INCR;
+
+    UPDOWN_GetArrowRect (infoPtr, rect, FLAG_DECR);
+    if(PtInRect(rect, pt)) return FLAG_DECR;
 
-    UPDOWN_GetArrowRect (hwnd, rect, FALSE);
-  return FALSE;
+    return 0;
 }
 
 
@@ -213,15 +244,14 @@ UPDOWN_GetArrowFromPoint (HWND hwnd, RECT *rect, POINT pt)
  *           UPDOWN_GetThousandSep
  * Returns the thousand sep. If an error occurs, it returns ','.
  */
-static char UPDOWN_GetThousandSep()
+static WCHAR UPDOWN_GetThousandSep(void)
 {
-  char sep[2];
+    WCHAR sep[2];
 
-  if(GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, 
-                     sep, sizeof(sep)) != 1)
-    return ',';
+    if(GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, sep, 2) != 1)
+        sep[0] = ',';
 
-  return sep[0];
+    return sep[0];
 }
 
 /***********************************************************************
@@ -230,47 +260,42 @@ static char UPDOWN_GetThousandSep()
  * it stores it in the control's CurVal
  * returns:
  *   TRUE  - if it read the integer from the buddy successfully
- *   FALSE - if an error occured
+ *   FALSE - if an error occurred
  */
-static BOOL UPDOWN_GetBuddyInt (HWND hwnd)
+static BOOL UPDOWN_GetBuddyInt (UPDOWN_INFO *infoPtr)
 {
-  UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd);
-  char txt[20], sep, *src, *dst;
-  int newVal;
-
-  if (!IsWindow(infoPtr->Buddy))
-    return FALSE;
-
-  /*if the buddy is a list window, we must set curr index */
-  if (!lstrcmpA (infoPtr->szBuddyClass, "ListBox")){
-    newVal = SendMessageA(infoPtr->Buddy, LB_GETCARETINDEX, 0, 0);
-    if(newVal < 0)
-      return FALSE;
-  }
-  else{
-    /* we have a regular window, so will get the text */
-    if (!GetWindowTextA(infoPtr->Buddy, txt, sizeof(txt)))
-      return FALSE;
-
-    sep = UPDOWN_GetThousandSep(); 
-
-    /* now get rid of the separators */
-    for(src = dst = txt; *src; src++)
-      if(*src != sep)
-       *dst++ = *src;
-    *dst = 0;
-
-    /* try to convert the number and validate it */
-    newVal = strtol(txt, &src, infoPtr->Base);
-    if(*src || !UPDOWN_InBounds (hwnd, newVal)) 
-      return FALSE;
-
-    TRACE("new value(%d) read from buddy (old=%d)\n", 
-                newVal, infoPtr->CurVal);
-  }
-  
-  infoPtr->CurVal = newVal;
-  return TRUE;
+    WCHAR txt[20], sep, *src, *dst;
+    int newVal;
+
+    if (!((infoPtr->dwStyle & UDS_SETBUDDYINT) && IsWindow(infoPtr->Buddy)))
+        return FALSE;
+
+    /*if the buddy is a list window, we must set curr index */
+    if (UPDOWN_IsBuddyListbox(infoPtr)) {
+        newVal = SendMessageW(infoPtr->Buddy, LB_GETCARETINDEX, 0, 0);
+        if(newVal < 0) return FALSE;
+    } else {
+        /* we have a regular window, so will get the text */
+        /* note that a zero-length string is a legitimate value for 'txt',
+         * and ought to result in a successful conversion to '0'. */
+        if (GetWindowTextW(infoPtr->Buddy, txt, COUNT_OF(txt)) < 0)
+            return FALSE;
+
+        sep = UPDOWN_GetThousandSep();
+
+        /* now get rid of the separators */
+        for(src = dst = txt; *src; src++)
+            if(*src != sep) *dst++ = *src;
+        *dst = 0;
+
+        /* try to convert the number and validate it */
+        newVal = strtolW(txt, &src, infoPtr->Base);
+        if(*src || !UPDOWN_InBounds (infoPtr, newVal)) return FALSE;
+    }
+
+    TRACE("new value(%d) from buddy (old=%d)\n", newVal, infoPtr->CurVal);
+    infoPtr->CurVal = newVal;
+    return TRUE;
 }
 
 
@@ -279,293 +304,371 @@ static BOOL UPDOWN_GetBuddyInt (HWND hwnd)
  * Tries to set the pos to the buddy window based on current pos
  * returns:
  *   TRUE  - if it set the caption of the  buddy successfully
- *   FALSE - if an error occured
+ *   FALSE - if an error occurred
  */
-static BOOL UPDOWN_SetBuddyInt (HWND hwnd)
+static BOOL UPDOWN_SetBuddyInt (const UPDOWN_INFO *infoPtr)
 {
-  UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd);
-  char txt1[20], sep;
-  int len;
-
-  if (!IsWindow(infoPtr->Buddy)) 
-    return FALSE;
-
-  TRACE("set new value(%d) to buddy.\n",
-              infoPtr->CurVal);
-
-  /*if the buddy is a list window, we must set curr index */
-  if(!lstrcmpA (infoPtr->szBuddyClass, "ListBox")){
-    SendMessageA(infoPtr->Buddy, LB_SETCURSEL, infoPtr->CurVal, 0);
-  }
-  else{ /* Regular window, so set caption to the number */
-    len = sprintf(txt1, (infoPtr->Base==16) ? "%X" : "%d", infoPtr->CurVal);
-
-    sep = UPDOWN_GetThousandSep(); 
-
-    /* Do thousands seperation if necessary */
-    if (!(GetWindowLongA (hwnd, GWL_STYLE) & UDS_NOTHOUSANDS) && (len > 3)) {
-      char txt2[20], *src = txt1, *dst = txt2;
-      if(len%3 > 0){
-       lstrcpynA (dst, src, len%3 + 1);      /* need to include the null */ 
-       dst += len%3;
-       src += len%3;
-      }
-      for(len=0; *src; len++){
-       if(len%3==0)
-         *dst++ = sep;
-       *dst++ = *src++;
-      }
-      *dst = 0;           /* null terminate it */
-      strcpy(txt1, txt2); /* move it to the proper place */
+    WCHAR fmt[3] = { '%', 'd', '\0' };
+    WCHAR txt[20];
+    int len;
+
+    if (!((infoPtr->dwStyle & UDS_SETBUDDYINT) && IsWindow(infoPtr->Buddy))) 
+        return FALSE;
+
+    TRACE("set new value(%d) to buddy.\n", infoPtr->CurVal);
+
+    /*if the buddy is a list window, we must set curr index */
+    if (UPDOWN_IsBuddyListbox(infoPtr)) {
+        return SendMessageW(infoPtr->Buddy, LB_SETCURSEL, infoPtr->CurVal, 0) != LB_ERR;
     }
-    SetWindowTextA(infoPtr->Buddy, txt1);
-  }
 
-  return TRUE;
-} 
+    /* Regular window, so set caption to the number */
+    if (infoPtr->Base == 16) fmt[1] = 'X';
+    len = wsprintfW(txt, fmt, infoPtr->CurVal);
+
+
+    /* Do thousands separation if necessary */
+    if (!(infoPtr->dwStyle & UDS_NOTHOUSANDS) && (len > 3)) {
+        WCHAR tmp[COUNT_OF(txt)], *src = tmp, *dst = txt;
+        WCHAR sep = UPDOWN_GetThousandSep();
+       int start = len % 3;
+
+       memcpy(tmp, txt, sizeof(txt));
+       if (start == 0) start = 3;
+       dst += start;
+       src += start;
+        for (len=0; *src; len++) {
+           if (len % 3 == 0) *dst++ = sep;
+           *dst++ = *src++;
+        }
+        *dst = 0;
+    }
+
+    return SetWindowTextW(infoPtr->Buddy, txt);
+}
 
 /***********************************************************************
- * UPDOWN_DrawBuddyBorder [Internal]
+ * UPDOWN_DrawBuddyBackground
  *
- * When we have a buddy set and that we are aligned on our buddy, we
- * want to draw a sunken edge to make like we are part of that control.
+ * Draw buddy background for visual integration.
  */
-static void UPDOWN_DrawBuddyBorder (HWND hwnd, HDC hdc)
+static BOOL UPDOWN_DrawBuddyBackground (const UPDOWN_INFO *infoPtr, HDC hdc)
 {
-  DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
-  RECT  clientRect;
-
-  GetClientRect(hwnd, &clientRect);
-
-  if (dwStyle & UDS_ALIGNLEFT)
-    DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_BOTTOM | BF_LEFT | BF_TOP);
-  else
-    DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_BOTTOM | BF_RIGHT | BF_TOP);
+    RECT br;
+    HTHEME buddyTheme = GetWindowTheme (infoPtr->Buddy);
+    if (!buddyTheme) return FALSE;
+
+    GetClientRect (infoPtr->Buddy, &br);
+    MapWindowPoints (infoPtr->Buddy, infoPtr->Self, (POINT*)&br, 2);
+    /* FIXME: take disabled etc. into account */
+    DrawThemeBackground (buddyTheme, hdc, 0, 0, &br, NULL);
+    return TRUE;
 }
 
 /***********************************************************************
- * UPDOWN_Draw [Internal]
+ * UPDOWN_Draw
  *
  * Draw the arrows. The background need not be erased.
  */
-static void UPDOWN_Draw (HWND hwnd, HDC hdc)
+static LRESULT UPDOWN_Draw (const UPDOWN_INFO *infoPtr, HDC hdc)
 {
-  UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd);
-  DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
-  BOOL prssed;
-  RECT rect;
-
-  /*
-   * Draw the common border between ourselves and our buddy.
-   */
-  if (UPDOWN_HasBuddyBorder(hwnd))
-    UPDOWN_DrawBuddyBorder(hwnd, hdc);
-  
-  /* Draw the incr button */
-  UPDOWN_GetArrowRect (hwnd, &rect, TRUE);
-  prssed = (infoPtr->Flags & FLAG_INCR) && (infoPtr->Flags & FLAG_MOUSEIN);
-  DrawFrameControl(hdc, &rect, DFC_SCROLL, 
-       (dwStyle & UDS_HORZ ? DFCS_SCROLLRIGHT : DFCS_SCROLLUP) |
-       (prssed ? DFCS_PUSHED : 0) |
-       (dwStyle&WS_DISABLED ? DFCS_INACTIVE : 0) );
-
-  /* Draw the space between the buttons */
-  rect.top = rect.bottom; rect.bottom++;
-  DrawEdge(hdc, &rect, 0, BF_MIDDLE);
-                   
-  /* Draw the decr button */
-  UPDOWN_GetArrowRect(hwnd, &rect, FALSE);
-  prssed = (infoPtr->Flags & FLAG_DECR) && (infoPtr->Flags & FLAG_MOUSEIN);
-  DrawFrameControl(hdc, &rect, DFC_SCROLL, 
-       (dwStyle & UDS_HORZ ? DFCS_SCROLLLEFT : DFCS_SCROLLDOWN) |
-       (prssed ? DFCS_PUSHED : 0) |
-       (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) );
+    BOOL uPressed, uHot, dPressed, dHot;
+    RECT rect;
+    HTHEME theme = GetWindowTheme (infoPtr->Self);
+    int uPart = 0, uState = 0, dPart = 0, dState = 0;
+    BOOL needBuddyBg = FALSE;
+
+    uPressed = (infoPtr->Flags & FLAG_PRESSED) && (infoPtr->Flags & FLAG_INCR);
+    uHot = (infoPtr->Flags & FLAG_INCR) && (infoPtr->Flags & FLAG_MOUSEIN);
+    dPressed = (infoPtr->Flags & FLAG_PRESSED) && (infoPtr->Flags & FLAG_DECR);
+    dHot = (infoPtr->Flags & FLAG_DECR) && (infoPtr->Flags & FLAG_MOUSEIN);
+    if (theme) {
+        uPart = (infoPtr->dwStyle & UDS_HORZ) ? SPNP_UPHORZ : SPNP_UP;
+        uState = (infoPtr->dwStyle & WS_DISABLED) ? DNS_DISABLED 
+            : (uPressed ? DNS_PRESSED : (uHot ? DNS_HOT : DNS_NORMAL));
+        dPart = (infoPtr->dwStyle & UDS_HORZ) ? SPNP_DOWNHORZ : SPNP_DOWN;
+        dState = (infoPtr->dwStyle & WS_DISABLED) ? DNS_DISABLED 
+            : (dPressed ? DNS_PRESSED : (dHot ? DNS_HOT : DNS_NORMAL));
+        needBuddyBg = IsWindow (infoPtr->Buddy)
+            && (IsThemeBackgroundPartiallyTransparent (theme, uPart, uState)
+              || IsThemeBackgroundPartiallyTransparent (theme, dPart, dState));
+    }
+
+    /* Draw the common border between ourselves and our buddy */
+    if (UPDOWN_HasBuddyBorder(infoPtr) || needBuddyBg) {
+        if (!theme || !UPDOWN_DrawBuddyBackground (infoPtr, hdc)) {
+            GetClientRect(infoPtr->Self, &rect);
+           DrawEdge(hdc, &rect, EDGE_SUNKEN,
+                    BF_BOTTOM | BF_TOP |
+                    (infoPtr->dwStyle & UDS_ALIGNLEFT ? BF_LEFT : BF_RIGHT));
+        }
+    }
+
+    /* Draw the incr button */
+    UPDOWN_GetArrowRect (infoPtr, &rect, FLAG_INCR);
+    if (theme) {
+        DrawThemeBackground(theme, hdc, uPart, uState, &rect, NULL);
+    } else {
+        DrawFrameControl(hdc, &rect, DFC_SCROLL,
+            (infoPtr->dwStyle & UDS_HORZ ? DFCS_SCROLLRIGHT : DFCS_SCROLLUP) |
+            ((infoPtr->dwStyle & UDS_HOTTRACK) && uHot ? DFCS_HOT : 0) |
+            (uPressed ? DFCS_PUSHED : 0) |
+            (infoPtr->dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) );
+    }
+
+    /* Draw the decr button */
+    UPDOWN_GetArrowRect(infoPtr, &rect, FLAG_DECR);
+    if (theme) {
+        DrawThemeBackground(theme, hdc, dPart, dState, &rect, NULL);
+    } else {
+        DrawFrameControl(hdc, &rect, DFC_SCROLL,
+            (infoPtr->dwStyle & UDS_HORZ ? DFCS_SCROLLLEFT : DFCS_SCROLLDOWN) |
+            ((infoPtr->dwStyle & UDS_HOTTRACK) && dHot ? DFCS_HOT : 0) |
+            (dPressed ? DFCS_PUSHED : 0) |
+            (infoPtr->dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) );
+    }
+
+    return 0;
 }
 
 /***********************************************************************
- * UPDOWN_Refresh [Internal]
+ * UPDOWN_Paint
  *
- * Synchronous drawing (must NOT be used in WM_PAINT).
+ * Asynchronous drawing (must ONLY be used in WM_PAINT).
  * Calls UPDOWN_Draw.
  */
-static void UPDOWN_Refresh (HWND hwnd)
+static LRESULT UPDOWN_Paint (const UPDOWN_INFO *infoPtr, HDC hdc)
 {
-    HDC hdc;
-  
-    hdc = GetDC (hwnd);
-    UPDOWN_Draw (hwnd, hdc);
-    ReleaseDC (hwnd, hdc);
+    PAINTSTRUCT ps;
+    if (hdc) return UPDOWN_Draw (infoPtr, hdc);
+    hdc = BeginPaint (infoPtr->Self, &ps);
+    UPDOWN_Draw (infoPtr, hdc);
+    EndPaint (infoPtr->Self, &ps);
+    return 0;
 }
 
+/***********************************************************************
+ * UPDOWN_KeyPressed
+ *
+ * Handle key presses (up & down) when we have to do so
+ */
+static LRESULT UPDOWN_KeyPressed(UPDOWN_INFO *infoPtr, int key)
+{
+    int arrow;
+
+    if (key == VK_UP) arrow = FLAG_INCR;
+    else if (key == VK_DOWN) arrow = FLAG_DECR;
+    else return 1;
+
+    UPDOWN_GetBuddyInt (infoPtr);
+    infoPtr->Flags &= ~FLAG_ARROW;
+    infoPtr->Flags |= FLAG_PRESSED | arrow;
+    InvalidateRect (infoPtr->Self, NULL, FALSE);
+    SetTimer(infoPtr->Self, TIMER_AUTOPRESS, AUTOPRESS_DELAY, 0);
+    UPDOWN_DoAction (infoPtr, 1, arrow);
+    return 0;
+}
 
 /***********************************************************************
- * UPDOWN_Paint [Internal]
+ * UPDOWN_MouseWheel
  *
- * Asynchronous drawing (must ONLY be used in WM_PAINT).
- * Calls UPDOWN_Draw.
+ * Handle mouse wheel scrolling
  */
-static void UPDOWN_Paint (HWND hwnd, HDC passedDC)
+static LRESULT UPDOWN_MouseWheel(UPDOWN_INFO *infoPtr, WPARAM wParam)
 {
-    PAINTSTRUCT ps;
-    HDC         hdc = passedDC;
+    int iWheelDelta = GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA;
+
+    if (wParam & (MK_SHIFT | MK_CONTROL))
+        return 0;
 
-    if (passedDC == 0)
-      hdc = BeginPaint (hwnd, &ps);
-    
-    UPDOWN_Draw (hwnd, hdc);
+    if (iWheelDelta != 0)
+    {
+        UPDOWN_GetBuddyInt(infoPtr);
+        UPDOWN_DoAction(infoPtr, abs(iWheelDelta), iWheelDelta > 0 ? FLAG_INCR : FLAG_DECR);
+    }
 
-    if (passedDC == 0)
-      EndPaint (hwnd, &ps);
+    return 1;
+}
+
+
+/***********************************************************************
+ * UPDOWN_Buddy_SubclassProc used to handle messages sent to the buddy
+ *                           control.
+ */
+static LRESULT CALLBACK
+UPDOWN_Buddy_SubclassProc(HWND  hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    WNDPROC superClassWndProc = (WNDPROC)GetPropW(hwnd, BUDDY_SUPERCLASS_WNDPROC);
+
+    TRACE("hwnd=%p, wndProc=%p, uMsg=%04x, wParam=%08lx, lParam=%08lx\n",
+          hwnd, superClassWndProc, uMsg, wParam, lParam);
+
+    if (uMsg == WM_KEYDOWN) {
+        HWND upDownHwnd = GetPropW(hwnd, BUDDY_UPDOWN_HWND);
+
+       UPDOWN_KeyPressed(UPDOWN_GetInfoPtr(upDownHwnd), (int)wParam);
+    }
+    else if (uMsg == WM_MOUSEWHEEL) {
+        HWND upDownHwnd = GetPropW(hwnd, BUDDY_UPDOWN_HWND);
+
+       UPDOWN_MouseWheel(UPDOWN_GetInfoPtr(upDownHwnd), (int)wParam);
+    }
+
+    return CallWindowProcW( superClassWndProc, hwnd, uMsg, wParam, lParam);
 }
 
 /***********************************************************************
  *           UPDOWN_SetBuddy
- * Tests if 'hwndBud' is a valid window handle. If not, returns FALSE.
- * Else, sets it as a new Buddy.
- * Then, it should subclass the buddy 
+ *
+ * Sets bud as a new Buddy.
+ * Then, it should subclass the buddy
  * If window has the UDS_ARROWKEYS, it subcalsses the buddy window to
  * process the UP/DOWN arrow keys.
  * If window has the UDS_ALIGNLEFT or UDS_ALIGNRIGHT style
  * the size/pos of the buddy and the control are adjusted accordingly.
  */
-static BOOL UPDOWN_SetBuddy (HWND hwnd, HWND hwndBud)
+static HWND UPDOWN_SetBuddy (UPDOWN_INFO* infoPtr, HWND bud)
 {
-  UPDOWN_INFO* infoPtr = UPDOWN_GetInfoPtr (hwnd); 
-  DWORD        dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
-  RECT         budRect;  /* new coord for the buddy */
-  int          x,width;  /* new x position and width for the up-down */
-  WNDPROC baseWndProc, currWndProc;
-         
-  /* Is it a valid bud? */
-  if(!IsWindow(hwndBud))
-    return FALSE;
-
-  /* there is already a body assigned */
-  if ( infoPtr->Buddy )  
-    RemovePropA(infoPtr->Buddy, BUDDY_UPDOWN_HWND);
-
-  /* Store buddy window handle */
-  infoPtr->Buddy = hwndBud;   
-
-  /* keep upDown ctrl hwnd in a buddy property */            
-  SetPropA( hwndBud, BUDDY_UPDOWN_HWND, hwnd); 
-
-  /* Store buddy window clas name */
-  memset(infoPtr->szBuddyClass, 0, UPDOWN_BUDDYCLASSNAMELEN);
-  GetClassNameA (hwndBud, infoPtr->szBuddyClass, UPDOWN_BUDDYCLASSNAMELEN-1);
-
-  if(dwStyle & UDS_ARROWKEYS){
-    /* Note that I don't clear the BUDDY_SUPERCLASS_WNDPROC property 
-       when we reset the upDown ctrl buddy to another buddy because it is not 
-       good to break the window proc chain. */
-
-       currWndProc = (WNDPROC) GetWindowLongA(hwndBud, GWL_WNDPROC);
-       if (currWndProc != UPDOWN_Buddy_SubclassProc) 
-       {
-               // replace the buddy's WndProc with ours
-               baseWndProc = (WNDPROC)SetWindowLongA(hwndBud, GWL_WNDPROC,
-                             (LPARAM)UPDOWN_Buddy_SubclassProc); 
-               // and save the base class' WndProc 
-               SetPropA(hwndBud, BUDDY_SUPERCLASS_WNDPROC, (HANDLE)baseWndProc);
-       }
-       // else
-       // its already been subclassed, don't overwrite BUDDY_SUPERCLASS_WNDPROC
-  }
-
-  /* Get the rect of the buddy relative to its parent */
-  GetWindowRect(infoPtr->Buddy, &budRect);
-  MapWindowPoints(HWND_DESKTOP, GetParent(infoPtr->Buddy),
-                 (POINT *)(&budRect.left), 2);
-
-  /* now do the positioning */
-  if  (dwStyle & UDS_ALIGNLEFT) {
-    x  = budRect.left;
-    budRect.left += DEFAULT_WIDTH+DEFAULT_XSEP;
-  }
-  else if (dwStyle & UDS_ALIGNRIGHT){
-    budRect.right -= DEFAULT_WIDTH+DEFAULT_XSEP;
-    x  = budRect.right+DEFAULT_XSEP;
-  }
-  else {
-    x  = budRect.right+DEFAULT_XSEP;
-  }
-
-  /* first adjust the buddy to accomodate the up/down */
-  SetWindowPos(infoPtr->Buddy, 0, budRect.left, budRect.top,
-              budRect.right  - budRect.left, budRect.bottom - budRect.top, 
-              SWP_NOACTIVATE|SWP_NOZORDER);
-
-  /* now position the up/down */
-  /* Since the UDS_ALIGN* flags were used, */
-  /* we will pick the position and size of the window. */
-  width = DEFAULT_WIDTH;
-
-  /*
-   * If the updown has a buddy border, it has to overlap with the buddy
-   * to look as if it is integrated with the buddy control. 
-   * We nudge the control or change it size to overlap.
-   */
-  if (UPDOWN_HasBuddyBorder(hwnd))
-  {
-    if(dwStyle & UDS_ALIGNLEFT)
-      width+=DEFAULT_BUDDYBORDER;
-    else
-      x-=DEFAULT_BUDDYBORDER;
-  }
+    static const WCHAR editW[] = { 'E', 'd', 'i', 't', 0 };
+    static const WCHAR listboxW[] = { 'L', 'i', 's', 't', 'b', 'o', 'x', 0 };
+    RECT  budRect;  /* new coord for the buddy */
+    int   x, width;  /* new x position and width for the up-down */
+    WNDPROC baseWndProc;
+    WCHAR buddyClass[40];
+    HWND ret;
+
+    TRACE("(hwnd=%p, bud=%p)\n", infoPtr->Self, bud);
+
+    ret = infoPtr->Buddy;
 
-  SetWindowPos (hwnd, infoPtr->Buddy, 
-               x, budRect.top-DEFAULT_ADDTOP,
-               width, (budRect.bottom-budRect.top)+DEFAULT_ADDTOP+DEFAULT_ADDBOT,
-               SWP_NOACTIVATE);
+    /* there is already a body assigned */
+    if (infoPtr->Buddy)  RemovePropW(infoPtr->Buddy, BUDDY_UPDOWN_HWND);
 
-  return TRUE;
-}        
+    if(!IsWindow(bud))
+        bud = 0;
+
+    /* Store buddy window handle */
+    infoPtr->Buddy = bud;
+
+    if(bud) {
+
+        /* keep upDown ctrl hwnd in a buddy property */
+        SetPropW( bud, BUDDY_UPDOWN_HWND, infoPtr->Self);
+
+        /* Store buddy window class type */
+        infoPtr->BuddyType = BUDDY_TYPE_UNKNOWN;
+        if (GetClassNameW(bud, buddyClass, COUNT_OF(buddyClass))) {
+            if (lstrcmpiW(buddyClass, editW) == 0)
+                infoPtr->BuddyType = BUDDY_TYPE_EDIT;
+            else if (lstrcmpiW(buddyClass, listboxW) == 0)
+                infoPtr->BuddyType = BUDDY_TYPE_LISTBOX;
+        }
+
+        if(infoPtr->dwStyle & UDS_ARROWKEYS){
+            /* Note that I don't clear the BUDDY_SUPERCLASS_WNDPROC property
+               when we reset the upDown ctrl buddy to another buddy because it is not
+               good to break the window proc chain. */
+            if (!GetPropW(bud, BUDDY_SUPERCLASS_WNDPROC)) {
+                baseWndProc = (WNDPROC)SetWindowLongPtrW(bud, GWLP_WNDPROC, (LPARAM)UPDOWN_Buddy_SubclassProc);
+                SetPropW(bud, BUDDY_SUPERCLASS_WNDPROC, (HANDLE)baseWndProc);
+            }
+        }
+
+        /* Get the rect of the buddy relative to its parent */
+        GetWindowRect(infoPtr->Buddy, &budRect);
+        MapWindowPoints(HWND_DESKTOP, GetParent(infoPtr->Buddy), (POINT *)(&budRect.left), 2);
+
+        /* now do the positioning */
+        if  (infoPtr->dwStyle & UDS_ALIGNLEFT) {
+            x  = budRect.left;
+            budRect.left += DEFAULT_WIDTH + DEFAULT_XSEP;
+        } else if (infoPtr->dwStyle & UDS_ALIGNRIGHT) {
+            budRect.right -= DEFAULT_WIDTH + DEFAULT_XSEP;
+            x  = budRect.right+DEFAULT_XSEP;
+        } else {
+            /* nothing to do */
+            return ret;
+        }
+
+        /* first adjust the buddy to accommodate the up/down */
+        SetWindowPos(infoPtr->Buddy, 0, budRect.left, budRect.top,
+                     budRect.right  - budRect.left, budRect.bottom - budRect.top,
+                     SWP_NOACTIVATE|SWP_NOZORDER);
+
+        /* now position the up/down */
+        /* Since the UDS_ALIGN* flags were used, */
+        /* we will pick the position and size of the window. */
+        width = DEFAULT_WIDTH;
+
+        /*
+         * If the updown has a buddy border, it has to overlap with the buddy
+         * to look as if it is integrated with the buddy control.
+         * We nudge the control or change its size to overlap.
+         */
+        if (UPDOWN_HasBuddyBorder(infoPtr)) {
+            if(infoPtr->dwStyle & UDS_ALIGNLEFT)
+                width += DEFAULT_BUDDYBORDER;
+            else
+                x -= DEFAULT_BUDDYBORDER;
+        }
+
+        SetWindowPos(infoPtr->Self, 0, x,
+                     budRect.top - DEFAULT_ADDTOP, width,
+                     budRect.bottom - budRect.top + DEFAULT_ADDTOP + DEFAULT_ADDBOT,
+                     SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOZORDER);
+    } else {
+        RECT rect;
+        GetWindowRect(infoPtr->Self, &rect);
+        MapWindowPoints(HWND_DESKTOP, GetParent(infoPtr->Self), (POINT *)&rect, 2);
+        SetWindowPos(infoPtr->Self, 0, rect.left, rect.top, DEFAULT_WIDTH, rect.bottom - rect.top,
+                     SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOZORDER);
+    }
+    return ret;
+}
 
 /***********************************************************************
  *           UPDOWN_DoAction
  *
- * This function increments/decrements the CurVal by the 
- * 'delta' amount according to the 'incr' flag
+ * This function increments/decrements the CurVal by the
+ * 'delta' amount according to the 'action' flag which can be a
+ * combination of FLAG_INCR and FLAG_DECR
  * It notifies the parent as required.
  * It handles wraping and non-wraping correctly.
  * It is assumed that delta>0
  */
-static void UPDOWN_DoAction (HWND hwnd, int delta, BOOL incr)
+static void UPDOWN_DoAction (UPDOWN_INFO *infoPtr, int delta, int action)
 {
-  UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd); 
-  DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
-  NM_UPDOWN ni;
-
-  TRACE("%s by %d\n", incr ? "inc" : "dec", delta);
-
-  /* check if we can do the modification first */
-  delta *= (incr ? 1 : -1) * (infoPtr->MaxVal < infoPtr->MinVal ? -1 : 1);
-
-  /* We must notify parent now to obtain permission */
-  ni.iPos = infoPtr->CurVal;
-  ni.iDelta = delta;
-  ni.hdr.hwndFrom = hwnd;
-  ni.hdr.idFrom   = GetWindowLongA (hwnd, GWL_ID);
-  ni.hdr.code = UDN_DELTAPOS; 
-  if (!SendMessageA(GetParent (hwnd), WM_NOTIFY,
-                  (WPARAM)ni.hdr.idFrom, (LPARAM)&ni))
-  {
-     /* Parent said: OK to adjust */
-
-     /* Now adjust value with (maybe new) delta */
-     if (UPDOWN_OffsetVal (hwnd, ni.iDelta))
-     {
-        /* Now take care about our buddy */
-        if(infoPtr->Buddy && IsWindow(infoPtr->Buddy)
-           && (dwStyle & UDS_SETBUDDYINT) )
-           UPDOWN_SetBuddyInt (hwnd);
-     }
-  }
-  
-  /* Also, notify it. This message is sent in any case. */
-  SendMessageA (GetParent (hwnd), 
-               dwStyle & UDS_HORZ ? WM_HSCROLL : WM_VSCROLL, 
-                MAKELONG(SB_THUMBPOSITION, infoPtr->CurVal), hwnd);
+    NM_UPDOWN ni;
+
+    TRACE("%d by %d\n", action, delta);
+
+    /* check if we can do the modification first */
+    delta *= (action & FLAG_INCR ? 1 : -1) * (infoPtr->MaxVal < infoPtr->MinVal ? -1 : 1);
+    if ( (action & FLAG_INCR) && (action & FLAG_DECR) ) delta = 0;
+
+    TRACE("current %d, delta: %d\n", infoPtr->CurVal, delta);
+
+    /* We must notify parent now to obtain permission */
+    ni.iPos = infoPtr->CurVal;
+    ni.iDelta = delta;
+    ni.hdr.hwndFrom = infoPtr->Self;
+    ni.hdr.idFrom   = GetWindowLongPtrW (infoPtr->Self, GWLP_ID);
+    ni.hdr.code = UDN_DELTAPOS;
+    if (!SendMessageW(infoPtr->Notify, WM_NOTIFY, ni.hdr.idFrom, (LPARAM)&ni)) {
+        /* Parent said: OK to adjust */
+
+        /* Now adjust value with (maybe new) delta */
+        if (UPDOWN_OffsetVal (infoPtr, ni.iDelta)) {
+            TRACE("new %d, delta: %d\n", infoPtr->CurVal, ni.iDelta);
+
+            /* Now take care about our buddy */
+            UPDOWN_SetBuddyInt (infoPtr);
+        }
+    }
+
+    /* Also, notify it. This message is sent in any case. */
+    SendMessageW( infoPtr->Notify, (infoPtr->dwStyle & UDS_HORZ) ? WM_HSCROLL : WM_VSCROLL,
+                 MAKELONG(SB_THUMBPOSITION, infoPtr->CurVal), (LPARAM)infoPtr->Self);
 }
 
 /***********************************************************************
@@ -574,15 +677,13 @@ static void UPDOWN_DoAction (HWND hwnd, int delta, BOOL incr)
  * Returns TRUE if it is enabled as well as its buddy (if any)
  *         FALSE otherwise
  */
-static BOOL UPDOWN_IsEnabled (HWND hwnd)
+static BOOL UPDOWN_IsEnabled (const UPDOWN_INFO *infoPtr)
 {
-  UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd);
-
-  if(GetWindowLongA (hwnd, GWL_STYLE) & WS_DISABLED)
-    return FALSE;
-  if(infoPtr->Buddy)
-    return IsWindowEnabled(infoPtr->Buddy);
-  return TRUE;
+    if (!IsWindowEnabled(infoPtr->Self))
+        return FALSE;
+    if(infoPtr->Buddy)
+        return IsWindowEnabled(infoPtr->Buddy);
+    return TRUE;
 }
 
 /***********************************************************************
@@ -590,27 +691,30 @@ static BOOL UPDOWN_IsEnabled (HWND hwnd)
  *
  * Deletes any timers, releases the mouse and does  redraw if necessary.
  * If the control is not in "capture" mode, it does nothing.
- * If the control was not in cancel mode, it returns FALSE. 
+ * If the control was not in cancel mode, it returns FALSE.
  * If the control was in cancel mode, it returns TRUE.
  */
-static BOOL UPDOWN_CancelMode (HWND hwnd)
+static BOOL UPDOWN_CancelMode (UPDOWN_INFO *infoPtr)
 {
-  UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd);
-  /* if not in 'capture' mode, do nothing */
-  if(!(infoPtr->Flags & FLAG_CLICKED))
-    return FALSE;
-
-  KillTimer (hwnd, TIMERID1); /* kill all possible timers */
-  KillTimer (hwnd, TIMERID2);
-  
-  if (GetCapture() == hwnd)    /* let the mouse go         */
-    ReleaseCapture();          /* if we still have it      */  
-  
-  infoPtr->Flags = 0;          /* get rid of any flags     */
-  UPDOWN_Refresh (hwnd);       /* redraw the control just in case */
-  
-  return TRUE;
+    if (!(infoPtr->Flags & FLAG_PRESSED)) return FALSE;
+
+    KillTimer (infoPtr->Self, TIMER_AUTOREPEAT);
+    KillTimer (infoPtr->Self, TIMER_ACCEL);
+    KillTimer (infoPtr->Self, TIMER_AUTOPRESS);
+
+    if (GetCapture() == infoPtr->Self) {
+       NMHDR hdr;
+       hdr.hwndFrom = infoPtr->Self;
+       hdr.idFrom   = GetWindowLongPtrW (infoPtr->Self, GWLP_ID);
+       hdr.code = NM_RELEASEDCAPTURE;
+       SendMessageW(infoPtr->Notify, WM_NOTIFY, hdr.idFrom, (LPARAM)&hdr);
+       ReleaseCapture();
+    }
+
+    infoPtr->Flags &= ~FLAG_PRESSED;
+    InvalidateRect (infoPtr->Self, NULL, FALSE);
+
+    return TRUE;
 }
 
 /***********************************************************************
@@ -618,80 +722,91 @@ static BOOL UPDOWN_CancelMode (HWND hwnd)
  *
  * Handle a mouse event for the updown.
  * 'pt' is the location of the mouse event in client or
- * windows coordinates. 
+ * windows coordinates.
  */
-static void UPDOWN_HandleMouseEvent (HWND hwnd, UINT msg, POINT pt)
+static void UPDOWN_HandleMouseEvent (UPDOWN_INFO *infoPtr, UINT msg, INT x, INT y)
 {
-  UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd);
-  DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
-  RECT rect;
-  int temp;
+    POINT pt = { x, y };
+    RECT rect;
+    int temp, arrow;
+    TRACKMOUSEEVENT tme;
+
+    TRACE("msg %04x point %s\n", msg, wine_dbgstr_point(&pt));
 
-  switch(msg)
+    switch(msg)
     {
-    case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
-      /* If we are already in the 'clicked' mode, then nothing to do */
-      if(infoPtr->Flags & FLAG_CLICKED)
-       return;
-
-      /* If the buddy is an edit, will set focus to it */
-      if (!lstrcmpA (infoPtr->szBuddyClass, "Edit"))
-       SetFocus(infoPtr->Buddy);
-
-      /* Now see which one is the 'active' arrow */
-      temp = UPDOWN_GetArrowFromPoint (hwnd, &rect, pt);
-
-      /* Update the CurVal if necessary */
-      if (dwStyle & UDS_SETBUDDYINT)
-       UPDOWN_GetBuddyInt (hwnd);
-       
-      /* Set up the correct flags */
-      infoPtr->Flags  = 0; 
-      infoPtr->Flags |= temp ? FLAG_INCR : FLAG_DECR;
-      infoPtr->Flags |= FLAG_MOUSEIN;
-      
-      /* repaint the control */
-      UPDOWN_Refresh (hwnd);
-
-      /* process the click */
-      UPDOWN_DoAction (hwnd, 1, infoPtr->Flags & FLAG_INCR);
-
-      /* now capture all mouse messages */
-      SetCapture (hwnd);
-
-      /* and startup the first timer */
-      SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0); 
-      break;
-
-    case WM_MOUSEMOVE:
-      /* If we are not in the 'clicked' mode, then nothing to do */
-      if(!(infoPtr->Flags & FLAG_CLICKED))
-       return;
-
-      /* save the flags to see if any got modified */
-      temp = infoPtr->Flags;
-
-      /* Now get the 'active' arrow rectangle */
-      if (infoPtr->Flags & FLAG_INCR)
-       UPDOWN_GetArrowRect (hwnd, &rect, TRUE);
-      else
-       UPDOWN_GetArrowRect (hwnd, &rect, FALSE);
-
-      /* Update the flags if we are in/out */
-      if(PtInRect(&rect, pt))
-       infoPtr->Flags |=  FLAG_MOUSEIN;
-      else{
-       infoPtr->Flags &= ~FLAG_MOUSEIN;
-       if(accelIndex != -1) /* if we have accel info */
-         accelIndex = 0;    /* reset it              */
-      }
-      /* If state changed, redraw the control */
-      if(temp != infoPtr->Flags)
-       UPDOWN_Refresh (hwnd);
-      break;
-
-      default:
-       ERR("Impossible case!\n");
+        case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
+
+            /* If the buddy is an edit, will set focus to it */
+           if (UPDOWN_IsBuddyEdit(infoPtr)) SetFocus(infoPtr->Buddy);
+
+            /* Now see which one is the 'active' arrow */
+            arrow = UPDOWN_GetArrowFromPoint (infoPtr, &rect, pt);
+
+            /* Update the flags if we are in/out */
+            infoPtr->Flags &= ~(FLAG_MOUSEIN | FLAG_ARROW);
+            if (arrow)
+                infoPtr->Flags |= FLAG_MOUSEIN | arrow;
+            else
+                if (infoPtr->AccelIndex != -1) infoPtr->AccelIndex = 0;
+
+           if (infoPtr->Flags & FLAG_ARROW) {
+
+               /* Update the CurVal if necessary */
+               UPDOWN_GetBuddyInt (infoPtr);
+
+               /* Set up the correct flags */
+               infoPtr->Flags |= FLAG_PRESSED;
+
+               /* repaint the control */
+               InvalidateRect (infoPtr->Self, NULL, FALSE);
+
+               /* process the click */
+               temp = (infoPtr->AccelCount && infoPtr->AccelVect) ? infoPtr->AccelVect[0].nInc : 1;
+               UPDOWN_DoAction (infoPtr, temp, infoPtr->Flags & FLAG_ARROW);
+
+               /* now capture all mouse messages */
+               SetCapture (infoPtr->Self);
+
+               /* and startup the first timer */
+               SetTimer(infoPtr->Self, TIMER_AUTOREPEAT, INITIAL_DELAY, 0);
+           }
+            break;
+
+       case WM_MOUSEMOVE:
+            /* save the flags to see if any got modified */
+            temp = infoPtr->Flags;
+
+            /* Now see which one is the 'active' arrow */
+            arrow = UPDOWN_GetArrowFromPoint (infoPtr, &rect, pt);
+
+            /* Update the flags if we are in/out */
+           infoPtr->Flags &= ~(FLAG_MOUSEIN | FLAG_ARROW);
+            if(arrow) {
+               infoPtr->Flags |=  FLAG_MOUSEIN | arrow;
+            } else {
+               if(infoPtr->AccelIndex != -1) infoPtr->AccelIndex = 0;
+            }
+
+            /* If state changed, redraw the control */
+            if(temp != infoPtr->Flags)
+                InvalidateRect (infoPtr->Self, NULL, FALSE);
+
+            /* Set up tracking so the mousein flags can be reset when the 
+             * mouse leaves the control */
+            tme.cbSize = sizeof( tme );
+            tme.dwFlags = TME_LEAVE;
+            tme.hwndTrack = infoPtr->Self;
+            TrackMouseEvent (&tme);
+
+            break;
+        case WM_MOUSELEAVE:
+           infoPtr->Flags &= ~(FLAG_MOUSEIN | FLAG_ARROW);
+            InvalidateRect (infoPtr->Self, NULL, FALSE);
+            break;
+
+       default:
+           ERR("Impossible case (msg=%x)!\n", msg);
     }
 
 }
@@ -699,315 +814,293 @@ static void UPDOWN_HandleMouseEvent (HWND hwnd, UINT msg, POINT pt)
 /***********************************************************************
  *           UpDownWndProc
  */
-static LRESULT WINAPI UpDownWindowProc(HWND hwnd, UINT message, WPARAM wParam,
-                               LPARAM lParam)
+static LRESULT WINAPI UpDownWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
-  UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd);
-  DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
-  int temp;
-  if (!infoPtr && (message != WM_CREATE) && (message != WM_NCCREATE))
-      return DefWindowProcA (hwnd, message, wParam, lParam); 
-  switch(message)
+    UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd);
+    static const WCHAR themeClass[] = {'S','p','i','n',0};
+    HTHEME theme;
+
+    TRACE("hwnd=%p msg=%04x wparam=%08lx lparam=%08lx\n", hwnd, message, wParam, lParam);
+
+    if (!infoPtr && (message != WM_CREATE))
+        return DefWindowProcW (hwnd, message, wParam, lParam);
+
+    switch(message)
     {
-    case WM_NCCREATE:
-      /* get rid of border, if any */
-      SetWindowLongA (hwnd, GWL_STYLE, dwStyle & ~WS_BORDER);
-      return TRUE;
-
-    case WM_CREATE:
-      infoPtr = (UPDOWN_INFO*)COMCTL32_Alloc (sizeof(UPDOWN_INFO));
-      SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
-
-      /* initialize the info struct */
-      infoPtr->AccelCount=0; infoPtr->AccelVect=0; 
-      infoPtr->CurVal=0; infoPtr->MinVal=0; infoPtr->MaxVal=9999;
-      infoPtr->Base  = 10; /* Default to base 10  */
-      infoPtr->Buddy = 0;  /* No buddy window yet */
-      infoPtr->Flags = 0;  /* And no flags        */
-
-      /* Do we pick the buddy win ourselves? */
-      if (dwStyle & UDS_AUTOBUDDY)
-       UPDOWN_SetBuddy (hwnd, GetWindow (hwnd, GW_HWNDPREV));
-       
-      TRACE("UpDown Ctrl creation, hwnd=%04x\n", hwnd);
-      break;
-    
-    case WM_DESTROY:
-      if(infoPtr->AccelVect)
-       COMCTL32_Free (infoPtr->AccelVect);
-
-      if ( IsWindow(infoPtr->Buddy) ) /* Cleanup */
-        RemovePropA(infoPtr->Buddy, BUDDY_UPDOWN_HWND);
-
-      COMCTL32_Free (infoPtr);
-      SetWindowLongA (hwnd, 0, 0);
-      TRACE("UpDown Ctrl destruction, hwnd=%04x\n", hwnd);
-      break;
-       
-    case WM_ENABLE:
-      if (dwStyle & WS_DISABLED)
-       UPDOWN_CancelMode (hwnd);
-
-      UPDOWN_Refresh (hwnd);
-      break;
-
-    case WM_TIMER:
-      /* if initial timer, kill it and start the repeat timer */
-      if(wParam == TIMERID1){
-       KillTimer(hwnd, TIMERID1);
-       /* if no accel info given, used default timer */
-       if(infoPtr->AccelCount==0 || infoPtr->AccelVect==0){
-         accelIndex = -1;
-         temp = REPEAT_DELAY;
-       }
-       else{
-         accelIndex = 0; /* otherwise, use it */
-         temp = infoPtr->AccelVect[accelIndex].nSec * 1000 + 1;
-       }
-       SetTimer(hwnd, TIMERID2, temp, 0);
-      }
-
-      /* now, if the mouse is above us, do the thing...*/
-      if(infoPtr->Flags & FLAG_MOUSEIN){
-       temp = accelIndex==-1 ? 1 : infoPtr->AccelVect[accelIndex].nInc;
-       UPDOWN_DoAction(hwnd, temp, infoPtr->Flags & FLAG_INCR);
-       
-       if(accelIndex!=-1 && accelIndex < infoPtr->AccelCount-1){
-         KillTimer(hwnd, TIMERID2);
-         accelIndex++; /* move to the next accel info */
-         temp = infoPtr->AccelVect[accelIndex].nSec * 1000 + 1;
-         /* make sure we have at least 1ms intervals */
-         SetTimer(hwnd, TIMERID2, temp, 0);        
-       }
-      }
-      break;
-
-    case WM_CANCELMODE:
-      UPDOWN_CancelMode (hwnd);
-      break;
-
-    case WM_LBUTTONUP:
-      if(!UPDOWN_CancelMode(hwnd))
-       break;
-
-      SendMessageA(GetParent(hwnd), dwStyle & UDS_HORZ ? WM_HSCROLL : WM_VSCROLL,
-                  MAKELONG(SB_ENDSCROLL, infoPtr->CurVal), hwnd);
-
-      /*If we released the mouse and our buddy is an edit */
-      /* we must select all text in it.                   */
-      if (!lstrcmpA (infoPtr->szBuddyClass, "Edit"))
-         SendMessageA(infoPtr->Buddy, EM_SETSEL, 0, MAKELONG(0, -1));
-      break;
-      
-    case WM_LBUTTONDOWN:
-    case WM_MOUSEMOVE:
-      if(UPDOWN_IsEnabled(hwnd)){
-       POINT pt;
-        pt.x = SLOWORD(lParam);
-        pt.y = SHIWORD(lParam);
-       UPDOWN_HandleMouseEvent (hwnd, message, pt );
-      }
-    break;
-
-    case WM_KEYDOWN:
-      if((dwStyle & UDS_ARROWKEYS) && UPDOWN_IsEnabled(hwnd)){
-       switch(wParam){
-       case VK_UP:  
-       case VK_DOWN:
-         UPDOWN_GetBuddyInt (hwnd);
-         /* FIXME: Paint the according button pressed for some time, like win95 does*/
-         UPDOWN_DoAction (hwnd, 1, wParam==VK_UP);
-         break;
+        case WM_CREATE:
+            infoPtr = (UPDOWN_INFO*)Alloc (sizeof(UPDOWN_INFO));
+           SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
+
+           /* initialize the info struct */
+           infoPtr->Self = hwnd;
+           infoPtr->Notify = ((LPCREATESTRUCTW)lParam)->hwndParent;
+            infoPtr->dwStyle = ((LPCREATESTRUCTW)lParam)->style;
+           infoPtr->AccelCount = 0;
+           infoPtr->AccelVect = 0;
+           infoPtr->AccelIndex = -1;
+           infoPtr->CurVal = 0;
+           infoPtr->MinVal = 100;
+           infoPtr->MaxVal = 0;
+           infoPtr->Base  = 10; /* Default to base 10  */
+           infoPtr->Buddy = 0;  /* No buddy window yet */
+           infoPtr->Flags = 0;  /* And no flags        */
+
+            SetWindowLongW (hwnd, GWL_STYLE, infoPtr->dwStyle & ~WS_BORDER);
+
+            /* Do we pick the buddy win ourselves? */
+           if (infoPtr->dwStyle & UDS_AUTOBUDDY)
+               UPDOWN_SetBuddy (infoPtr, GetWindow (hwnd, GW_HWNDPREV));
+
+            OpenThemeData (hwnd, themeClass);
+
+           TRACE("UpDown Ctrl creation, hwnd=%p\n", hwnd);
+           break;
+
+       case WM_DESTROY:
+           Free (infoPtr->AccelVect);
+
+           if(infoPtr->Buddy) RemovePropW(infoPtr->Buddy, BUDDY_UPDOWN_HWND);
+
+           Free (infoPtr);
+           SetWindowLongPtrW (hwnd, 0, 0);
+            theme = GetWindowTheme (hwnd);
+            CloseThemeData (theme);
+           TRACE("UpDown Ctrl destruction, hwnd=%p\n", hwnd);
+           break;
+
+       case WM_ENABLE:
+           if (wParam) {
+               infoPtr->dwStyle &= ~WS_DISABLED;
+           } else {
+               infoPtr->dwStyle |= WS_DISABLED;
+               UPDOWN_CancelMode (infoPtr);
+           }
+           InvalidateRect (infoPtr->Self, NULL, FALSE);
+           break;
+
+        case WM_STYLECHANGED:
+            if (wParam == GWL_STYLE) {
+                infoPtr->dwStyle = ((LPSTYLESTRUCT)lParam)->styleNew;
+               InvalidateRect (infoPtr->Self, NULL, FALSE);
+            }
+            break;
+
+        case WM_THEMECHANGED:
+            theme = GetWindowTheme (hwnd);
+            CloseThemeData (theme);
+            OpenThemeData (hwnd, themeClass);
+            InvalidateRect (hwnd, NULL, FALSE);
+            break;
+
+       case WM_TIMER:
+          /* is this the auto-press timer? */
+          if(wParam == TIMER_AUTOPRESS) {
+               KillTimer(hwnd, TIMER_AUTOPRESS);
+               infoPtr->Flags &= ~(FLAG_PRESSED | FLAG_ARROW);
+               InvalidateRect(infoPtr->Self, NULL, FALSE);
+          }
+
+          /* if initial timer, kill it and start the repeat timer */
+          if(wParam == TIMER_AUTOREPEAT) {
+               int temp;
+
+               KillTimer(hwnd, TIMER_AUTOREPEAT);
+               /* if no accel info given, used default timer */
+               if(infoPtr->AccelCount==0 || infoPtr->AccelVect==0) {
+                   infoPtr->AccelIndex = -1;
+                   temp = REPEAT_DELAY;
+               } else {
+                   infoPtr->AccelIndex = 0; /* otherwise, use it */
+                   temp = infoPtr->AccelVect[infoPtr->AccelIndex].nSec * 1000 + 1;
+               }
+               SetTimer(hwnd, TIMER_ACCEL, temp, 0);
+           }
+
+           /* now, if the mouse is above us, do the thing...*/
+           if(infoPtr->Flags & FLAG_MOUSEIN) {
+               int temp;
+
+               temp = infoPtr->AccelIndex == -1 ? 1 : infoPtr->AccelVect[infoPtr->AccelIndex].nInc;
+               UPDOWN_DoAction(infoPtr, temp, infoPtr->Flags & FLAG_ARROW);
+
+               if(infoPtr->AccelIndex != -1 && infoPtr->AccelIndex < infoPtr->AccelCount-1) {
+                   KillTimer(hwnd, TIMER_ACCEL);
+                   infoPtr->AccelIndex++; /* move to the next accel info */
+                   temp = infoPtr->AccelVect[infoPtr->AccelIndex].nSec * 1000 + 1;
+                   /* make sure we have at least 1ms intervals */
+                   SetTimer(hwnd, TIMER_ACCEL, temp, 0);
+               }
+           }
+           break;
+
+       case WM_CANCELMODE:
+         return UPDOWN_CancelMode (infoPtr);
+
+       case WM_LBUTTONUP:
+           if (GetCapture() != infoPtr->Self) break;
+
+           if ( (infoPtr->Flags & FLAG_MOUSEIN) &&
+                (infoPtr->Flags & FLAG_ARROW) ) {
+
+               SendMessageW( infoPtr->Notify,
+                             (infoPtr->dwStyle & UDS_HORZ) ? WM_HSCROLL : WM_VSCROLL,
+                             MAKELONG(SB_ENDSCROLL, infoPtr->CurVal),
+                             (LPARAM)hwnd);
+               if (UPDOWN_IsBuddyEdit(infoPtr))
+                   SendMessageW(infoPtr->Buddy, EM_SETSEL, 0, MAKELONG(0, -1));
+           }
+           UPDOWN_CancelMode(infoPtr);
+           break;
+
+       case WM_LBUTTONDOWN:
+       case WM_MOUSEMOVE:
+        case WM_MOUSELEAVE:
+           if(UPDOWN_IsEnabled(infoPtr))
+               UPDOWN_HandleMouseEvent (infoPtr, message, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
+           break;
+
+        case WM_MOUSEWHEEL:
+            UPDOWN_MouseWheel(infoPtr, wParam);
+            break;
+
+       case WM_KEYDOWN:
+           if((infoPtr->dwStyle & UDS_ARROWKEYS) && UPDOWN_IsEnabled(infoPtr))
+               return UPDOWN_KeyPressed(infoPtr, (int)wParam);
+           break;
+
+       case WM_PRINTCLIENT:
+       case WM_PAINT:
+           return UPDOWN_Paint (infoPtr, (HDC)wParam);
+
+       case UDM_GETACCEL:
+           if (wParam==0 && lParam==0) return infoPtr->AccelCount;
+           if (wParam && lParam) {
+               int temp = min(infoPtr->AccelCount, wParam);
+               memcpy((void *)lParam, infoPtr->AccelVect, temp*sizeof(UDACCEL));
+               return temp;
+           }
+           return 0;
+
+       case UDM_SETACCEL:
+       {
+           unsigned temp;
+
+           TRACE("UDM_SETACCEL\n");
+
+           if(infoPtr->AccelVect) {
+               Free (infoPtr->AccelVect);
+               infoPtr->AccelCount = 0;
+               infoPtr->AccelVect  = 0;
+           }
+           if(wParam==0) return TRUE;
+           infoPtr->AccelVect = Alloc (wParam*sizeof(UDACCEL));
+           if(infoPtr->AccelVect == 0) return FALSE;
+           memcpy(infoPtr->AccelVect, (void*)lParam, wParam*sizeof(UDACCEL));
+            infoPtr->AccelCount = wParam;
+
+            for (temp = 0; temp < wParam; temp++)
+                TRACE("%d: nSec %u nInc %u\n", temp, infoPtr->AccelVect[temp].nSec, infoPtr->AccelVect[temp].nInc);
+
+           return TRUE;
        }
-      }
-      break;
-      
-    case WM_PAINT:
-      UPDOWN_Paint (hwnd, (HDC)wParam);
-      break;
-    
-    case UDM_GETACCEL:
-      if (wParam==0 && lParam==0)    /*if both zero, */
-       return infoPtr->AccelCount;  /*just return the accel count*/
-      if (wParam || lParam){
-       UNKNOWN_PARAM(UDM_GETACCEL, wParam, lParam);
-       return 0;
-      }
-      temp = min(infoPtr->AccelCount, wParam);
-      memcpy((void *)lParam, infoPtr->AccelVect, temp*sizeof(UDACCEL));
-      return temp;
-
-    case UDM_SETACCEL:
-      TRACE("UpDown Ctrl new accel info, hwnd=%04x\n", hwnd);
-      if(infoPtr->AccelVect){
-       COMCTL32_Free (infoPtr->AccelVect);
-       infoPtr->AccelCount = 0;
-       infoPtr->AccelVect  = 0;
-      }
-      if(wParam==0)
-       return TRUE;
-      infoPtr->AccelVect = COMCTL32_Alloc (wParam*sizeof(UDACCEL));
-      if(infoPtr->AccelVect==0)
-       return FALSE;
-      memcpy(infoPtr->AccelVect, (void*)lParam, wParam*sizeof(UDACCEL));
-      return TRUE;
-
-    case UDM_GETBASE:
-      if (wParam || lParam)
-       UNKNOWN_PARAM(UDM_GETBASE, wParam, lParam);
-      return infoPtr->Base;
-
-    case UDM_SETBASE:
-      TRACE("UpDown Ctrl new base(%d), hwnd=%04x\n", 
-                    wParam, hwnd);
-      if ( !(wParam==10 || wParam==16) || lParam)
-       UNKNOWN_PARAM(UDM_SETBASE, wParam, lParam);
-      if (wParam==10 || wParam==16){
-       temp = infoPtr->Base;
-       infoPtr->Base = wParam;
-       return temp;       /* return the prev base */
-      }
-      break;
-
-    case UDM_GETBUDDY:
-      if (wParam || lParam)
-       UNKNOWN_PARAM(UDM_GETBUDDY, wParam, lParam);
-      return infoPtr->Buddy;
-
-    case UDM_SETBUDDY:
-      if (lParam)
-       UNKNOWN_PARAM(UDM_SETBUDDY, wParam, lParam);
-      temp = infoPtr->Buddy;
-      UPDOWN_SetBuddy (hwnd, wParam);
-      TRACE("UpDown Ctrl new buddy(%04x), hwnd=%04x\n", 
-                    infoPtr->Buddy, hwnd);
-      return temp;
-
-    case UDM_GETPOS:
-      if (wParam || lParam)
-       UNKNOWN_PARAM(UDM_GETPOS, wParam, lParam);
-      temp = UPDOWN_GetBuddyInt (hwnd);
-      return MAKELONG(infoPtr->CurVal, temp ? 0 : 1);
-
-    case UDM_SETPOS:
-      if (wParam || HIWORD(lParam))
-       UNKNOWN_PARAM(UDM_GETPOS, wParam, lParam);
-      temp = SLOWORD(lParam);
-      TRACE("UpDown Ctrl new value(%d), hwnd=%04x\n",
-                    temp, hwnd);
-      if(!UPDOWN_InBounds(hwnd, temp)){
-       if(temp < infoPtr->MinVal)  
-         temp = infoPtr->MinVal;
-       if(temp > infoPtr->MaxVal)
-         temp = infoPtr->MaxVal;
-      }
-      wParam = infoPtr->CurVal; /* save prev value   */
-      infoPtr->CurVal = temp;   /* set the new value */
-      if(dwStyle & UDS_SETBUDDYINT)
-       UPDOWN_SetBuddyInt (hwnd);
-      return wParam;            /* return prev value */
-      
-    case UDM_GETRANGE:
-      if (wParam || lParam)
-       UNKNOWN_PARAM(UDM_GETRANGE, wParam, lParam);
-      return MAKELONG(infoPtr->MaxVal, infoPtr->MinVal);
-
-    case UDM_SETRANGE:
-      if (wParam)
-       UNKNOWN_PARAM(UDM_SETRANGE, wParam, lParam); /* we must have:     */
-      infoPtr->MaxVal = SLOWORD(lParam); /* UD_MINVAL <= Max <= UD_MAXVAL */
-      infoPtr->MinVal = SHIWORD(lParam); /* UD_MINVAL <= Min <= UD_MAXVAL */
-                                         /* |Max-Min| <= UD_MAXVAL        */
-      TRACE("UpDown Ctrl new range(%d to %d), hwnd=%04x\n", 
-                    infoPtr->MinVal, infoPtr->MaxVal, hwnd);
-      break;                             
-
-    case UDM_GETRANGE32:
-      if (wParam)
-       *(LPINT)wParam = infoPtr->MinVal;
-      if (lParam)
-       *(LPINT)lParam = infoPtr->MaxVal;
-      break;
-
-    case UDM_SETRANGE32:
-      infoPtr->MinVal = (INT)wParam;
-      infoPtr->MaxVal = (INT)lParam;
-      if (infoPtr->MaxVal <= infoPtr->MinVal)
-       infoPtr->MaxVal = infoPtr->MinVal + 1;
-      TRACE("UpDown Ctrl new range(%d to %d), hwnd=%04x\n", 
-                    infoPtr->MinVal, infoPtr->MaxVal, hwnd);
-      break;
-
-    case UDM_GETPOS32:
-      if ((LPBOOL)lParam != NULL)
-       *((LPBOOL)lParam) = TRUE;
-      return infoPtr->CurVal;
-
-    case UDM_SETPOS32:
-      if(!UPDOWN_InBounds(hwnd, (int)lParam)){
-       if((int)lParam < infoPtr->MinVal)
-         lParam = infoPtr->MinVal;
-       if((int)lParam > infoPtr->MaxVal)
-         lParam = infoPtr->MaxVal;
-      }
-      temp = infoPtr->CurVal; /* save prev value   */
-      infoPtr->CurVal = (int)lParam;   /* set the new value */
-      if(dwStyle & UDS_SETBUDDYINT)
-       UPDOWN_SetBuddyInt (hwnd);
-      return temp;            /* return prev value */
-
-    default: 
-      if (message >= WM_USER) 
-       ERR("unknown msg %04x wp=%04x lp=%08lx\n", 
-            message, wParam, lParam);
-      return DefWindowProcA (hwnd, message, wParam, lParam); 
-    } 
+       case UDM_GETBASE:
+           return infoPtr->Base;
 
-    return 0;
-}
+       case UDM_SETBASE:
+           TRACE("UpDown Ctrl new base(%ld), hwnd=%p\n", wParam, hwnd);
+           if (wParam==10 || wParam==16) {
+               WPARAM temp = infoPtr->Base;
+               infoPtr->Base = wParam;
+               return temp;
+           }
+           break;
 
-/***********************************************************************
- * UPDOWN_Buddy_SubclassProc used to handle messages sent to the buddy 
- *                           control.
- */
-LRESULT CALLBACK
-UPDOWN_Buddy_SubclassProc (
-  HWND   hwnd, 
-  UINT   uMsg, 
-  WPARAM wParam, 
-  LPARAM lParam)
-{
-  WNDPROC superClassWndProc = (WNDPROC)GetPropA(hwnd, BUDDY_SUPERCLASS_WNDPROC);
-  TRACE("hwnd=%04x, wndProc=%d, uMsg=%04x, wParam=%d, lParam=%d\n", 
-        hwnd, (INT)superClassWndProc, uMsg, wParam, (UINT)lParam);
+       case UDM_GETBUDDY:
+           return (LRESULT)infoPtr->Buddy;
 
-  switch (uMsg) 
-  {
-    case WM_KEYDOWN:
-    {
-      if ( ((int)wParam == VK_UP ) || ((int)wParam == VK_DOWN ) )
-      {
-        HWND upDownHwnd      = GetPropA(hwnd, BUDDY_UPDOWN_HWND);
-        UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(upDownHwnd);
-      
-        if (!lstrcmpA (infoPtr->szBuddyClass, "ListBox"))
-        {
-          /* if the buddy is a list window, we must update curr index */
-          INT oldVal = SendMessageA(hwnd, LB_GETCURSEL, 0, 0);
-          SendMessageA(hwnd, LB_SETCURSEL, oldVal+1, 0);
-        }
-        else
-        {
-         UPDOWN_GetBuddyInt(upDownHwnd);
-          UPDOWN_DoAction(upDownHwnd, 1, wParam==VK_UP);
-        }
+       case UDM_SETBUDDY:
+           return (LRESULT)UPDOWN_SetBuddy (infoPtr, (HWND)wParam);
+
+       case UDM_GETPOS:
+       {
+           int temp = UPDOWN_GetBuddyInt (infoPtr);
+           return MAKELONG(infoPtr->CurVal, temp ? 0 : 1);
+       }
+       case UDM_SETPOS:
+       {
+           int temp = (short)LOWORD(lParam);
+
+           TRACE("UpDown Ctrl new value(%d), hwnd=%p\n", temp, hwnd);
+           if(!UPDOWN_InBounds(infoPtr, temp)) {
+               if(temp < infoPtr->MinVal) temp = infoPtr->MinVal;
+               if(temp > infoPtr->MaxVal) temp = infoPtr->MaxVal;
+           }
+           wParam = infoPtr->CurVal;
+           infoPtr->CurVal = temp;
+           UPDOWN_SetBuddyInt (infoPtr);
+           return wParam;            /* return prev value */
+       }
+       case UDM_GETRANGE:
+           return MAKELONG(infoPtr->MaxVal, infoPtr->MinVal);
+
+       case UDM_SETRANGE:
+                                                     /* we must have:     */
+           infoPtr->MaxVal = (short)(lParam);       /* UD_MINVAL <= Max <= UD_MAXVAL */
+           infoPtr->MinVal = (short)HIWORD(lParam); /* UD_MINVAL <= Min <= UD_MAXVAL */
+                                                     /* |Max-Min| <= UD_MAXVAL        */
+           TRACE("UpDown Ctrl new range(%d to %d), hwnd=%p\n",
+                 infoPtr->MinVal, infoPtr->MaxVal, hwnd);
+           break;
+
+       case UDM_GETRANGE32:
+           if (wParam) *(LPINT)wParam = infoPtr->MinVal;
+           if (lParam) *(LPINT)lParam = infoPtr->MaxVal;
+           break;
+
+       case UDM_SETRANGE32:
+           infoPtr->MinVal = (INT)wParam;
+           infoPtr->MaxVal = (INT)lParam;
+           if (infoPtr->MaxVal <= infoPtr->MinVal)
+               infoPtr->MaxVal = infoPtr->MinVal + 1;
+           TRACE("UpDown Ctrl new range(%d to %d), hwnd=%p\n",
+                 infoPtr->MinVal, infoPtr->MaxVal, hwnd);
+           break;
+
+       case UDM_GETPOS32:
+           if ((LPBOOL)lParam != NULL) *((LPBOOL)lParam) = TRUE;
+           return infoPtr->CurVal;
+
+       case UDM_SETPOS32:
+       {
+           int temp;
+
+           if(!UPDOWN_InBounds(infoPtr, (int)lParam)) {
+               if((int)lParam < infoPtr->MinVal) lParam = infoPtr->MinVal;
+               if((int)lParam > infoPtr->MaxVal) lParam = infoPtr->MaxVal;
+           }
+           temp = infoPtr->CurVal;         /* save prev value   */
+           infoPtr->CurVal = (int)lParam;  /* set the new value */
+           UPDOWN_SetBuddyInt (infoPtr);
+           return temp;                    /* return prev value */
+       }
+       case UDM_GETUNICODEFORMAT:
+           /* we lie a bit here, we're always using Unicode internally */
+           return infoPtr->UnicodeFormat;
 
-        break;
-      }
-      /* else Fall Through */
+       case UDM_SETUNICODEFORMAT:
+       {
+           /* do we really need to honour this flag? */
+           int temp = infoPtr->UnicodeFormat;
+           infoPtr->UnicodeFormat = (BOOL)wParam;
+           return temp;
+       }
+       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);
     }
-  }
-  return CallWindowProcA( superClassWndProc, hwnd, uMsg, wParam, lParam);
+
+    return 0;
 }
 
 /***********************************************************************
@@ -1015,22 +1108,20 @@ UPDOWN_Buddy_SubclassProc (
  *
  * Registers the updown window class.
  */
-
-VOID
-UPDOWN_Register(void)
+void UPDOWN_Register(void)
 {
-    WNDCLASSA wndClass;
+    WNDCLASSW wndClass;
 
-    ZeroMemory( &wndClass, sizeof( WNDCLASSA ) );
-    wndClass.style         = CS_GLOBALCLASS | CS_VREDRAW;
-    wndClass.lpfnWndProc   = (WNDPROC)UpDownWindowProc;
+    ZeroMemory( &wndClass, sizeof( WNDCLASSW ) );
+    wndClass.style         = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
+    wndClass.lpfnWndProc   = UpDownWindowProc;
     wndClass.cbClsExtra    = 0;
     wndClass.cbWndExtra    = sizeof(UPDOWN_INFO*);
-    wndClass.hCursor       = LoadCursorA( 0, IDC_ARROWA );
-    wndClass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
-    wndClass.lpszClassName = UPDOWN_CLASSA;
-    RegisterClassA( &wndClass );
+    wndClass.hCursor       = LoadCursorW( 0, (LPWSTR)IDC_ARROW );
+    wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
+    wndClass.lpszClassName = UPDOWN_CLASSW;
+
+    RegisterClassW( &wndClass );
 }
 
 
@@ -1039,10 +1130,7 @@ UPDOWN_Register(void)
  *
  * Unregisters the updown window class.
  */
-
-VOID
-UPDOWN_Unregister (void)
+void UPDOWN_Unregister (void)
 {
-    UnregisterClassA (UPDOWN_CLASSA, (HINSTANCE)NULL);
+    UnregisterClassW (UPDOWN_CLASSW, NULL);
 }
-