xref: /reactos/dll/win32/comctl32/updown.c (revision 0707475f)
1c2c66affSColin Finck /*
2c2c66affSColin Finck  * Updown control
3c2c66affSColin Finck  *
4c2c66affSColin Finck  * Copyright 1997, 2002 Dimitrie O. Paun
5c2c66affSColin Finck  *
6c2c66affSColin Finck  * This library is free software; you can redistribute it and/or
7c2c66affSColin Finck  * modify it under the terms of the GNU Lesser General Public
8c2c66affSColin Finck  * License as published by the Free Software Foundation; either
9c2c66affSColin Finck  * version 2.1 of the License, or (at your option) any later version.
10c2c66affSColin Finck  *
11c2c66affSColin Finck  * This library is distributed in the hope that it will be useful,
12c2c66affSColin Finck  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13c2c66affSColin Finck  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14c2c66affSColin Finck  * Lesser General Public License for more details.
15c2c66affSColin Finck  *
16c2c66affSColin Finck  * You should have received a copy of the GNU Lesser General Public
17c2c66affSColin Finck  * License along with this library; if not, write to the Free Software
18c2c66affSColin Finck  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19c2c66affSColin Finck  */
20c2c66affSColin Finck 
21*0707475fSJustin Miller #include <assert.h>
22b3fb8555SGiannis Adamopoulos #include <stdlib.h>
23b3fb8555SGiannis Adamopoulos #include <string.h>
24b3fb8555SGiannis Adamopoulos #include <stdarg.h>
25b3fb8555SGiannis Adamopoulos #include <stdio.h>
26b3fb8555SGiannis Adamopoulos 
27b3fb8555SGiannis Adamopoulos #include "windef.h"
28b3fb8555SGiannis Adamopoulos #include "winbase.h"
29b3fb8555SGiannis Adamopoulos #include "wingdi.h"
30b3fb8555SGiannis Adamopoulos #include "winuser.h"
31b3fb8555SGiannis Adamopoulos #include "winnls.h"
32b3fb8555SGiannis Adamopoulos #include "commctrl.h"
33c2c66affSColin Finck #include "comctl32.h"
34b3fb8555SGiannis Adamopoulos #include "uxtheme.h"
35b3fb8555SGiannis Adamopoulos #include "vssym32.h"
36b3fb8555SGiannis Adamopoulos #include "wine/heap.h"
37b3fb8555SGiannis Adamopoulos #include "wine/debug.h"
38c2c66affSColin Finck 
39c2c66affSColin Finck WINE_DEFAULT_DEBUG_CHANNEL(updown);
40c2c66affSColin Finck 
41c2c66affSColin Finck typedef struct
42c2c66affSColin Finck {
43c2c66affSColin Finck     HWND      Self;            /* Handle to this up-down control */
44c2c66affSColin Finck     HWND      Notify;          /* Handle to the parent window */
45c2c66affSColin Finck     DWORD     dwStyle;         /* The GWL_STYLE for this window */
46c2c66affSColin Finck     UINT      AccelCount;      /* Number of elements in AccelVect */
47c2c66affSColin Finck     UDACCEL*  AccelVect;       /* Vector containing AccelCount elements */
48c2c66affSColin Finck     INT       AccelIndex;      /* Current accel index, -1 if not accel'ing */
49c2c66affSColin Finck     INT       Base;            /* Base to display nr in the buddy window */
50c2c66affSColin Finck     INT       CurVal;          /* Current up-down value */
51c2c66affSColin Finck     INT       MinVal;          /* Minimum up-down value */
52c2c66affSColin Finck     INT       MaxVal;          /* Maximum up-down value */
53c2c66affSColin Finck     HWND      Buddy;           /* Handle to the buddy window */
54c2c66affSColin Finck     INT       BuddyType;       /* Remembers the buddy type BUDDY_TYPE_* */
55c2c66affSColin Finck     INT       Flags;           /* Internal Flags FLAG_* */
56c2c66affSColin Finck     BOOL      UnicodeFormat;   /* Marks the use of Unicode internally */
57c2c66affSColin Finck } UPDOWN_INFO;
58c2c66affSColin Finck 
59c2c66affSColin Finck /* Control configuration constants */
60c2c66affSColin Finck 
61c2c66affSColin Finck #define INITIAL_DELAY	500    /* initial timer until auto-inc kicks in */
62c2c66affSColin Finck #define AUTOPRESS_DELAY	250    /* time to keep arrow pressed on KEY_DOWN */
63c2c66affSColin Finck #define REPEAT_DELAY	50     /* delay between auto-increments */
64c2c66affSColin Finck 
65c2c66affSColin Finck #define DEFAULT_WIDTH	    16 /* default width of the ctrl */
66c2c66affSColin Finck #define DEFAULT_XSEP         0 /* default separation between buddy and ctrl */
67c2c66affSColin Finck #define DEFAULT_ADDTOP       0 /* amount to extend above the buddy window */
68c2c66affSColin Finck #define DEFAULT_ADDBOT       0 /* amount to extend below the buddy window */
69c2c66affSColin Finck #define DEFAULT_BUDDYBORDER  2 /* Width/height of the buddy border */
70c2c66affSColin Finck #define DEFAULT_BUDDYSPACER  2 /* Spacer between the buddy and the ctrl */
71c2c66affSColin Finck #define DEFAULT_BUDDYBORDER_THEMED  1 /* buddy border when theming is enabled */
72c2c66affSColin Finck #define DEFAULT_BUDDYSPACER_THEMED  0 /* buddy spacer when theming is enabled */
73c2c66affSColin Finck 
74c2c66affSColin Finck /* Work constants */
75c2c66affSColin Finck 
76c2c66affSColin Finck #define FLAG_INCR	0x01
77c2c66affSColin Finck #define FLAG_DECR	0x02
78c2c66affSColin Finck #define FLAG_MOUSEIN	0x04
79c2c66affSColin Finck #define FLAG_PRESSED	0x08
80c2c66affSColin Finck #define FLAG_BUDDYINT	0x10 /* UDS_SETBUDDYINT was set on creation */
81c2c66affSColin Finck #define FLAG_ARROW	(FLAG_INCR | FLAG_DECR)
82c2c66affSColin Finck 
83c2c66affSColin Finck #define BUDDY_TYPE_UNKNOWN 0
84c2c66affSColin Finck #define BUDDY_TYPE_LISTBOX 1
85c2c66affSColin Finck #define BUDDY_TYPE_EDIT    2
86c2c66affSColin Finck 
87c2c66affSColin Finck #define TIMER_AUTOREPEAT   1
88c2c66affSColin Finck #define TIMER_ACCEL        2
89c2c66affSColin Finck #define TIMER_AUTOPRESS    3
90c2c66affSColin Finck 
91c2c66affSColin Finck #define UPDOWN_GetInfoPtr(hwnd) ((UPDOWN_INFO *)GetWindowLongPtrW (hwnd,0))
92c2c66affSColin Finck 
93c2c66affSColin Finck /* id used for SetWindowSubclass */
94c2c66affSColin Finck #define BUDDY_SUBCLASSID   1
95c2c66affSColin Finck 
96c2c66affSColin Finck static void UPDOWN_DoAction (UPDOWN_INFO *infoPtr, int delta, int action);
97c2c66affSColin Finck 
98c2c66affSColin Finck /***********************************************************************
99c2c66affSColin Finck  *           UPDOWN_IsBuddyEdit
100c2c66affSColin Finck  * Tests if our buddy is an edit control.
101c2c66affSColin Finck  */
UPDOWN_IsBuddyEdit(const UPDOWN_INFO * infoPtr)102c2c66affSColin Finck static inline BOOL UPDOWN_IsBuddyEdit(const UPDOWN_INFO *infoPtr)
103c2c66affSColin Finck {
104c2c66affSColin Finck     return infoPtr->BuddyType == BUDDY_TYPE_EDIT;
105c2c66affSColin Finck }
106c2c66affSColin Finck 
107c2c66affSColin Finck /***********************************************************************
108c2c66affSColin Finck  *           UPDOWN_IsBuddyListbox
109c2c66affSColin Finck  * Tests if our buddy is a listbox control.
110c2c66affSColin Finck  */
UPDOWN_IsBuddyListbox(const UPDOWN_INFO * infoPtr)111c2c66affSColin Finck static inline BOOL UPDOWN_IsBuddyListbox(const UPDOWN_INFO *infoPtr)
112c2c66affSColin Finck {
113c2c66affSColin Finck     return infoPtr->BuddyType == BUDDY_TYPE_LISTBOX;
114c2c66affSColin Finck }
115c2c66affSColin Finck 
116c2c66affSColin Finck /***********************************************************************
117c2c66affSColin Finck  *           UPDOWN_InBounds
118c2c66affSColin Finck  * Tests if a given value 'val' is between the Min&Max limits
119c2c66affSColin Finck  */
UPDOWN_InBounds(const UPDOWN_INFO * infoPtr,int val)120c2c66affSColin Finck static BOOL UPDOWN_InBounds(const UPDOWN_INFO *infoPtr, int val)
121c2c66affSColin Finck {
122c2c66affSColin Finck     if(infoPtr->MaxVal > infoPtr->MinVal)
123c2c66affSColin Finck         return (infoPtr->MinVal <= val) && (val <= infoPtr->MaxVal);
124c2c66affSColin Finck     else
125c2c66affSColin Finck         return (infoPtr->MaxVal <= val) && (val <= infoPtr->MinVal);
126c2c66affSColin Finck }
127c2c66affSColin Finck 
128c2c66affSColin Finck /***********************************************************************
129c2c66affSColin Finck  *           UPDOWN_OffsetVal
130c2c66affSColin Finck  * Change the current value by delta.
131c2c66affSColin Finck  * It returns TRUE is the value was changed successfully, or FALSE
132c2c66affSColin Finck  * if the value was not changed, as it would go out of bounds.
133c2c66affSColin Finck  */
UPDOWN_OffsetVal(UPDOWN_INFO * infoPtr,int delta)134c2c66affSColin Finck static BOOL UPDOWN_OffsetVal(UPDOWN_INFO *infoPtr, int delta)
135c2c66affSColin Finck {
136c2c66affSColin Finck     /* check if we can do the modification first */
137c2c66affSColin Finck     if(!UPDOWN_InBounds (infoPtr, infoPtr->CurVal+delta)) {
138c2c66affSColin Finck         if (infoPtr->dwStyle & UDS_WRAP) {
139c2c66affSColin Finck             delta += (delta < 0 ? -1 : 1) *
140c2c66affSColin Finck 		     (infoPtr->MaxVal < infoPtr->MinVal ? -1 : 1) *
141c2c66affSColin Finck 		     (infoPtr->MinVal - infoPtr->MaxVal) +
142c2c66affSColin Finck 		     (delta < 0 ? 1 : -1);
143c2c66affSColin Finck         } else if ((infoPtr->MaxVal > infoPtr->MinVal && infoPtr->CurVal+delta > infoPtr->MaxVal)
144c2c66affSColin Finck                 || (infoPtr->MaxVal < infoPtr->MinVal && infoPtr->CurVal+delta < infoPtr->MaxVal)) {
145c2c66affSColin Finck             delta = infoPtr->MaxVal - infoPtr->CurVal;
146c2c66affSColin Finck         } else {
147c2c66affSColin Finck             delta = infoPtr->MinVal - infoPtr->CurVal;
148c2c66affSColin Finck         }
149c2c66affSColin Finck     }
150c2c66affSColin Finck 
151c2c66affSColin Finck     infoPtr->CurVal += delta;
152c2c66affSColin Finck     return delta != 0;
153c2c66affSColin Finck }
154c2c66affSColin Finck 
155c2c66affSColin Finck /***********************************************************************
156c2c66affSColin Finck  * UPDOWN_HasBuddyBorder
157c2c66affSColin Finck  *
158c2c66affSColin Finck  * When we have a buddy set and that we are aligned on our buddy, we
159c2c66affSColin Finck  * want to draw a sunken edge to make like we are part of that control.
160c2c66affSColin Finck  */
UPDOWN_HasBuddyBorder(const UPDOWN_INFO * infoPtr)161c2c66affSColin Finck static BOOL UPDOWN_HasBuddyBorder(const UPDOWN_INFO *infoPtr)
162c2c66affSColin Finck {
163c2c66affSColin Finck     return  ( ((infoPtr->dwStyle & (UDS_ALIGNLEFT | UDS_ALIGNRIGHT)) != 0) &&
164c2c66affSColin Finck 	      UPDOWN_IsBuddyEdit(infoPtr) );
165c2c66affSColin Finck }
166c2c66affSColin Finck 
167c2c66affSColin Finck /***********************************************************************
168c2c66affSColin Finck  *           UPDOWN_GetArrowRect
169c2c66affSColin Finck  * wndPtr   - pointer to the up-down wnd
170c2c66affSColin Finck  * rect     - will hold the rectangle
171c2c66affSColin Finck  * arrow    - FLAG_INCR to get the "increment" rect (up or right)
172c2c66affSColin Finck  *            FLAG_DECR to get the "decrement" rect (down or left)
173c2c66affSColin Finck  */
UPDOWN_GetArrowRect(const UPDOWN_INFO * infoPtr,RECT * rect,unsigned int arrow)174*0707475fSJustin Miller static void UPDOWN_GetArrowRect (const UPDOWN_INFO* infoPtr, RECT *rect, unsigned int arrow)
175c2c66affSColin Finck {
176c2c66affSColin Finck     HTHEME theme = GetWindowTheme (infoPtr->Self);
177c2c66affSColin Finck     const int border = theme ? DEFAULT_BUDDYBORDER_THEMED : DEFAULT_BUDDYBORDER;
178c2c66affSColin Finck     const int spacer = theme ? DEFAULT_BUDDYSPACER_THEMED : DEFAULT_BUDDYSPACER;
179*0707475fSJustin Miller     int size;
180*0707475fSJustin Miller 
181*0707475fSJustin Miller     assert(arrow && (arrow & (FLAG_INCR | FLAG_DECR)) != (FLAG_INCR | FLAG_DECR));
182*0707475fSJustin Miller 
183c2c66affSColin Finck     GetClientRect (infoPtr->Self, rect);
184c2c66affSColin Finck 
185c2c66affSColin Finck     /*
186c2c66affSColin Finck      * Make sure we calculate the rectangle to fit even if we draw the
187c2c66affSColin Finck      * border.
188c2c66affSColin Finck      */
189c2c66affSColin Finck     if (UPDOWN_HasBuddyBorder(infoPtr)) {
190c2c66affSColin Finck         if (infoPtr->dwStyle & UDS_ALIGNLEFT)
191c2c66affSColin Finck             rect->left += border;
192c2c66affSColin Finck         else
193c2c66affSColin Finck             rect->right -= border;
194c2c66affSColin Finck 
195c2c66affSColin Finck         InflateRect(rect, 0, -border);
196c2c66affSColin Finck     }
197c2c66affSColin Finck 
198c2c66affSColin Finck     /* now figure out if we need a space away from the buddy */
199c2c66affSColin Finck     if (IsWindow(infoPtr->Buddy) ) {
200c2c66affSColin Finck 	if (infoPtr->dwStyle & UDS_ALIGNLEFT) rect->right -= spacer;
201c2c66affSColin Finck 	else if (infoPtr->dwStyle & UDS_ALIGNRIGHT) rect->left += spacer;
202c2c66affSColin Finck     }
203c2c66affSColin Finck 
204c2c66affSColin Finck     /*
205c2c66affSColin Finck      * We're calculating the midpoint to figure-out where the
206*0707475fSJustin Miller      * separation between the buttons will lay.
207c2c66affSColin Finck      */
208c2c66affSColin Finck     if (infoPtr->dwStyle & UDS_HORZ) {
209*0707475fSJustin Miller         size = (rect->right - rect->left) / 2;
210c2c66affSColin Finck         if (arrow & FLAG_INCR)
211*0707475fSJustin Miller             rect->left = rect->right - size;
212*0707475fSJustin Miller         else if (arrow & FLAG_DECR)
213*0707475fSJustin Miller             rect->right = rect->left + size;
214c2c66affSColin Finck     } else {
215*0707475fSJustin Miller         size = (rect->bottom - rect->top) / 2;
216c2c66affSColin Finck         if (arrow & FLAG_INCR)
217*0707475fSJustin Miller             rect->bottom = rect->top + size;
218*0707475fSJustin Miller         else if (arrow & FLAG_DECR)
219*0707475fSJustin Miller             rect->top = rect->bottom - size;
220c2c66affSColin Finck     }
221c2c66affSColin Finck }
222c2c66affSColin Finck 
223c2c66affSColin Finck /***********************************************************************
224c2c66affSColin Finck  *           UPDOWN_GetArrowFromPoint
225c2c66affSColin Finck  * Returns the rectagle (for the up or down arrow) that contains pt.
226c2c66affSColin Finck  * If it returns the up rect, it returns FLAG_INCR.
227c2c66affSColin Finck  * If it returns the down rect, it returns FLAG_DECR.
228c2c66affSColin Finck  */
UPDOWN_GetArrowFromPoint(const UPDOWN_INFO * infoPtr,RECT * rect,POINT pt)229c2c66affSColin Finck static INT UPDOWN_GetArrowFromPoint (const UPDOWN_INFO *infoPtr, RECT *rect, POINT pt)
230c2c66affSColin Finck {
231c2c66affSColin Finck     UPDOWN_GetArrowRect (infoPtr, rect, FLAG_INCR);
232c2c66affSColin Finck     if(PtInRect(rect, pt)) return FLAG_INCR;
233c2c66affSColin Finck 
234c2c66affSColin Finck     UPDOWN_GetArrowRect (infoPtr, rect, FLAG_DECR);
235c2c66affSColin Finck     if(PtInRect(rect, pt)) return FLAG_DECR;
236c2c66affSColin Finck 
237c2c66affSColin Finck     return 0;
238c2c66affSColin Finck }
239c2c66affSColin Finck 
240c2c66affSColin Finck 
241c2c66affSColin Finck /***********************************************************************
242c2c66affSColin Finck  *           UPDOWN_GetThousandSep
243c2c66affSColin Finck  * Returns the thousand sep. If an error occurs, it returns ','.
244c2c66affSColin Finck  */
UPDOWN_GetThousandSep(void)245c2c66affSColin Finck static WCHAR UPDOWN_GetThousandSep(void)
246c2c66affSColin Finck {
247c2c66affSColin Finck     WCHAR sep[2];
248c2c66affSColin Finck 
249c2c66affSColin Finck     if(GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, sep, 2) != 1)
250c2c66affSColin Finck         sep[0] = ',';
251c2c66affSColin Finck 
252c2c66affSColin Finck     return sep[0];
253c2c66affSColin Finck }
254c2c66affSColin Finck 
255c2c66affSColin Finck /***********************************************************************
256c2c66affSColin Finck  *           UPDOWN_GetBuddyInt
257c2c66affSColin Finck  * Tries to read the pos from the buddy window and if it succeeds,
258c2c66affSColin Finck  * it stores it in the control's CurVal
259c2c66affSColin Finck  * returns:
260c2c66affSColin Finck  *   TRUE  - if it read the integer from the buddy successfully
261c2c66affSColin Finck  *   FALSE - if an error occurred
262c2c66affSColin Finck  */
UPDOWN_GetBuddyInt(UPDOWN_INFO * infoPtr)263c2c66affSColin Finck static BOOL UPDOWN_GetBuddyInt (UPDOWN_INFO *infoPtr)
264c2c66affSColin Finck {
265c2c66affSColin Finck     WCHAR txt[20], sep, *src, *dst;
266c2c66affSColin Finck     int newVal;
267c2c66affSColin Finck 
268c2c66affSColin Finck     if (!((infoPtr->Flags & FLAG_BUDDYINT) && IsWindow(infoPtr->Buddy)))
269c2c66affSColin Finck         return FALSE;
270c2c66affSColin Finck 
271c2c66affSColin Finck     /*if the buddy is a list window, we must set curr index */
272c2c66affSColin Finck     if (UPDOWN_IsBuddyListbox(infoPtr)) {
273c2c66affSColin Finck         newVal = SendMessageW(infoPtr->Buddy, LB_GETCARETINDEX, 0, 0);
274c2c66affSColin Finck         if(newVal < 0) return FALSE;
275c2c66affSColin Finck     } else {
276c2c66affSColin Finck         /* we have a regular window, so will get the text */
277c2c66affSColin Finck         /* note that a zero-length string is a legitimate value for 'txt',
278c2c66affSColin Finck          * and ought to result in a successful conversion to '0'. */
279b3fb8555SGiannis Adamopoulos         if (GetWindowTextW(infoPtr->Buddy, txt, ARRAY_SIZE(txt)) < 0)
280c2c66affSColin Finck             return FALSE;
281c2c66affSColin Finck 
282c2c66affSColin Finck         sep = UPDOWN_GetThousandSep();
283c2c66affSColin Finck 
284c2c66affSColin Finck         /* now get rid of the separators */
285c2c66affSColin Finck         for(src = dst = txt; *src; src++)
286c2c66affSColin Finck             if(*src != sep) *dst++ = *src;
287c2c66affSColin Finck         *dst = 0;
288c2c66affSColin Finck 
289c2c66affSColin Finck         /* try to convert the number and validate it */
290*0707475fSJustin Miller         newVal = wcstol(txt, &src, infoPtr->Base);
291c2c66affSColin Finck         if(*src || !UPDOWN_InBounds (infoPtr, newVal)) return FALSE;
292c2c66affSColin Finck     }
293c2c66affSColin Finck 
294c2c66affSColin Finck     TRACE("new value(%d) from buddy (old=%d)\n", newVal, infoPtr->CurVal);
295c2c66affSColin Finck     infoPtr->CurVal = newVal;
296c2c66affSColin Finck     return TRUE;
297c2c66affSColin Finck }
298c2c66affSColin Finck 
299c2c66affSColin Finck 
300c2c66affSColin Finck /***********************************************************************
301c2c66affSColin Finck  *           UPDOWN_SetBuddyInt
302c2c66affSColin Finck  * Tries to set the pos to the buddy window based on current pos
303c2c66affSColin Finck  * returns:
304c2c66affSColin Finck  *   TRUE  - if it set the caption of the  buddy successfully
305c2c66affSColin Finck  *   FALSE - if an error occurred
306c2c66affSColin Finck  */
UPDOWN_SetBuddyInt(const UPDOWN_INFO * infoPtr)307c2c66affSColin Finck static BOOL UPDOWN_SetBuddyInt (const UPDOWN_INFO *infoPtr)
308c2c66affSColin Finck {
309c2c66affSColin Finck     static const WCHAR fmt_hex[] = { '0', 'x', '%', '0', '4', 'X', 0 };
310c2c66affSColin Finck     static const WCHAR fmt_dec_oct[] = { '%', 'd', '\0' };
311c2c66affSColin Finck     const WCHAR *fmt;
312c2c66affSColin Finck     WCHAR txt[20], txt_old[20] = { 0 };
313c2c66affSColin Finck     int len;
314c2c66affSColin Finck 
315c2c66affSColin Finck     if (!((infoPtr->Flags & FLAG_BUDDYINT) && IsWindow(infoPtr->Buddy)))
316c2c66affSColin Finck         return FALSE;
317c2c66affSColin Finck 
318c2c66affSColin Finck     TRACE("set new value(%d) to buddy.\n", infoPtr->CurVal);
319c2c66affSColin Finck 
320c2c66affSColin Finck     /*if the buddy is a list window, we must set curr index */
321c2c66affSColin Finck     if (UPDOWN_IsBuddyListbox(infoPtr)) {
322c2c66affSColin Finck         return SendMessageW(infoPtr->Buddy, LB_SETCURSEL, infoPtr->CurVal, 0) != LB_ERR;
323c2c66affSColin Finck     }
324c2c66affSColin Finck 
325c2c66affSColin Finck     /* Regular window, so set caption to the number */
326c2c66affSColin Finck     fmt = (infoPtr->Base == 16) ? fmt_hex : fmt_dec_oct;
327c2c66affSColin Finck     len = wsprintfW(txt, fmt, infoPtr->CurVal);
328c2c66affSColin Finck 
329c2c66affSColin Finck 
330c2c66affSColin Finck     /* Do thousands separation if necessary */
331c2c66affSColin Finck     if ((infoPtr->Base == 10) && !(infoPtr->dwStyle & UDS_NOTHOUSANDS) && (len > 3)) {
332b3fb8555SGiannis Adamopoulos         WCHAR tmp[ARRAY_SIZE(txt)], *src = tmp, *dst = txt;
333c2c66affSColin Finck         WCHAR sep = UPDOWN_GetThousandSep();
334c2c66affSColin Finck 	int start = len % 3;
335c2c66affSColin Finck 
336c2c66affSColin Finck 	memcpy(tmp, txt, sizeof(txt));
337c2c66affSColin Finck 	if (start == 0) start = 3;
338c2c66affSColin Finck 	dst += start;
339c2c66affSColin Finck 	src += start;
340c2c66affSColin Finck         for (len=0; *src; len++) {
341c2c66affSColin Finck 	    if (len % 3 == 0) *dst++ = sep;
342c2c66affSColin Finck 	    *dst++ = *src++;
343c2c66affSColin Finck         }
344c2c66affSColin Finck         *dst = 0;
345c2c66affSColin Finck     }
346c2c66affSColin Finck 
347c2c66affSColin Finck     /* if nothing changed exit earlier */
348b3fb8555SGiannis Adamopoulos     GetWindowTextW(infoPtr->Buddy, txt_old, ARRAY_SIZE(txt_old));
349c2c66affSColin Finck     if (lstrcmpiW(txt_old, txt) == 0) return FALSE;
350c2c66affSColin Finck 
351c2c66affSColin Finck     return SetWindowTextW(infoPtr->Buddy, txt);
352c2c66affSColin Finck }
353c2c66affSColin Finck 
354c2c66affSColin Finck /***********************************************************************
355c2c66affSColin Finck  * UPDOWN_DrawBuddyBackground
356c2c66affSColin Finck  *
357c2c66affSColin Finck  * Draw buddy background for visual integration.
358c2c66affSColin Finck  */
UPDOWN_DrawBuddyBackground(const UPDOWN_INFO * infoPtr,HDC hdc)359c2c66affSColin Finck static BOOL UPDOWN_DrawBuddyBackground (const UPDOWN_INFO *infoPtr, HDC hdc)
360c2c66affSColin Finck {
361c2c66affSColin Finck     RECT br, r;
362c2c66affSColin Finck     HTHEME buddyTheme = GetWindowTheme (infoPtr->Buddy);
363c2c66affSColin Finck     if (!buddyTheme) return FALSE;
364c2c66affSColin Finck 
365c2c66affSColin Finck     GetWindowRect (infoPtr->Buddy, &br);
366c2c66affSColin Finck     MapWindowPoints (NULL, infoPtr->Self, (POINT*)&br, 2);
367c2c66affSColin Finck     GetClientRect (infoPtr->Self, &r);
368c2c66affSColin Finck 
369c2c66affSColin Finck     if (infoPtr->dwStyle & UDS_ALIGNLEFT)
370c2c66affSColin Finck         br.left = r.left;
371c2c66affSColin Finck     else if (infoPtr->dwStyle & UDS_ALIGNRIGHT)
372c2c66affSColin Finck         br.right = r.right;
373c2c66affSColin Finck     /* FIXME: take disabled etc. into account */
374c2c66affSColin Finck     DrawThemeBackground (buddyTheme, hdc, 0, 0, &br, NULL);
375c2c66affSColin Finck     return TRUE;
376c2c66affSColin Finck }
377c2c66affSColin Finck 
378c2c66affSColin Finck /***********************************************************************
379c2c66affSColin Finck  * UPDOWN_Draw
380c2c66affSColin Finck  *
381c2c66affSColin Finck  * Draw the arrows. The background need not be erased.
382c2c66affSColin Finck  */
UPDOWN_Draw(const UPDOWN_INFO * infoPtr,HDC hdc)383c2c66affSColin Finck static LRESULT UPDOWN_Draw (const UPDOWN_INFO *infoPtr, HDC hdc)
384c2c66affSColin Finck {
385c2c66affSColin Finck     BOOL uPressed, uHot, dPressed, dHot;
386c2c66affSColin Finck     RECT rect;
387c2c66affSColin Finck     HTHEME theme = GetWindowTheme (infoPtr->Self);
388c2c66affSColin Finck     int uPart = 0, uState = 0, dPart = 0, dState = 0;
389c2c66affSColin Finck     BOOL needBuddyBg = FALSE;
390c2c66affSColin Finck 
391c2c66affSColin Finck     uPressed = (infoPtr->Flags & FLAG_PRESSED) && (infoPtr->Flags & FLAG_INCR);
392c2c66affSColin Finck     uHot = (infoPtr->Flags & FLAG_INCR) && (infoPtr->Flags & FLAG_MOUSEIN);
393c2c66affSColin Finck     dPressed = (infoPtr->Flags & FLAG_PRESSED) && (infoPtr->Flags & FLAG_DECR);
394c2c66affSColin Finck     dHot = (infoPtr->Flags & FLAG_DECR) && (infoPtr->Flags & FLAG_MOUSEIN);
395c2c66affSColin Finck     if (theme) {
396c2c66affSColin Finck         uPart = (infoPtr->dwStyle & UDS_HORZ) ? SPNP_UPHORZ : SPNP_UP;
397c2c66affSColin Finck         uState = (infoPtr->dwStyle & WS_DISABLED) ? DNS_DISABLED
398c2c66affSColin Finck             : (uPressed ? DNS_PRESSED : (uHot ? DNS_HOT : DNS_NORMAL));
399c2c66affSColin Finck         dPart = (infoPtr->dwStyle & UDS_HORZ) ? SPNP_DOWNHORZ : SPNP_DOWN;
400c2c66affSColin Finck         dState = (infoPtr->dwStyle & WS_DISABLED) ? DNS_DISABLED
401c2c66affSColin Finck             : (dPressed ? DNS_PRESSED : (dHot ? DNS_HOT : DNS_NORMAL));
402c2c66affSColin Finck         needBuddyBg = IsWindow (infoPtr->Buddy)
403c2c66affSColin Finck             && (IsThemeBackgroundPartiallyTransparent (theme, uPart, uState)
404c2c66affSColin Finck               || IsThemeBackgroundPartiallyTransparent (theme, dPart, dState));
405c2c66affSColin Finck     }
406c2c66affSColin Finck 
407c2c66affSColin Finck     /* Draw the common border between ourselves and our buddy */
408c2c66affSColin Finck     if (UPDOWN_HasBuddyBorder(infoPtr) || needBuddyBg) {
409c2c66affSColin Finck         if (!theme || !UPDOWN_DrawBuddyBackground (infoPtr, hdc)) {
410c2c66affSColin Finck             GetClientRect(infoPtr->Self, &rect);
411c2c66affSColin Finck 	    DrawEdge(hdc, &rect, EDGE_SUNKEN,
412c2c66affSColin Finck 		     BF_BOTTOM | BF_TOP |
413c2c66affSColin Finck 		     (infoPtr->dwStyle & UDS_ALIGNLEFT ? BF_LEFT : BF_RIGHT));
414c2c66affSColin Finck         }
415c2c66affSColin Finck     }
416c2c66affSColin Finck 
417c2c66affSColin Finck     /* Draw the incr button */
418c2c66affSColin Finck     UPDOWN_GetArrowRect (infoPtr, &rect, FLAG_INCR);
419c2c66affSColin Finck     if (theme) {
420c2c66affSColin Finck         DrawThemeBackground(theme, hdc, uPart, uState, &rect, NULL);
421c2c66affSColin Finck     } else {
422c2c66affSColin Finck         DrawFrameControl(hdc, &rect, DFC_SCROLL,
423c2c66affSColin Finck             (infoPtr->dwStyle & UDS_HORZ ? DFCS_SCROLLRIGHT : DFCS_SCROLLUP) |
424c2c66affSColin Finck             ((infoPtr->dwStyle & UDS_HOTTRACK) && uHot ? DFCS_HOT : 0) |
425c2c66affSColin Finck             (uPressed ? DFCS_PUSHED : 0) |
426c2c66affSColin Finck             (infoPtr->dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) );
427c2c66affSColin Finck     }
428c2c66affSColin Finck 
429c2c66affSColin Finck     /* Draw the decr button */
430c2c66affSColin Finck     UPDOWN_GetArrowRect(infoPtr, &rect, FLAG_DECR);
431c2c66affSColin Finck     if (theme) {
432c2c66affSColin Finck         DrawThemeBackground(theme, hdc, dPart, dState, &rect, NULL);
433c2c66affSColin Finck     } else {
434c2c66affSColin Finck         DrawFrameControl(hdc, &rect, DFC_SCROLL,
435c2c66affSColin Finck             (infoPtr->dwStyle & UDS_HORZ ? DFCS_SCROLLLEFT : DFCS_SCROLLDOWN) |
436c2c66affSColin Finck             ((infoPtr->dwStyle & UDS_HOTTRACK) && dHot ? DFCS_HOT : 0) |
437c2c66affSColin Finck             (dPressed ? DFCS_PUSHED : 0) |
438c2c66affSColin Finck             (infoPtr->dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) );
439c2c66affSColin Finck     }
440c2c66affSColin Finck 
441c2c66affSColin Finck     return 0;
442c2c66affSColin Finck }
443c2c66affSColin Finck 
444c2c66affSColin Finck /***********************************************************************
445c2c66affSColin Finck  * UPDOWN_Paint
446c2c66affSColin Finck  *
447c2c66affSColin Finck  * Asynchronous drawing (must ONLY be used in WM_PAINT).
448c2c66affSColin Finck  * Calls UPDOWN_Draw.
449c2c66affSColin Finck  */
UPDOWN_Paint(const UPDOWN_INFO * infoPtr,HDC hdc)450c2c66affSColin Finck static LRESULT UPDOWN_Paint (const UPDOWN_INFO *infoPtr, HDC hdc)
451c2c66affSColin Finck {
452c2c66affSColin Finck     PAINTSTRUCT ps;
453c2c66affSColin Finck     if (hdc) return UPDOWN_Draw (infoPtr, hdc);
454c2c66affSColin Finck     hdc = BeginPaint (infoPtr->Self, &ps);
455c2c66affSColin Finck     UPDOWN_Draw (infoPtr, hdc);
456c2c66affSColin Finck     EndPaint (infoPtr->Self, &ps);
457c2c66affSColin Finck     return 0;
458c2c66affSColin Finck }
459c2c66affSColin Finck 
460c2c66affSColin Finck /***********************************************************************
461c2c66affSColin Finck  * UPDOWN_KeyPressed
462c2c66affSColin Finck  *
463c2c66affSColin Finck  * Handle key presses (up & down) when we have to do so
464c2c66affSColin Finck  */
UPDOWN_KeyPressed(UPDOWN_INFO * infoPtr,int key)465c2c66affSColin Finck static LRESULT UPDOWN_KeyPressed(UPDOWN_INFO *infoPtr, int key)
466c2c66affSColin Finck {
467c2c66affSColin Finck     int arrow, accel;
468c2c66affSColin Finck 
469c2c66affSColin Finck     if (key == VK_UP) arrow = FLAG_INCR;
470c2c66affSColin Finck     else if (key == VK_DOWN) arrow = FLAG_DECR;
471c2c66affSColin Finck     else return 1;
472c2c66affSColin Finck 
473c2c66affSColin Finck     UPDOWN_GetBuddyInt (infoPtr);
474c2c66affSColin Finck     infoPtr->Flags &= ~FLAG_ARROW;
475c2c66affSColin Finck     infoPtr->Flags |= FLAG_PRESSED | arrow;
476c2c66affSColin Finck     InvalidateRect (infoPtr->Self, NULL, FALSE);
477c2c66affSColin Finck     SetTimer(infoPtr->Self, TIMER_AUTOPRESS, AUTOPRESS_DELAY, 0);
478c2c66affSColin Finck     accel = (infoPtr->AccelCount && infoPtr->AccelVect) ? infoPtr->AccelVect[0].nInc : 1;
479c2c66affSColin Finck     UPDOWN_DoAction (infoPtr, accel, arrow);
480c2c66affSColin Finck     return 0;
481c2c66affSColin Finck }
482c2c66affSColin Finck 
UPDOWN_GetPos(UPDOWN_INFO * infoPtr,BOOL * err)483c2c66affSColin Finck static int UPDOWN_GetPos(UPDOWN_INFO *infoPtr, BOOL *err)
484c2c66affSColin Finck {
485c2c66affSColin Finck     BOOL succ = UPDOWN_GetBuddyInt(infoPtr);
486c2c66affSColin Finck     int val = infoPtr->CurVal;
487c2c66affSColin Finck 
488c2c66affSColin Finck     if(!UPDOWN_InBounds(infoPtr, val)) {
489c2c66affSColin Finck         if((infoPtr->MinVal < infoPtr->MaxVal && val < infoPtr->MinVal)
490c2c66affSColin Finck                 || (infoPtr->MinVal > infoPtr->MaxVal && val > infoPtr->MinVal))
491c2c66affSColin Finck             val = infoPtr->MinVal;
492c2c66affSColin Finck         else
493c2c66affSColin Finck             val = infoPtr->MaxVal;
494c2c66affSColin Finck 
495c2c66affSColin Finck         succ = FALSE;
496c2c66affSColin Finck     }
497c2c66affSColin Finck 
498c2c66affSColin Finck     if(err) *err = !succ;
499c2c66affSColin Finck     return val;
500c2c66affSColin Finck }
501c2c66affSColin Finck 
UPDOWN_SetPos(UPDOWN_INFO * infoPtr,int pos)502c2c66affSColin Finck static int UPDOWN_SetPos(UPDOWN_INFO *infoPtr, int pos)
503c2c66affSColin Finck {
504c2c66affSColin Finck     int ret = infoPtr->CurVal;
505c2c66affSColin Finck 
506c2c66affSColin Finck     if(!UPDOWN_InBounds(infoPtr, pos)) {
507c2c66affSColin Finck         if((infoPtr->MinVal < infoPtr->MaxVal && pos < infoPtr->MinVal)
508c2c66affSColin Finck                 || (infoPtr->MinVal > infoPtr->MaxVal && pos > infoPtr->MinVal))
509c2c66affSColin Finck             pos = infoPtr->MinVal;
510c2c66affSColin Finck         else
511c2c66affSColin Finck             pos = infoPtr->MaxVal;
512c2c66affSColin Finck     }
513c2c66affSColin Finck 
514c2c66affSColin Finck     infoPtr->CurVal = pos;
515c2c66affSColin Finck     UPDOWN_SetBuddyInt(infoPtr);
516c2c66affSColin Finck 
517c2c66affSColin Finck     if(!UPDOWN_InBounds(infoPtr, ret)) {
518c2c66affSColin Finck         if((infoPtr->MinVal < infoPtr->MaxVal && ret < infoPtr->MinVal)
519c2c66affSColin Finck                 || (infoPtr->MinVal > infoPtr->MaxVal && ret > infoPtr->MinVal))
520c2c66affSColin Finck             ret = infoPtr->MinVal;
521c2c66affSColin Finck         else
522c2c66affSColin Finck             ret = infoPtr->MaxVal;
523c2c66affSColin Finck     }
524c2c66affSColin Finck     return ret;
525c2c66affSColin Finck }
526c2c66affSColin Finck 
527c2c66affSColin Finck 
528c2c66affSColin Finck /***********************************************************************
529c2c66affSColin Finck  * UPDOWN_SetRange
530c2c66affSColin Finck  *
531c2c66affSColin Finck  * Handle UDM_SETRANGE, UDM_SETRANGE32
532c2c66affSColin Finck  *
533c2c66affSColin Finck  * FIXME: handle Max == Min properly:
534c2c66affSColin Finck  *        - arrows should be disabled (without WS_DISABLED set),
535c2c66affSColin Finck  *          visually they can't be pressed and don't respond;
536c2c66affSColin Finck  *        - all input messages should still pass in.
537c2c66affSColin Finck  */
UPDOWN_SetRange(UPDOWN_INFO * infoPtr,INT Max,INT Min)538c2c66affSColin Finck static LRESULT UPDOWN_SetRange(UPDOWN_INFO *infoPtr, INT Max, INT Min)
539c2c66affSColin Finck {
540c2c66affSColin Finck     infoPtr->MaxVal = Max;
541c2c66affSColin Finck     infoPtr->MinVal = Min;
542c2c66affSColin Finck 
543c2c66affSColin Finck     TRACE("UpDown Ctrl new range(%d to %d), hwnd=%p\n",
544c2c66affSColin Finck            infoPtr->MinVal, infoPtr->MaxVal, infoPtr->Self);
545c2c66affSColin Finck 
546c2c66affSColin Finck     return 0;
547c2c66affSColin Finck }
548c2c66affSColin Finck 
549c2c66affSColin Finck /***********************************************************************
550c2c66affSColin Finck  * UPDOWN_MouseWheel
551c2c66affSColin Finck  *
552c2c66affSColin Finck  * Handle mouse wheel scrolling
553c2c66affSColin Finck  */
UPDOWN_MouseWheel(UPDOWN_INFO * infoPtr,WPARAM wParam)554c2c66affSColin Finck static LRESULT UPDOWN_MouseWheel(UPDOWN_INFO *infoPtr, WPARAM wParam)
555c2c66affSColin Finck {
556c2c66affSColin Finck     int iWheelDelta = GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA;
557c2c66affSColin Finck 
558c2c66affSColin Finck     if (wParam & (MK_SHIFT | MK_CONTROL))
559c2c66affSColin Finck         return 0;
560c2c66affSColin Finck 
561c2c66affSColin Finck     if (iWheelDelta != 0)
562c2c66affSColin Finck     {
563c2c66affSColin Finck         UPDOWN_GetBuddyInt(infoPtr);
564c2c66affSColin Finck         UPDOWN_DoAction(infoPtr, abs(iWheelDelta), iWheelDelta > 0 ? FLAG_INCR : FLAG_DECR);
565c2c66affSColin Finck     }
566c2c66affSColin Finck 
567c2c66affSColin Finck     return 1;
568c2c66affSColin Finck }
569c2c66affSColin Finck 
570c2c66affSColin Finck 
571c2c66affSColin Finck /***********************************************************************
572c2c66affSColin Finck  * UPDOWN_Buddy_SubclassProc used to handle messages sent to the buddy
573c2c66affSColin Finck  *                           control.
574c2c66affSColin Finck  */
575c2c66affSColin Finck static LRESULT CALLBACK
UPDOWN_Buddy_SubclassProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam,UINT_PTR uId,DWORD_PTR ref_data)576c2c66affSColin Finck UPDOWN_Buddy_SubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
577c2c66affSColin Finck                           UINT_PTR uId, DWORD_PTR ref_data)
578c2c66affSColin Finck {
579c2c66affSColin Finck     UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr((HWND)ref_data);
580c2c66affSColin Finck 
581c2c66affSColin Finck     TRACE("hwnd=%p, uMsg=%04x, wParam=%08lx, lParam=%08lx\n",
582c2c66affSColin Finck           hwnd, uMsg, wParam, lParam);
583c2c66affSColin Finck 
584c2c66affSColin Finck     switch(uMsg)
585c2c66affSColin Finck     {
586c2c66affSColin Finck     case WM_KEYDOWN:
587b3fb8555SGiannis Adamopoulos         if (infoPtr)
588b3fb8555SGiannis Adamopoulos         {
589c2c66affSColin Finck             UPDOWN_KeyPressed(infoPtr, (int)wParam);
590b3fb8555SGiannis Adamopoulos             if (wParam == VK_UP || wParam == VK_DOWN)
591b3fb8555SGiannis Adamopoulos                 return 0;
592b3fb8555SGiannis Adamopoulos         }
593c2c66affSColin Finck         break;
594c2c66affSColin Finck 
595c2c66affSColin Finck     case WM_MOUSEWHEEL:
596b3fb8555SGiannis Adamopoulos         if (infoPtr)
597c2c66affSColin Finck             UPDOWN_MouseWheel(infoPtr, (int)wParam);
598c2c66affSColin Finck         break;
599c2c66affSColin Finck 
600b3fb8555SGiannis Adamopoulos     case WM_NCDESTROY:
601b3fb8555SGiannis Adamopoulos         RemoveWindowSubclass(hwnd, UPDOWN_Buddy_SubclassProc, BUDDY_SUBCLASSID);
602b3fb8555SGiannis Adamopoulos         break;
603c2c66affSColin Finck     default:
604c2c66affSColin Finck 	break;
605c2c66affSColin Finck     }
606c2c66affSColin Finck 
607c2c66affSColin Finck     return DefSubclassProc(hwnd, uMsg, wParam, lParam);
608c2c66affSColin Finck }
609c2c66affSColin Finck 
UPDOWN_ResetSubclass(UPDOWN_INFO * infoPtr)610b3fb8555SGiannis Adamopoulos static void UPDOWN_ResetSubclass (UPDOWN_INFO *infoPtr)
611b3fb8555SGiannis Adamopoulos {
612b3fb8555SGiannis Adamopoulos     SetWindowSubclass(infoPtr->Buddy, UPDOWN_Buddy_SubclassProc, BUDDY_SUBCLASSID, 0);
613b3fb8555SGiannis Adamopoulos }
614b3fb8555SGiannis Adamopoulos 
615c2c66affSColin Finck /***********************************************************************
616c2c66affSColin Finck  *           UPDOWN_SetBuddy
617c2c66affSColin Finck  *
618c2c66affSColin Finck  * Sets bud as a new Buddy.
619c2c66affSColin Finck  * Then, it should subclass the buddy
620c2c66affSColin Finck  * If window has the UDS_ARROWKEYS, it subclasses the buddy window to
621c2c66affSColin Finck  * process the UP/DOWN arrow keys.
622c2c66affSColin Finck  * If window has the UDS_ALIGNLEFT or UDS_ALIGNRIGHT style
623c2c66affSColin Finck  * the size/pos of the buddy and the control are adjusted accordingly.
624c2c66affSColin Finck  */
UPDOWN_SetBuddy(UPDOWN_INFO * infoPtr,HWND bud)625c2c66affSColin Finck static HWND UPDOWN_SetBuddy (UPDOWN_INFO* infoPtr, HWND bud)
626c2c66affSColin Finck {
627c2c66affSColin Finck     RECT  budRect;  /* new coord for the buddy */
628c2c66affSColin Finck     int   x, width;  /* new x position and width for the up-down */
629c2c66affSColin Finck     WCHAR buddyClass[40];
630edd99e8cSAmine Khaldi     HWND old_buddy;
631c2c66affSColin Finck 
632c2c66affSColin Finck     TRACE("(hwnd=%p, bud=%p)\n", infoPtr->Self, bud);
633c2c66affSColin Finck 
634edd99e8cSAmine Khaldi     old_buddy = infoPtr->Buddy;
635c2c66affSColin Finck 
636b3fb8555SGiannis Adamopoulos     UPDOWN_ResetSubclass (infoPtr);
637b3fb8555SGiannis Adamopoulos 
638c2c66affSColin Finck     if (!IsWindow(bud)) bud = NULL;
639c2c66affSColin Finck 
640c2c66affSColin Finck     /* Store buddy window handle */
641c2c66affSColin Finck     infoPtr->Buddy = bud;
642c2c66affSColin Finck 
643c2c66affSColin Finck     if(bud) {
644c2c66affSColin Finck         /* Store buddy window class type */
645c2c66affSColin Finck         infoPtr->BuddyType = BUDDY_TYPE_UNKNOWN;
646b3fb8555SGiannis Adamopoulos         if (GetClassNameW(bud, buddyClass, ARRAY_SIZE(buddyClass))) {
647c2c66affSColin Finck             if (lstrcmpiW(buddyClass, WC_EDITW) == 0)
648c2c66affSColin Finck                 infoPtr->BuddyType = BUDDY_TYPE_EDIT;
649c2c66affSColin Finck             else if (lstrcmpiW(buddyClass, WC_LISTBOXW) == 0)
650c2c66affSColin Finck                 infoPtr->BuddyType = BUDDY_TYPE_LISTBOX;
651c2c66affSColin Finck         }
652c2c66affSColin Finck 
653c2c66affSColin Finck         if (infoPtr->dwStyle & UDS_ARROWKEYS)
654c2c66affSColin Finck             SetWindowSubclass(bud, UPDOWN_Buddy_SubclassProc, BUDDY_SUBCLASSID,
655c2c66affSColin Finck                               (DWORD_PTR)infoPtr->Self);
656c2c66affSColin Finck 
657c2c66affSColin Finck         /* Get the rect of the buddy relative to its parent */
658c2c66affSColin Finck         GetWindowRect(infoPtr->Buddy, &budRect);
659c2c66affSColin Finck         MapWindowPoints(HWND_DESKTOP, GetParent(infoPtr->Buddy), (POINT *)(&budRect.left), 2);
660c2c66affSColin Finck 
661c2c66affSColin Finck         /* now do the positioning */
662c2c66affSColin Finck         if  (infoPtr->dwStyle & UDS_ALIGNLEFT) {
663c2c66affSColin Finck             x  = budRect.left;
664c2c66affSColin Finck             budRect.left += DEFAULT_WIDTH + DEFAULT_XSEP;
665c2c66affSColin Finck         } else if (infoPtr->dwStyle & UDS_ALIGNRIGHT) {
666c2c66affSColin Finck             budRect.right -= DEFAULT_WIDTH + DEFAULT_XSEP;
667c2c66affSColin Finck             x  = budRect.right+DEFAULT_XSEP;
668c2c66affSColin Finck         } else {
669c2c66affSColin Finck             /* nothing to do */
670edd99e8cSAmine Khaldi             return old_buddy;
671c2c66affSColin Finck         }
672c2c66affSColin Finck 
673c2c66affSColin Finck         /* first adjust the buddy to accommodate the up/down */
674c2c66affSColin Finck         SetWindowPos(infoPtr->Buddy, 0, budRect.left, budRect.top,
675c2c66affSColin Finck                      budRect.right  - budRect.left, budRect.bottom - budRect.top,
676c2c66affSColin Finck                      SWP_NOACTIVATE|SWP_NOZORDER);
677c2c66affSColin Finck 
678c2c66affSColin Finck         /* now position the up/down */
679c2c66affSColin Finck         /* Since the UDS_ALIGN* flags were used, */
680c2c66affSColin Finck         /* we will pick the position and size of the window. */
681c2c66affSColin Finck         width = DEFAULT_WIDTH;
682c2c66affSColin Finck 
683c2c66affSColin Finck         /*
684c2c66affSColin Finck          * If the updown has a buddy border, it has to overlap with the buddy
685c2c66affSColin Finck          * to look as if it is integrated with the buddy control.
686c2c66affSColin Finck          * We nudge the control or change its size to overlap.
687c2c66affSColin Finck          */
688c2c66affSColin Finck         if (UPDOWN_HasBuddyBorder(infoPtr)) {
689c2c66affSColin Finck             if(infoPtr->dwStyle & UDS_ALIGNLEFT)
690c2c66affSColin Finck                 width += DEFAULT_BUDDYBORDER;
691c2c66affSColin Finck             else
692c2c66affSColin Finck                 x -= DEFAULT_BUDDYBORDER;
693c2c66affSColin Finck         }
694c2c66affSColin Finck 
695c2c66affSColin Finck         SetWindowPos(infoPtr->Self, 0, x,
696c2c66affSColin Finck                      budRect.top - DEFAULT_ADDTOP, width,
697c2c66affSColin Finck                      budRect.bottom - budRect.top + DEFAULT_ADDTOP + DEFAULT_ADDBOT,
698c2c66affSColin Finck                      SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOZORDER);
699edd99e8cSAmine Khaldi     } else if (!(infoPtr->dwStyle & UDS_HORZ) && old_buddy != NULL) {
700c2c66affSColin Finck         RECT rect;
701c2c66affSColin Finck         GetWindowRect(infoPtr->Self, &rect);
702c2c66affSColin Finck         MapWindowPoints(HWND_DESKTOP, GetParent(infoPtr->Self), (POINT *)&rect, 2);
703c2c66affSColin Finck         SetWindowPos(infoPtr->Self, 0, rect.left, rect.top, DEFAULT_WIDTH, rect.bottom - rect.top,
704c2c66affSColin Finck                      SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOZORDER);
705c2c66affSColin Finck     }
706edd99e8cSAmine Khaldi 
707edd99e8cSAmine Khaldi     return old_buddy;
708c2c66affSColin Finck }
709c2c66affSColin Finck 
710c2c66affSColin Finck /***********************************************************************
711c2c66affSColin Finck  *           UPDOWN_DoAction
712c2c66affSColin Finck  *
713c2c66affSColin Finck  * This function increments/decrements the CurVal by the
714c2c66affSColin Finck  * 'delta' amount according to the 'action' flag which can be a
715c2c66affSColin Finck  * combination of FLAG_INCR and FLAG_DECR
716c2c66affSColin Finck  * It notifies the parent as required.
717c2c66affSColin Finck  * It handles wrapping and non-wrapping correctly.
718c2c66affSColin Finck  * It is assumed that delta>0
719c2c66affSColin Finck  */
UPDOWN_DoAction(UPDOWN_INFO * infoPtr,int delta,int action)720c2c66affSColin Finck static void UPDOWN_DoAction (UPDOWN_INFO *infoPtr, int delta, int action)
721c2c66affSColin Finck {
722c2c66affSColin Finck     NM_UPDOWN ni;
723c2c66affSColin Finck 
724c2c66affSColin Finck     TRACE("%d by %d\n", action, delta);
725c2c66affSColin Finck 
726c2c66affSColin Finck     /* check if we can do the modification first */
727c2c66affSColin Finck     delta *= (action & FLAG_INCR ? 1 : -1) * (infoPtr->MaxVal < infoPtr->MinVal ? -1 : 1);
728c2c66affSColin Finck     if ( (action & FLAG_INCR) && (action & FLAG_DECR) ) delta = 0;
729c2c66affSColin Finck 
730c2c66affSColin Finck     TRACE("current %d, delta: %d\n", infoPtr->CurVal, delta);
731c2c66affSColin Finck 
732c2c66affSColin Finck     /* We must notify parent now to obtain permission */
733c2c66affSColin Finck     ni.iPos = infoPtr->CurVal;
734c2c66affSColin Finck     ni.iDelta = delta;
735c2c66affSColin Finck     ni.hdr.hwndFrom = infoPtr->Self;
736c2c66affSColin Finck     ni.hdr.idFrom   = GetWindowLongPtrW (infoPtr->Self, GWLP_ID);
737c2c66affSColin Finck     ni.hdr.code = UDN_DELTAPOS;
738c2c66affSColin Finck     if (!SendMessageW(infoPtr->Notify, WM_NOTIFY, ni.hdr.idFrom, (LPARAM)&ni)) {
739c2c66affSColin Finck         /* Parent said: OK to adjust */
740c2c66affSColin Finck 
741c2c66affSColin Finck         /* Now adjust value with (maybe new) delta */
742c2c66affSColin Finck         if (UPDOWN_OffsetVal (infoPtr, ni.iDelta)) {
743c2c66affSColin Finck             TRACE("new %d, delta: %d\n", infoPtr->CurVal, ni.iDelta);
744c2c66affSColin Finck 
745c2c66affSColin Finck             /* Now take care about our buddy */
746c2c66affSColin Finck             UPDOWN_SetBuddyInt (infoPtr);
747c2c66affSColin Finck         }
748c2c66affSColin Finck     }
749c2c66affSColin Finck 
750c2c66affSColin Finck     /* Also, notify it. This message is sent in any case. */
751c2c66affSColin Finck     SendMessageW( infoPtr->Notify, (infoPtr->dwStyle & UDS_HORZ) ? WM_HSCROLL : WM_VSCROLL,
752c2c66affSColin Finck 		  MAKELONG(SB_THUMBPOSITION, infoPtr->CurVal), (LPARAM)infoPtr->Self);
753c2c66affSColin Finck }
754c2c66affSColin Finck 
755c2c66affSColin Finck /***********************************************************************
756c2c66affSColin Finck  *           UPDOWN_IsEnabled
757c2c66affSColin Finck  *
758c2c66affSColin Finck  * Returns TRUE if it is enabled as well as its buddy (if any)
759c2c66affSColin Finck  *         FALSE otherwise
760c2c66affSColin Finck  */
UPDOWN_IsEnabled(const UPDOWN_INFO * infoPtr)761c2c66affSColin Finck static BOOL UPDOWN_IsEnabled (const UPDOWN_INFO *infoPtr)
762c2c66affSColin Finck {
763c2c66affSColin Finck     if (!IsWindowEnabled(infoPtr->Self))
764c2c66affSColin Finck         return FALSE;
765c2c66affSColin Finck     if(infoPtr->Buddy)
766c2c66affSColin Finck         return IsWindowEnabled(infoPtr->Buddy);
767c2c66affSColin Finck     return TRUE;
768c2c66affSColin Finck }
769c2c66affSColin Finck 
770c2c66affSColin Finck /***********************************************************************
771c2c66affSColin Finck  *           UPDOWN_CancelMode
772c2c66affSColin Finck  *
773c2c66affSColin Finck  * Deletes any timers, releases the mouse and does  redraw if necessary.
774c2c66affSColin Finck  * If the control is not in "capture" mode, it does nothing.
775c2c66affSColin Finck  * If the control was not in cancel mode, it returns FALSE.
776c2c66affSColin Finck  * If the control was in cancel mode, it returns TRUE.
777c2c66affSColin Finck  */
UPDOWN_CancelMode(UPDOWN_INFO * infoPtr)778c2c66affSColin Finck static BOOL UPDOWN_CancelMode (UPDOWN_INFO *infoPtr)
779c2c66affSColin Finck {
780c2c66affSColin Finck     if (!(infoPtr->Flags & FLAG_PRESSED)) return FALSE;
781c2c66affSColin Finck 
782c2c66affSColin Finck     KillTimer (infoPtr->Self, TIMER_AUTOREPEAT);
783c2c66affSColin Finck     KillTimer (infoPtr->Self, TIMER_ACCEL);
784c2c66affSColin Finck     KillTimer (infoPtr->Self, TIMER_AUTOPRESS);
785c2c66affSColin Finck 
786841732e4SJoachim Henze     if (GetCapture() == infoPtr->Self)
787c2c66affSColin Finck         ReleaseCapture();
788c2c66affSColin Finck 
789c2c66affSColin Finck     infoPtr->Flags &= ~FLAG_PRESSED;
790c2c66affSColin Finck     InvalidateRect (infoPtr->Self, NULL, FALSE);
791c2c66affSColin Finck 
792c2c66affSColin Finck     return TRUE;
793c2c66affSColin Finck }
794c2c66affSColin Finck 
795c2c66affSColin Finck /***********************************************************************
796c2c66affSColin Finck  *           UPDOWN_HandleMouseEvent
797c2c66affSColin Finck  *
798c2c66affSColin Finck  * Handle a mouse event for the updown.
799c2c66affSColin Finck  * 'pt' is the location of the mouse event in client or
800c2c66affSColin Finck  * windows coordinates.
801c2c66affSColin Finck  */
UPDOWN_HandleMouseEvent(UPDOWN_INFO * infoPtr,UINT msg,INT x,INT y)802c2c66affSColin Finck static void UPDOWN_HandleMouseEvent (UPDOWN_INFO *infoPtr, UINT msg, INT x, INT y)
803c2c66affSColin Finck {
804c2c66affSColin Finck     POINT pt = { x, y };
805c2c66affSColin Finck     RECT rect;
806c2c66affSColin Finck     int temp, arrow;
807c2c66affSColin Finck     TRACKMOUSEEVENT tme;
808c2c66affSColin Finck 
809c2c66affSColin Finck     TRACE("msg %04x point %s\n", msg, wine_dbgstr_point(&pt));
810c2c66affSColin Finck 
811c2c66affSColin Finck     switch(msg)
812c2c66affSColin Finck     {
813c2c66affSColin Finck         case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
814c2c66affSColin Finck 
815c2c66affSColin Finck             /* If the buddy is an edit, will set focus to it */
816c2c66affSColin Finck 	    if (UPDOWN_IsBuddyEdit(infoPtr)) SetFocus(infoPtr->Buddy);
817c2c66affSColin Finck 
818c2c66affSColin Finck             /* Now see which one is the 'active' arrow */
819c2c66affSColin Finck             arrow = UPDOWN_GetArrowFromPoint (infoPtr, &rect, pt);
820c2c66affSColin Finck 
821c2c66affSColin Finck             /* Update the flags if we are in/out */
822c2c66affSColin Finck             infoPtr->Flags &= ~(FLAG_MOUSEIN | FLAG_ARROW);
823c2c66affSColin Finck             if (arrow)
824c2c66affSColin Finck                 infoPtr->Flags |= FLAG_MOUSEIN | arrow;
825c2c66affSColin Finck             else
826c2c66affSColin Finck                 if (infoPtr->AccelIndex != -1) infoPtr->AccelIndex = 0;
827c2c66affSColin Finck 
828c2c66affSColin Finck 	    if (infoPtr->Flags & FLAG_ARROW) {
829c2c66affSColin Finck 
830c2c66affSColin Finck             	/* Update the CurVal if necessary */
831c2c66affSColin Finck             	UPDOWN_GetBuddyInt (infoPtr);
832c2c66affSColin Finck 
833c2c66affSColin Finck             	/* Set up the correct flags */
834c2c66affSColin Finck             	infoPtr->Flags |= FLAG_PRESSED;
835c2c66affSColin Finck 
836c2c66affSColin Finck             	/* repaint the control */
837c2c66affSColin Finck 	    	InvalidateRect (infoPtr->Self, NULL, FALSE);
838c2c66affSColin Finck 
839c2c66affSColin Finck             	/* process the click */
840c2c66affSColin Finck 		temp = (infoPtr->AccelCount && infoPtr->AccelVect) ? infoPtr->AccelVect[0].nInc : 1;
841c2c66affSColin Finck             	UPDOWN_DoAction (infoPtr, temp, infoPtr->Flags & FLAG_ARROW);
842c2c66affSColin Finck 
843c2c66affSColin Finck             	/* now capture all mouse messages */
844c2c66affSColin Finck             	SetCapture (infoPtr->Self);
845c2c66affSColin Finck 
846c2c66affSColin Finck             	/* and startup the first timer */
847c2c66affSColin Finck             	SetTimer(infoPtr->Self, TIMER_AUTOREPEAT, INITIAL_DELAY, 0);
848c2c66affSColin Finck 	    }
849c2c66affSColin Finck             break;
850c2c66affSColin Finck 
851c2c66affSColin Finck 	case WM_MOUSEMOVE:
852c2c66affSColin Finck             /* save the flags to see if any got modified */
853c2c66affSColin Finck             temp = infoPtr->Flags;
854c2c66affSColin Finck 
855c2c66affSColin Finck             /* Now see which one is the 'active' arrow */
856c2c66affSColin Finck             arrow = UPDOWN_GetArrowFromPoint (infoPtr, &rect, pt);
857c2c66affSColin Finck 
858c2c66affSColin Finck             /* Update the flags if we are in/out */
859c2c66affSColin Finck 	    infoPtr->Flags &= ~(FLAG_MOUSEIN | FLAG_ARROW);
860c2c66affSColin Finck             if(arrow) {
861c2c66affSColin Finck 	        infoPtr->Flags |=  FLAG_MOUSEIN | arrow;
862c2c66affSColin Finck             } else {
863c2c66affSColin Finck 	        if(infoPtr->AccelIndex != -1) infoPtr->AccelIndex = 0;
864c2c66affSColin Finck             }
865c2c66affSColin Finck 
866c2c66affSColin Finck             /* If state changed, redraw the control */
867c2c66affSColin Finck             if(temp != infoPtr->Flags)
868c2c66affSColin Finck 		 InvalidateRect (infoPtr->Self, NULL, FALSE);
869c2c66affSColin Finck 
870c2c66affSColin Finck             /* Set up tracking so the mousein flags can be reset when the
871c2c66affSColin Finck              * mouse leaves the control */
872c2c66affSColin Finck             tme.cbSize = sizeof( tme );
873c2c66affSColin Finck             tme.dwFlags = TME_LEAVE;
874c2c66affSColin Finck             tme.hwndTrack = infoPtr->Self;
875c2c66affSColin Finck             TrackMouseEvent (&tme);
876c2c66affSColin Finck 
877c2c66affSColin Finck             break;
878c2c66affSColin Finck         case WM_MOUSELEAVE:
879c2c66affSColin Finck 	    infoPtr->Flags &= ~(FLAG_MOUSEIN | FLAG_ARROW);
880c2c66affSColin Finck             InvalidateRect (infoPtr->Self, NULL, FALSE);
881c2c66affSColin Finck             break;
882c2c66affSColin Finck 
883c2c66affSColin Finck 	default:
884c2c66affSColin Finck 	    ERR("Impossible case (msg=%x)!\n", msg);
885c2c66affSColin Finck     }
886c2c66affSColin Finck 
887c2c66affSColin Finck }
888c2c66affSColin Finck 
889c2c66affSColin Finck /***********************************************************************
890c2c66affSColin Finck  *           UpDownWndProc
891c2c66affSColin Finck  */
UpDownWindowProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)892c2c66affSColin Finck static LRESULT WINAPI UpDownWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
893c2c66affSColin Finck {
894c2c66affSColin Finck     UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd);
895c2c66affSColin Finck     static const WCHAR themeClass[] = {'S','p','i','n',0};
896c2c66affSColin Finck     HTHEME theme;
897c2c66affSColin Finck 
898c2c66affSColin Finck     TRACE("hwnd=%p msg=%04x wparam=%08lx lparam=%08lx\n", hwnd, message, wParam, lParam);
899c2c66affSColin Finck 
900c2c66affSColin Finck     if (!infoPtr && (message != WM_CREATE))
901c2c66affSColin Finck         return DefWindowProcW (hwnd, message, wParam, lParam);
902c2c66affSColin Finck 
903c2c66affSColin Finck     switch(message)
904c2c66affSColin Finck     {
905c2c66affSColin Finck         case WM_CREATE:
906c2c66affSColin Finck 	    {
907c2c66affSColin Finck 	    CREATESTRUCTW *pcs = (CREATESTRUCTW*)lParam;
908c2c66affSColin Finck 
909b3fb8555SGiannis Adamopoulos             infoPtr = heap_alloc_zero(sizeof(*infoPtr));
910c2c66affSColin Finck 	    SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
911c2c66affSColin Finck 
912c2c66affSColin Finck 	    /* initialize the info struct */
913c2c66affSColin Finck 	    infoPtr->Self = hwnd;
914c2c66affSColin Finck 	    infoPtr->Notify  = pcs->hwndParent;
915c2c66affSColin Finck 	    infoPtr->dwStyle = pcs->style;
916c2c66affSColin Finck 	    infoPtr->AccelCount = 0;
917c2c66affSColin Finck 	    infoPtr->AccelVect = 0;
918c2c66affSColin Finck 	    infoPtr->AccelIndex = -1;
919c2c66affSColin Finck 	    infoPtr->CurVal = 0;
920c2c66affSColin Finck 	    infoPtr->MinVal = 100;
921c2c66affSColin Finck 	    infoPtr->MaxVal = 0;
922c2c66affSColin Finck 	    infoPtr->Base  = 10; /* Default to base 10  */
923c2c66affSColin Finck 	    infoPtr->Buddy = 0;  /* No buddy window yet */
924c2c66affSColin Finck 	    infoPtr->Flags = (infoPtr->dwStyle & UDS_SETBUDDYINT) ? FLAG_BUDDYINT : 0;
925c2c66affSColin Finck 
926c2c66affSColin Finck             SetWindowLongW (hwnd, GWL_STYLE, infoPtr->dwStyle & ~WS_BORDER);
927c2c66affSColin Finck 	    if (!(infoPtr->dwStyle & UDS_HORZ))
928c2c66affSColin Finck 	        SetWindowPos (hwnd, NULL, 0, 0, DEFAULT_WIDTH, pcs->cy,
929c2c66affSColin Finck 	                      SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
930c2c66affSColin Finck 
931c2c66affSColin Finck             /* Do we pick the buddy win ourselves? */
932c2c66affSColin Finck 	    if (infoPtr->dwStyle & UDS_AUTOBUDDY)
933c2c66affSColin Finck 		UPDOWN_SetBuddy (infoPtr, GetWindow (hwnd, GW_HWNDPREV));
934c2c66affSColin Finck 
935c2c66affSColin Finck 	    OpenThemeData (hwnd, themeClass);
936c2c66affSColin Finck 
937c2c66affSColin Finck 	    TRACE("UpDown Ctrl creation, hwnd=%p\n", hwnd);
938c2c66affSColin Finck 	    }
939c2c66affSColin Finck 	    break;
940c2c66affSColin Finck 
941c2c66affSColin Finck 	case WM_DESTROY:
942b3fb8555SGiannis Adamopoulos 	    heap_free (infoPtr->AccelVect);
943b3fb8555SGiannis Adamopoulos             UPDOWN_ResetSubclass (infoPtr);
944b3fb8555SGiannis Adamopoulos 	    heap_free (infoPtr);
945c2c66affSColin Finck 	    SetWindowLongPtrW (hwnd, 0, 0);
946c2c66affSColin Finck             theme = GetWindowTheme (hwnd);
947c2c66affSColin Finck             CloseThemeData (theme);
948c2c66affSColin Finck 	    TRACE("UpDown Ctrl destruction, hwnd=%p\n", hwnd);
949c2c66affSColin Finck 	    break;
950c2c66affSColin Finck 
951c2c66affSColin Finck 	case WM_ENABLE:
952c2c66affSColin Finck 	    if (wParam) {
953c2c66affSColin Finck 		infoPtr->dwStyle &= ~WS_DISABLED;
954c2c66affSColin Finck 	    } else {
955c2c66affSColin Finck 		infoPtr->dwStyle |= WS_DISABLED;
956c2c66affSColin Finck 	    	UPDOWN_CancelMode (infoPtr);
957c2c66affSColin Finck 	    }
958c2c66affSColin Finck 	    InvalidateRect (infoPtr->Self, NULL, FALSE);
959c2c66affSColin Finck 	    break;
960c2c66affSColin Finck 
961c2c66affSColin Finck         case WM_STYLECHANGED:
962c2c66affSColin Finck             if (wParam == GWL_STYLE) {
963c2c66affSColin Finck                 infoPtr->dwStyle = ((LPSTYLESTRUCT)lParam)->styleNew;
964c2c66affSColin Finck 	        InvalidateRect (infoPtr->Self, NULL, FALSE);
965c2c66affSColin Finck             }
966c2c66affSColin Finck             break;
967c2c66affSColin Finck 
968c2c66affSColin Finck         case WM_THEMECHANGED:
969c2c66affSColin Finck             theme = GetWindowTheme (hwnd);
970c2c66affSColin Finck             CloseThemeData (theme);
971c2c66affSColin Finck             OpenThemeData (hwnd, themeClass);
972c2c66affSColin Finck             InvalidateRect (hwnd, NULL, FALSE);
973c2c66affSColin Finck             break;
974c2c66affSColin Finck 
975c2c66affSColin Finck 	case WM_TIMER:
976c2c66affSColin Finck 	   /* is this the auto-press timer? */
977c2c66affSColin Finck 	   if(wParam == TIMER_AUTOPRESS) {
978c2c66affSColin Finck 		KillTimer(hwnd, TIMER_AUTOPRESS);
979c2c66affSColin Finck 		infoPtr->Flags &= ~(FLAG_PRESSED | FLAG_ARROW);
980c2c66affSColin Finck 		InvalidateRect(infoPtr->Self, NULL, FALSE);
981c2c66affSColin Finck 	   }
982c2c66affSColin Finck 
983c2c66affSColin Finck 	   /* if initial timer, kill it and start the repeat timer */
984c2c66affSColin Finck   	   if(wParam == TIMER_AUTOREPEAT) {
985c2c66affSColin Finck 		INT delay;
986c2c66affSColin Finck 
987c2c66affSColin Finck 		KillTimer(hwnd, TIMER_AUTOREPEAT);
988c2c66affSColin Finck 		/* if no accel info given, used default timer */
989c2c66affSColin Finck 		if(infoPtr->AccelCount==0 || infoPtr->AccelVect==0) {
990c2c66affSColin Finck 		    infoPtr->AccelIndex = -1;
991c2c66affSColin Finck 		    delay = REPEAT_DELAY;
992c2c66affSColin Finck 		} else {
993c2c66affSColin Finck 		    infoPtr->AccelIndex = 0; /* otherwise, use it */
994c2c66affSColin Finck 		    delay = infoPtr->AccelVect[infoPtr->AccelIndex].nSec * 1000 + 1;
995c2c66affSColin Finck 		}
996c2c66affSColin Finck 		SetTimer(hwnd, TIMER_ACCEL, delay, 0);
997c2c66affSColin Finck       	    }
998c2c66affSColin Finck 
999c2c66affSColin Finck 	    /* now, if the mouse is above us, do the thing...*/
1000c2c66affSColin Finck 	    if(infoPtr->Flags & FLAG_MOUSEIN) {
1001c2c66affSColin Finck 		int temp;
1002c2c66affSColin Finck 
1003c2c66affSColin Finck 		temp = infoPtr->AccelIndex == -1 ? 1 : infoPtr->AccelVect[infoPtr->AccelIndex].nInc;
1004c2c66affSColin Finck 		UPDOWN_DoAction(infoPtr, temp, infoPtr->Flags & FLAG_ARROW);
1005c2c66affSColin Finck 
1006c2c66affSColin Finck 		if(infoPtr->AccelIndex != -1 && infoPtr->AccelIndex < infoPtr->AccelCount-1) {
1007c2c66affSColin Finck 		    KillTimer(hwnd, TIMER_ACCEL);
1008c2c66affSColin Finck 		    infoPtr->AccelIndex++; /* move to the next accel info */
1009c2c66affSColin Finck 		    temp = infoPtr->AccelVect[infoPtr->AccelIndex].nSec * 1000 + 1;
1010c2c66affSColin Finck 	  	    /* make sure we have at least 1ms intervals */
1011c2c66affSColin Finck 		    SetTimer(hwnd, TIMER_ACCEL, temp, 0);
1012c2c66affSColin Finck 		}
1013c2c66affSColin Finck 	    }
1014c2c66affSColin Finck 	    break;
1015c2c66affSColin Finck 
1016c2c66affSColin Finck 	case WM_CANCELMODE:
1017c2c66affSColin Finck 	  return UPDOWN_CancelMode (infoPtr);
1018c2c66affSColin Finck 
1019c2c66affSColin Finck 	case WM_LBUTTONUP:
1020c2c66affSColin Finck 	    if (GetCapture() != infoPtr->Self) break;
1021c2c66affSColin Finck 
1022c2c66affSColin Finck 	    if ( (infoPtr->Flags & FLAG_MOUSEIN) &&
1023c2c66affSColin Finck 		 (infoPtr->Flags & FLAG_ARROW) ) {
1024c2c66affSColin Finck 
1025c2c66affSColin Finck 	    	SendMessageW( infoPtr->Notify,
1026c2c66affSColin Finck 			      (infoPtr->dwStyle & UDS_HORZ) ? WM_HSCROLL : WM_VSCROLL,
1027c2c66affSColin Finck                   	      MAKELONG(SB_ENDSCROLL, infoPtr->CurVal),
1028c2c66affSColin Finck 			      (LPARAM)hwnd);
1029c2c66affSColin Finck 		if (UPDOWN_IsBuddyEdit(infoPtr))
1030c2c66affSColin Finck 		    SendMessageW(infoPtr->Buddy, EM_SETSEL, 0, MAKELONG(0, -1));
1031c2c66affSColin Finck 	    }
1032c2c66affSColin Finck 	    UPDOWN_CancelMode(infoPtr);
1033c2c66affSColin Finck 	    break;
1034c2c66affSColin Finck 
1035c2c66affSColin Finck 	case WM_LBUTTONDOWN:
1036c2c66affSColin Finck 	case WM_MOUSEMOVE:
1037c2c66affSColin Finck         case WM_MOUSELEAVE:
1038c2c66affSColin Finck 	    if(UPDOWN_IsEnabled(infoPtr))
1039c2c66affSColin Finck 		UPDOWN_HandleMouseEvent (infoPtr, message, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
1040c2c66affSColin Finck 	    break;
1041c2c66affSColin Finck 
1042c2c66affSColin Finck         case WM_MOUSEWHEEL:
1043c2c66affSColin Finck             UPDOWN_MouseWheel(infoPtr, wParam);
1044c2c66affSColin Finck             break;
1045c2c66affSColin Finck 
1046c2c66affSColin Finck 	case WM_KEYDOWN:
1047c2c66affSColin Finck 	    if((infoPtr->dwStyle & UDS_ARROWKEYS) && UPDOWN_IsEnabled(infoPtr))
1048c2c66affSColin Finck 		return UPDOWN_KeyPressed(infoPtr, (int)wParam);
1049c2c66affSColin Finck 	    break;
1050c2c66affSColin Finck 
1051c2c66affSColin Finck 	case WM_PRINTCLIENT:
1052c2c66affSColin Finck 	case WM_PAINT:
1053c2c66affSColin Finck 	    return UPDOWN_Paint (infoPtr, (HDC)wParam);
1054c2c66affSColin Finck 
1055c2c66affSColin Finck 	case UDM_GETACCEL:
1056c2c66affSColin Finck 	    if (wParam==0 && lParam==0) return infoPtr->AccelCount;
1057c2c66affSColin Finck 	    if (wParam && lParam) {
1058c2c66affSColin Finck 		int temp = min(infoPtr->AccelCount, wParam);
1059c2c66affSColin Finck 	        memcpy((void *)lParam, infoPtr->AccelVect, temp*sizeof(UDACCEL));
1060c2c66affSColin Finck 	        return temp;
1061c2c66affSColin Finck       	    }
1062c2c66affSColin Finck 	    return 0;
1063c2c66affSColin Finck 
1064c2c66affSColin Finck 	case UDM_SETACCEL:
1065c2c66affSColin Finck 	{
1066c2c66affSColin Finck 	    TRACE("UDM_SETACCEL\n");
1067c2c66affSColin Finck 
1068c2c66affSColin Finck 	    if(infoPtr->AccelVect) {
1069b3fb8555SGiannis Adamopoulos 		heap_free (infoPtr->AccelVect);
1070c2c66affSColin Finck 		infoPtr->AccelCount = 0;
1071c2c66affSColin Finck 		infoPtr->AccelVect  = 0;
1072c2c66affSColin Finck       	    }
1073c2c66affSColin Finck 	    if(wParam==0) return TRUE;
1074b3fb8555SGiannis Adamopoulos 	    infoPtr->AccelVect = heap_alloc(wParam*sizeof(UDACCEL));
1075b3fb8555SGiannis Adamopoulos 	    if(!infoPtr->AccelVect) return FALSE;
1076c2c66affSColin Finck 	    memcpy(infoPtr->AccelVect, (void*)lParam, wParam*sizeof(UDACCEL));
1077c2c66affSColin Finck             infoPtr->AccelCount = wParam;
1078c2c66affSColin Finck 
1079c2c66affSColin Finck             if (TRACE_ON(updown))
1080c2c66affSColin Finck             {
1081c2c66affSColin Finck                 UINT i;
1082c2c66affSColin Finck 
1083c2c66affSColin Finck                 for (i = 0; i < wParam; i++)
1084c2c66affSColin Finck                     TRACE("%u: nSec %u nInc %u\n", i,
1085c2c66affSColin Finck                         infoPtr->AccelVect[i].nSec, infoPtr->AccelVect[i].nInc);
1086c2c66affSColin Finck             }
1087c2c66affSColin Finck 
1088c2c66affSColin Finck     	    return TRUE;
1089c2c66affSColin Finck 	}
1090c2c66affSColin Finck 	case UDM_GETBASE:
1091c2c66affSColin Finck 	    return infoPtr->Base;
1092c2c66affSColin Finck 
1093c2c66affSColin Finck 	case UDM_SETBASE:
1094c2c66affSColin Finck 	    TRACE("UpDown Ctrl new base(%ld), hwnd=%p\n", wParam, hwnd);
1095c2c66affSColin Finck 	    if (wParam==10 || wParam==16) {
1096c2c66affSColin Finck 		WPARAM old_base = infoPtr->Base;
1097c2c66affSColin Finck 		infoPtr->Base = wParam;
1098c2c66affSColin Finck 
1099c2c66affSColin Finck 		if (old_base != infoPtr->Base)
1100c2c66affSColin Finck 		    UPDOWN_SetBuddyInt(infoPtr);
1101c2c66affSColin Finck 
1102c2c66affSColin Finck 		return old_base;
1103c2c66affSColin Finck 	    }
1104c2c66affSColin Finck 	    break;
1105c2c66affSColin Finck 
1106c2c66affSColin Finck 	case UDM_GETBUDDY:
1107c2c66affSColin Finck 	    return (LRESULT)infoPtr->Buddy;
1108c2c66affSColin Finck 
1109c2c66affSColin Finck 	case UDM_SETBUDDY:
1110c2c66affSColin Finck 	    return (LRESULT)UPDOWN_SetBuddy (infoPtr, (HWND)wParam);
1111c2c66affSColin Finck 
1112c2c66affSColin Finck 	case UDM_GETPOS:
1113c2c66affSColin Finck 	{
1114c2c66affSColin Finck             BOOL err;
1115c2c66affSColin Finck             int pos;
1116c2c66affSColin Finck 
1117c2c66affSColin Finck             pos = UPDOWN_GetPos(infoPtr, &err);
1118c2c66affSColin Finck             return MAKELONG(pos, err);
1119c2c66affSColin Finck 	}
1120c2c66affSColin Finck 	case UDM_SETPOS:
1121c2c66affSColin Finck 	{
1122c2c66affSColin Finck             return UPDOWN_SetPos(infoPtr, (short)LOWORD(lParam));
1123c2c66affSColin Finck 	}
1124c2c66affSColin Finck 	case UDM_GETRANGE:
1125c2c66affSColin Finck 	    return MAKELONG(infoPtr->MaxVal, infoPtr->MinVal);
1126c2c66affSColin Finck 
1127c2c66affSColin Finck 	case UDM_SETRANGE:
1128c2c66affSColin Finck 	    /* we must have:
1129c2c66affSColin Finck 	    UD_MINVAL <= Max <= UD_MAXVAL
1130c2c66affSColin Finck 	    UD_MINVAL <= Min <= UD_MAXVAL
1131c2c66affSColin Finck 	    |Max-Min| <= UD_MAXVAL */
1132c2c66affSColin Finck 	    UPDOWN_SetRange(infoPtr, (short)lParam, (short)HIWORD(lParam));
1133c2c66affSColin Finck 	    break;
1134c2c66affSColin Finck 
1135c2c66affSColin Finck 	case UDM_GETRANGE32:
1136c2c66affSColin Finck 	    if (wParam) *(LPINT)wParam = infoPtr->MinVal;
1137c2c66affSColin Finck 	    if (lParam) *(LPINT)lParam = infoPtr->MaxVal;
1138c2c66affSColin Finck 	    break;
1139c2c66affSColin Finck 
1140c2c66affSColin Finck 	case UDM_SETRANGE32:
1141c2c66affSColin Finck 	    UPDOWN_SetRange(infoPtr, (INT)lParam, (INT)wParam);
1142c2c66affSColin Finck 	    break;
1143c2c66affSColin Finck 
1144c2c66affSColin Finck 	case UDM_GETPOS32:
1145c2c66affSColin Finck 	{
1146c2c66affSColin Finck             return UPDOWN_GetPos(infoPtr, (BOOL*)lParam);
1147c2c66affSColin Finck 	}
1148c2c66affSColin Finck 	case UDM_SETPOS32:
1149c2c66affSColin Finck 	{
1150c2c66affSColin Finck             return UPDOWN_SetPos(infoPtr, (int)lParam);
1151c2c66affSColin Finck 	}
1152c2c66affSColin Finck 	case UDM_GETUNICODEFORMAT:
1153c2c66affSColin Finck 	    /* we lie a bit here, we're always using Unicode internally */
1154c2c66affSColin Finck 	    return infoPtr->UnicodeFormat;
1155c2c66affSColin Finck 
1156c2c66affSColin Finck 	case UDM_SETUNICODEFORMAT:
1157c2c66affSColin Finck 	{
1158c2c66affSColin Finck 	    /* do we really need to honour this flag? */
1159c2c66affSColin Finck 	    int temp = infoPtr->UnicodeFormat;
1160c2c66affSColin Finck 	    infoPtr->UnicodeFormat = (BOOL)wParam;
1161c2c66affSColin Finck 	    return temp;
1162c2c66affSColin Finck 	}
1163c2c66affSColin Finck 	default:
1164c2c66affSColin Finck 	    if ((message >= WM_USER) && (message < WM_APP) && !COMCTL32_IsReflectedMessage(message))
1165c2c66affSColin Finck 		ERR("unknown msg %04x wp=%04lx lp=%08lx\n", message, wParam, lParam);
1166c2c66affSColin Finck 	    return DefWindowProcW (hwnd, message, wParam, lParam);
1167c2c66affSColin Finck     }
1168c2c66affSColin Finck 
1169c2c66affSColin Finck     return 0;
1170c2c66affSColin Finck }
1171c2c66affSColin Finck 
1172c2c66affSColin Finck /***********************************************************************
1173c2c66affSColin Finck  *		UPDOWN_Register	[Internal]
1174c2c66affSColin Finck  *
1175c2c66affSColin Finck  * Registers the updown window class.
1176c2c66affSColin Finck  */
UPDOWN_Register(void)1177c2c66affSColin Finck void UPDOWN_Register(void)
1178c2c66affSColin Finck {
1179c2c66affSColin Finck     WNDCLASSW wndClass;
1180c2c66affSColin Finck 
1181c2c66affSColin Finck     ZeroMemory( &wndClass, sizeof( WNDCLASSW ) );
1182c2c66affSColin Finck     wndClass.style         = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
1183c2c66affSColin Finck     wndClass.lpfnWndProc   = UpDownWindowProc;
1184c2c66affSColin Finck     wndClass.cbClsExtra    = 0;
1185c2c66affSColin Finck     wndClass.cbWndExtra    = sizeof(UPDOWN_INFO*);
1186c2c66affSColin Finck     wndClass.hCursor       = LoadCursorW( 0, (LPWSTR)IDC_ARROW );
1187c2c66affSColin Finck     wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1188c2c66affSColin Finck     wndClass.lpszClassName = UPDOWN_CLASSW;
1189c2c66affSColin Finck 
1190c2c66affSColin Finck     RegisterClassW( &wndClass );
1191c2c66affSColin Finck }
1192c2c66affSColin Finck 
1193c2c66affSColin Finck 
1194c2c66affSColin Finck /***********************************************************************
1195c2c66affSColin Finck  *		UPDOWN_Unregister	[Internal]
1196c2c66affSColin Finck  *
1197c2c66affSColin Finck  * Unregisters the updown window class.
1198c2c66affSColin Finck  */
UPDOWN_Unregister(void)1199c2c66affSColin Finck void UPDOWN_Unregister (void)
1200c2c66affSColin Finck {
1201c2c66affSColin Finck     UnregisterClassW (UPDOWN_CLASSW, NULL);
1202c2c66affSColin Finck }
1203