4 * Copyright 1997 Dimitrie O. Paun
7 * - I am not sure about the default values for the Min, Max, Pos
8 * (in the UPDOWN_INFO the fields: MinVal, MaxVal, CurVal)
9 * - I think I do not handle correctly the WS_BORDER style.
10 * (Should be fixed. <ekohl@abo.rhein-zeitung.de>)
13 * Not much. The following have not been tested at all:
15 * - listbox as buddy window
18 * - UDS_ALIGNLEFT, ~UDS_WRAP
19 * (tested - they work)
20 * - integers with thousand separators.
21 * (fixed bugs. <noel@macadamian.com>)
23 * Even though the above list seems rather large, the control seems to
24 * behave very well so I am confident it does work in most (all) of the
27 * I do not like the arrows yet, I'll work more on them later on.
40 #include "debugtools.h"
42 DEFAULT_DEBUG_CHANNEL(updown
);
44 #define UPDOWN_BUDDYCLASSNAMELEN 40
48 UINT AccelCount
; /* Number of elements in AccelVect */
49 UDACCEL
* AccelVect
; /* Vector containing AccelCount elements */
50 INT Base
; /* Base to display nr in the buddy window */
51 INT CurVal
; /* Current up-down value */
52 INT MinVal
; /* Minimum up-down value */
53 INT MaxVal
; /* Maximum up-down value */
54 HWND Buddy
; /* Handle to the buddy window */
55 CHAR szBuddyClass
[UPDOWN_BUDDYCLASSNAMELEN
]; /* Buddy window class name */
56 INT Flags
; /* Internal Flags FLAG_* */
59 /* Control configuration constants */
61 #define INITIAL_DELAY 500 /* initial timer until auto-increment kicks in */
62 #define REPEAT_DELAY 50 /* delay between auto-increments */
64 #define DEFAULT_WIDTH 14 /* default width of the ctrl */
65 #define DEFAULT_XSEP 0 /* default separation between buddy and crtl */
66 #define DEFAULT_ADDTOP 0 /* amount to extend above the buddy window */
67 #define DEFAULT_ADDBOT 0 /* amount to extend below the buddy window */
68 #define DEFAULT_BUDDYBORDER 2 /* Width/height of the buddy border */
73 #define FLAG_INCR 0x01
74 #define FLAG_DECR 0x02
75 #define FLAG_MOUSEIN 0x04
76 #define FLAG_CLICKED (FLAG_INCR | FLAG_DECR)
80 #define BUDDY_UPDOWN_HWND "buddyUpDownHWND"
81 #define BUDDY_SUPERCLASS_WNDPROC "buddySupperClassWndProc"
83 static int accelIndex
= -1;
85 #define UNKNOWN_PARAM(msg, wParam, lParam) WARN(\
86 "UpDown Ctrl: Unknown parameter(s) for message " #msg \
87 "(%04x): wp=%04x lp=%08lx\n", msg, wParam, lParam);
89 #define UPDOWN_GetInfoPtr(hwnd) ((UPDOWN_INFO *)GetWindowLongA (hwnd,0))
91 static LRESULT CALLBACK
92 UPDOWN_Buddy_SubclassProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
);
94 /***********************************************************************
96 * Tests if a given value 'val' is between the Min&Max limits
98 static BOOL
UPDOWN_InBounds(HWND hwnd
, int val
)
100 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
102 if(infoPtr
->MaxVal
> infoPtr
->MinVal
)
103 return (infoPtr
->MinVal
<= val
) && (val
<= infoPtr
->MaxVal
);
105 return (infoPtr
->MaxVal
<= val
) && (val
<= infoPtr
->MinVal
);
108 /***********************************************************************
110 * Tests if we can change the current value by delta. If so, it changes
111 * it and returns TRUE. Else, it leaves it unchanged and returns FALSE.
113 static BOOL
UPDOWN_OffsetVal(HWND hwnd
, int delta
)
115 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
117 /* check if we can do the modification first */
118 if(!UPDOWN_InBounds (hwnd
, infoPtr
->CurVal
+delta
)){
119 if (GetWindowLongA (hwnd
, GWL_STYLE
) & UDS_WRAP
)
121 delta
+= (delta
< 0 ? -1 : 1) *
122 (infoPtr
->MaxVal
< infoPtr
->MinVal
? -1 : 1) *
123 (infoPtr
->MinVal
- infoPtr
->MaxVal
) +
124 (delta
< 0 ? 1 : -1);
130 infoPtr
->CurVal
+= delta
;
134 /***********************************************************************
135 * UPDOWN_HasBuddyBorder [Internal]
137 * When we have a buddy set and that we are aligned on our buddy, we
138 * want to draw a sunken edge to make like we are part of that control.
140 static BOOL
UPDOWN_HasBuddyBorder(HWND hwnd
)
142 UPDOWN_INFO
* infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
143 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
145 return ( ((dwStyle
& (UDS_ALIGNLEFT
| UDS_ALIGNRIGHT
)) != 0) &&
146 (SendMessageA(hwnd
, UDM_GETBUDDY
, 0, 0) != 0) &&
147 (lstrcmpiA(infoPtr
->szBuddyClass
, "EDIT") == 0 ) );
150 /***********************************************************************
151 * UPDOWN_GetArrowRect
152 * wndPtr - pointer to the up-down wnd
153 * rect - will hold the rectangle
154 * incr - TRUE get the "increment" rect (up or right)
155 * FALSE get the "decrement" rect (down or left)
158 static void UPDOWN_GetArrowRect (HWND hwnd
, RECT
*rect
, BOOL incr
)
160 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
161 int len
; /* will hold the width or height */
163 GetClientRect (hwnd
, rect
);
166 * Make sure we calculate the rectangle to fit even if we draw the
169 if (UPDOWN_HasBuddyBorder(hwnd
))
171 if (dwStyle
& UDS_ALIGNLEFT
)
172 rect
->left
+=DEFAULT_BUDDYBORDER
;
174 rect
->right
-=DEFAULT_BUDDYBORDER
;
176 InflateRect(rect
, 0, -DEFAULT_BUDDYBORDER
);
180 * We're calculating the midpoint to figure-out where the
181 * separation between the buttons will lay. We make sure that we
182 * round the uneven numbers by adding 1.
184 if (dwStyle
& UDS_HORZ
) {
185 len
= rect
->right
- rect
->left
+ 1; /* compute the width */
187 rect
->left
= rect
->left
+ len
/2;
189 rect
->right
= rect
->left
+ len
/2;
192 len
= rect
->bottom
- rect
->top
+ 1; /* compute the height */
194 rect
->bottom
= rect
->top
+ len
/2;
196 rect
->top
= rect
->top
+ len
/2;
200 /***********************************************************************
201 * UPDOWN_GetArrowFromPoint
202 * Returns the rectagle (for the up or down arrow) that contains pt.
203 * If it returns the up rect, it returns TRUE.
204 * If it returns the down rect, it returns FALSE.
207 UPDOWN_GetArrowFromPoint (HWND hwnd
, RECT
*rect
, POINT pt
)
209 UPDOWN_GetArrowRect (hwnd
, rect
, TRUE
);
210 if(PtInRect(rect
, pt
))
213 UPDOWN_GetArrowRect (hwnd
, rect
, FALSE
);
218 /***********************************************************************
219 * UPDOWN_GetThousandSep
220 * Returns the thousand sep. If an error occurs, it returns ','.
222 static char UPDOWN_GetThousandSep()
226 if(GetLocaleInfoA(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
,
227 sep
, sizeof(sep
)) != 1)
233 /***********************************************************************
235 * Tries to read the pos from the buddy window and if it succeeds,
236 * it stores it in the control's CurVal
238 * TRUE - if it read the integer from the buddy successfully
239 * FALSE - if an error occured
241 static BOOL
UPDOWN_GetBuddyInt (HWND hwnd
)
243 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
244 char txt
[20], sep
, *src
, *dst
;
247 if (!IsWindow(infoPtr
->Buddy
))
250 /*if the buddy is a list window, we must set curr index */
251 if (!lstrcmpA (infoPtr
->szBuddyClass
, "ListBox")){
252 newVal
= SendMessageA(infoPtr
->Buddy
, LB_GETCARETINDEX
, 0, 0);
257 /* we have a regular window, so will get the text */
258 if (!GetWindowTextA(infoPtr
->Buddy
, txt
, sizeof(txt
)))
261 sep
= UPDOWN_GetThousandSep();
263 /* now get rid of the separators */
264 for(src
= dst
= txt
; *src
; src
++)
269 /* try to convert the number and validate it */
270 newVal
= strtol(txt
, &src
, infoPtr
->Base
);
271 if(*src
|| !UPDOWN_InBounds (hwnd
, newVal
))
274 TRACE("new value(%d) read from buddy (old=%d)\n",
275 newVal
, infoPtr
->CurVal
);
278 infoPtr
->CurVal
= newVal
;
283 /***********************************************************************
285 * Tries to set the pos to the buddy window based on current pos
287 * TRUE - if it set the caption of the buddy successfully
288 * FALSE - if an error occured
290 static BOOL
UPDOWN_SetBuddyInt (HWND hwnd
)
292 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
296 if (!IsWindow(infoPtr
->Buddy
))
299 TRACE("set new value(%d) to buddy.\n",
302 /*if the buddy is a list window, we must set curr index */
303 if(!lstrcmpA (infoPtr
->szBuddyClass
, "ListBox")){
304 SendMessageA(infoPtr
->Buddy
, LB_SETCURSEL
, infoPtr
->CurVal
, 0);
306 else{ /* Regular window, so set caption to the number */
307 len
= sprintf(txt1
, (infoPtr
->Base
==16) ? "%X" : "%d", infoPtr
->CurVal
);
309 sep
= UPDOWN_GetThousandSep();
311 /* Do thousands seperation if necessary */
312 if (!(GetWindowLongA (hwnd
, GWL_STYLE
) & UDS_NOTHOUSANDS
) && (len
> 3)) {
313 char txt2
[20], *src
= txt1
, *dst
= txt2
;
315 lstrcpynA (dst
, src
, len
%3 + 1); /* need to include the null */
319 for(len
=0; *src
; len
++){
324 *dst
= 0; /* null terminate it */
325 strcpy(txt1
, txt2
); /* move it to the proper place */
327 SetWindowTextA(infoPtr
->Buddy
, txt1
);
333 /***********************************************************************
334 * UPDOWN_DrawBuddyBorder [Internal]
336 * When we have a buddy set and that we are aligned on our buddy, we
337 * want to draw a sunken edge to make like we are part of that control.
339 static void UPDOWN_DrawBuddyBorder (HWND hwnd
, HDC hdc
)
341 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
344 GetClientRect(hwnd
, &clientRect
);
346 if (dwStyle
& UDS_ALIGNLEFT
)
347 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_BOTTOM
| BF_LEFT
| BF_TOP
);
349 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_BOTTOM
| BF_RIGHT
| BF_TOP
);
352 /***********************************************************************
353 * UPDOWN_Draw [Internal]
355 * Draw the arrows. The background need not be erased.
357 static void UPDOWN_Draw (HWND hwnd
, HDC hdc
)
359 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
360 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
365 * Draw the common border between ourselves and our buddy.
367 if (UPDOWN_HasBuddyBorder(hwnd
))
368 UPDOWN_DrawBuddyBorder(hwnd
, hdc
);
370 /* Draw the incr button */
371 UPDOWN_GetArrowRect (hwnd
, &rect
, TRUE
);
372 prssed
= (infoPtr
->Flags
& FLAG_INCR
) && (infoPtr
->Flags
& FLAG_MOUSEIN
);
373 DrawFrameControl(hdc
, &rect
, DFC_SCROLL
,
374 (dwStyle
& UDS_HORZ
? DFCS_SCROLLRIGHT
: DFCS_SCROLLUP
) |
375 (prssed
? DFCS_PUSHED
: 0) |
376 (dwStyle
&WS_DISABLED
? DFCS_INACTIVE
: 0) );
378 /* Draw the space between the buttons */
379 rect
.top
= rect
.bottom
; rect
.bottom
++;
380 DrawEdge(hdc
, &rect
, 0, BF_MIDDLE
);
382 /* Draw the decr button */
383 UPDOWN_GetArrowRect(hwnd
, &rect
, FALSE
);
384 prssed
= (infoPtr
->Flags
& FLAG_DECR
) && (infoPtr
->Flags
& FLAG_MOUSEIN
);
385 DrawFrameControl(hdc
, &rect
, DFC_SCROLL
,
386 (dwStyle
& UDS_HORZ
? DFCS_SCROLLLEFT
: DFCS_SCROLLDOWN
) |
387 (prssed
? DFCS_PUSHED
: 0) |
388 (dwStyle
& WS_DISABLED
? DFCS_INACTIVE
: 0) );
391 /***********************************************************************
392 * UPDOWN_Refresh [Internal]
394 * Synchronous drawing (must NOT be used in WM_PAINT).
397 static void UPDOWN_Refresh (HWND hwnd
)
402 UPDOWN_Draw (hwnd
, hdc
);
403 ReleaseDC (hwnd
, hdc
);
407 /***********************************************************************
408 * UPDOWN_Paint [Internal]
410 * Asynchronous drawing (must ONLY be used in WM_PAINT).
413 static void UPDOWN_Paint (HWND hwnd
, HDC passedDC
)
419 hdc
= BeginPaint (hwnd
, &ps
);
421 UPDOWN_Draw (hwnd
, hdc
);
424 EndPaint (hwnd
, &ps
);
427 /***********************************************************************
429 * Tests if 'hwndBud' is a valid window handle. If not, returns FALSE.
430 * Else, sets it as a new Buddy.
431 * Then, it should subclass the buddy
432 * If window has the UDS_ARROWKEYS, it subcalsses the buddy window to
433 * process the UP/DOWN arrow keys.
434 * If window has the UDS_ALIGNLEFT or UDS_ALIGNRIGHT style
435 * the size/pos of the buddy and the control are adjusted accordingly.
437 static BOOL
UPDOWN_SetBuddy (HWND hwnd
, HWND hwndBud
)
439 UPDOWN_INFO
* infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
440 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
441 RECT budRect
; /* new coord for the buddy */
442 int x
,width
; /* new x position and width for the up-down */
443 WNDPROC baseWndProc
, currWndProc
;
445 /* Is it a valid bud? */
446 if(!IsWindow(hwndBud
))
449 /* there is already a body assigned */
450 if ( infoPtr
->Buddy
)
451 RemovePropA(infoPtr
->Buddy
, BUDDY_UPDOWN_HWND
);
453 /* Store buddy window handle */
454 infoPtr
->Buddy
= hwndBud
;
456 /* keep upDown ctrl hwnd in a buddy property */
457 SetPropA( hwndBud
, BUDDY_UPDOWN_HWND
, hwnd
);
459 /* Store buddy window clas name */
460 memset(infoPtr
->szBuddyClass
, 0, UPDOWN_BUDDYCLASSNAMELEN
);
461 GetClassNameA (hwndBud
, infoPtr
->szBuddyClass
, UPDOWN_BUDDYCLASSNAMELEN
-1);
463 if(dwStyle
& UDS_ARROWKEYS
){
464 /* Note that I don't clear the BUDDY_SUPERCLASS_WNDPROC property
465 when we reset the upDown ctrl buddy to another buddy because it is not
466 good to break the window proc chain. */
468 currWndProc
= (WNDPROC
) GetWindowLongA(hwndBud
, GWL_WNDPROC
);
469 if (currWndProc
!= UPDOWN_Buddy_SubclassProc
)
471 // replace the buddy's WndProc with ours
472 baseWndProc
= (WNDPROC
)SetWindowLongA(hwndBud
, GWL_WNDPROC
,
473 (LPARAM
)UPDOWN_Buddy_SubclassProc
);
474 // and save the base class' WndProc
475 SetPropA(hwndBud
, BUDDY_SUPERCLASS_WNDPROC
, (HANDLE
)baseWndProc
);
478 // its already been subclassed, don't overwrite BUDDY_SUPERCLASS_WNDPROC
481 /* do we need to do any adjustments? */
482 if(!(dwStyle
& (UDS_ALIGNLEFT
| UDS_ALIGNRIGHT
)))
485 /* Get the rect of the buddy relative to its parent */
486 GetWindowRect(infoPtr
->Buddy
, &budRect
);
487 MapWindowPoints(HWND_DESKTOP
, GetParent(infoPtr
->Buddy
),
488 (POINT
*)(&budRect
.left
), 2);
490 /* now do the positioning */
491 if(dwStyle
& UDS_ALIGNRIGHT
){
492 budRect
.right
-= DEFAULT_WIDTH
+DEFAULT_XSEP
;
493 x
= budRect
.right
+DEFAULT_XSEP
;
495 else{ /* UDS_ALIGNLEFT */
497 budRect
.left
+= DEFAULT_WIDTH
+DEFAULT_XSEP
;
500 /* first adjust the buddy to accomodate the up/down */
501 SetWindowPos(infoPtr
->Buddy
, 0, budRect
.left
, budRect
.top
,
502 budRect
.right
- budRect
.left
, budRect
.bottom
- budRect
.top
,
503 SWP_NOACTIVATE
|SWP_NOZORDER
);
505 /* now position the up/down */
506 /* Since the UDS_ALIGN* flags were used, */
507 /* we will pick the position and size of the window. */
508 width
= DEFAULT_WIDTH
;
511 * If the updown has a buddy border, it has to overlap with the buddy
512 * to look as if it is integrated with the buddy control.
513 * We nudge the control or change it size to overlap.
515 if (UPDOWN_HasBuddyBorder(hwnd
))
517 if(dwStyle
& UDS_ALIGNRIGHT
)
518 x
-=DEFAULT_BUDDYBORDER
;
520 width
+=DEFAULT_BUDDYBORDER
;
523 SetWindowPos (hwnd
, infoPtr
->Buddy
,
524 x
, budRect
.top
-DEFAULT_ADDTOP
,
525 width
, (budRect
.bottom
-budRect
.top
)+DEFAULT_ADDTOP
+DEFAULT_ADDBOT
,
531 /***********************************************************************
534 * This function increments/decrements the CurVal by the
535 * 'delta' amount according to the 'incr' flag
536 * It notifies the parent as required.
537 * It handles wraping and non-wraping correctly.
538 * It is assumed that delta>0
540 static void UPDOWN_DoAction (HWND hwnd
, int delta
, BOOL incr
)
542 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
543 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
546 TRACE("%s by %d\n", incr
? "inc" : "dec", delta
);
548 /* check if we can do the modification first */
549 delta
*= (incr
? 1 : -1) * (infoPtr
->MaxVal
< infoPtr
->MinVal
? -1 : 1);
551 /* We must notify parent now to obtain permission */
552 ni
.iPos
= infoPtr
->CurVal
;
554 ni
.hdr
.hwndFrom
= hwnd
;
555 ni
.hdr
.idFrom
= GetWindowLongA (hwnd
, GWL_ID
);
556 ni
.hdr
.code
= UDN_DELTAPOS
;
557 if (!SendMessageA(GetParent (hwnd
), WM_NOTIFY
,
558 (WPARAM
)ni
.hdr
.idFrom
, (LPARAM
)&ni
))
560 /* Parent said: OK to adjust */
562 /* Now adjust value with (maybe new) delta */
563 if (UPDOWN_OffsetVal (hwnd
, ni
.iDelta
))
565 /* Now take care about our buddy */
566 if(infoPtr
->Buddy
&& IsWindow(infoPtr
->Buddy
)
567 && (dwStyle
& UDS_SETBUDDYINT
) )
568 UPDOWN_SetBuddyInt (hwnd
);
572 /* Also, notify it. This message is sent in any case. */
573 SendMessageA (GetParent (hwnd
),
574 dwStyle
& UDS_HORZ
? WM_HSCROLL
: WM_VSCROLL
,
575 MAKELONG(SB_THUMBPOSITION
, infoPtr
->CurVal
), hwnd
);
578 /***********************************************************************
581 * Returns TRUE if it is enabled as well as its buddy (if any)
584 static BOOL
UPDOWN_IsEnabled (HWND hwnd
)
586 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
588 if(GetWindowLongA (hwnd
, GWL_STYLE
) & WS_DISABLED
)
591 return IsWindowEnabled(infoPtr
->Buddy
);
595 /***********************************************************************
598 * Deletes any timers, releases the mouse and does redraw if necessary.
599 * If the control is not in "capture" mode, it does nothing.
600 * If the control was not in cancel mode, it returns FALSE.
601 * If the control was in cancel mode, it returns TRUE.
603 static BOOL
UPDOWN_CancelMode (HWND hwnd
)
605 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
607 /* if not in 'capture' mode, do nothing */
608 if(!(infoPtr
->Flags
& FLAG_CLICKED
))
611 KillTimer (hwnd
, TIMERID1
); /* kill all possible timers */
612 KillTimer (hwnd
, TIMERID2
);
614 if (GetCapture() == hwnd
) /* let the mouse go */
615 ReleaseCapture(); /* if we still have it */
617 infoPtr
->Flags
= 0; /* get rid of any flags */
618 UPDOWN_Refresh (hwnd
); /* redraw the control just in case */
623 /***********************************************************************
624 * UPDOWN_HandleMouseEvent
626 * Handle a mouse event for the updown.
627 * 'pt' is the location of the mouse event in client or
628 * windows coordinates.
630 static void UPDOWN_HandleMouseEvent (HWND hwnd
, UINT msg
, POINT pt
)
632 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
633 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
639 case WM_LBUTTONDOWN
: /* Initialise mouse tracking */
640 /* If we are already in the 'clicked' mode, then nothing to do */
641 if(infoPtr
->Flags
& FLAG_CLICKED
)
644 /* If the buddy is an edit, will set focus to it */
645 if (!lstrcmpA (infoPtr
->szBuddyClass
, "Edit"))
646 SetFocus(infoPtr
->Buddy
);
648 /* Now see which one is the 'active' arrow */
649 temp
= UPDOWN_GetArrowFromPoint (hwnd
, &rect
, pt
);
651 /* Update the CurVal if necessary */
652 if (dwStyle
& UDS_SETBUDDYINT
)
653 UPDOWN_GetBuddyInt (hwnd
);
655 /* Set up the correct flags */
657 infoPtr
->Flags
|= temp
? FLAG_INCR
: FLAG_DECR
;
658 infoPtr
->Flags
|= FLAG_MOUSEIN
;
660 /* repaint the control */
661 UPDOWN_Refresh (hwnd
);
663 /* process the click */
664 UPDOWN_DoAction (hwnd
, 1, infoPtr
->Flags
& FLAG_INCR
);
666 /* now capture all mouse messages */
669 /* and startup the first timer */
670 SetTimer(hwnd
, TIMERID1
, INITIAL_DELAY
, 0);
674 /* If we are not in the 'clicked' mode, then nothing to do */
675 if(!(infoPtr
->Flags
& FLAG_CLICKED
))
678 /* save the flags to see if any got modified */
679 temp
= infoPtr
->Flags
;
681 /* Now get the 'active' arrow rectangle */
682 if (infoPtr
->Flags
& FLAG_INCR
)
683 UPDOWN_GetArrowRect (hwnd
, &rect
, TRUE
);
685 UPDOWN_GetArrowRect (hwnd
, &rect
, FALSE
);
687 /* Update the flags if we are in/out */
688 if(PtInRect(&rect
, pt
))
689 infoPtr
->Flags
|= FLAG_MOUSEIN
;
691 infoPtr
->Flags
&= ~FLAG_MOUSEIN
;
692 if(accelIndex
!= -1) /* if we have accel info */
693 accelIndex
= 0; /* reset it */
695 /* If state changed, redraw the control */
696 if(temp
!= infoPtr
->Flags
)
697 UPDOWN_Refresh (hwnd
);
701 ERR("Impossible case!\n");
706 /***********************************************************************
709 static LRESULT WINAPI
UpDownWindowProc(HWND hwnd
, UINT message
, WPARAM wParam
,
712 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
713 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
715 if (!infoPtr
&& (message
!= WM_CREATE
) && (message
!= WM_NCCREATE
))
716 return DefWindowProcA (hwnd
, message
, wParam
, lParam
);
720 /* get rid of border, if any */
721 SetWindowLongA (hwnd
, GWL_STYLE
, dwStyle
& ~WS_BORDER
);
725 infoPtr
= (UPDOWN_INFO
*)COMCTL32_Alloc (sizeof(UPDOWN_INFO
));
726 SetWindowLongA (hwnd
, 0, (DWORD
)infoPtr
);
728 /* initialize the info struct */
729 infoPtr
->AccelCount
=0; infoPtr
->AccelVect
=0;
730 infoPtr
->CurVal
=0; infoPtr
->MinVal
=0; infoPtr
->MaxVal
=100; /*FIXME*/
731 infoPtr
->Base
= 10; /* Default to base 10 */
732 infoPtr
->Buddy
= 0; /* No buddy window yet */
733 infoPtr
->Flags
= 0; /* And no flags */
735 /* Do we pick the buddy win ourselves? */
736 if (dwStyle
& UDS_AUTOBUDDY
)
737 UPDOWN_SetBuddy (hwnd
, GetWindow (hwnd
, GW_HWNDPREV
));
739 TRACE("UpDown Ctrl creation, hwnd=%04x\n", hwnd
);
743 if(infoPtr
->AccelVect
)
744 COMCTL32_Free (infoPtr
->AccelVect
);
746 if ( IsWindow(infoPtr
->Buddy
) ) /* Cleanup */
747 RemovePropA(infoPtr
->Buddy
, BUDDY_UPDOWN_HWND
);
749 COMCTL32_Free (infoPtr
);
750 SetWindowLongA (hwnd
, 0, 0);
751 TRACE("UpDown Ctrl destruction, hwnd=%04x\n", hwnd
);
755 if (dwStyle
& WS_DISABLED
)
756 UPDOWN_CancelMode (hwnd
);
758 UPDOWN_Refresh (hwnd
);
762 /* if initial timer, kill it and start the repeat timer */
763 if(wParam
== TIMERID1
){
764 KillTimer(hwnd
, TIMERID1
);
765 /* if no accel info given, used default timer */
766 if(infoPtr
->AccelCount
==0 || infoPtr
->AccelVect
==0){
771 accelIndex
= 0; /* otherwise, use it */
772 temp
= infoPtr
->AccelVect
[accelIndex
].nSec
* 1000 + 1;
774 SetTimer(hwnd
, TIMERID2
, temp
, 0);
777 /* now, if the mouse is above us, do the thing...*/
778 if(infoPtr
->Flags
& FLAG_MOUSEIN
){
779 temp
= accelIndex
==-1 ? 1 : infoPtr
->AccelVect
[accelIndex
].nInc
;
780 UPDOWN_DoAction(hwnd
, temp
, infoPtr
->Flags
& FLAG_INCR
);
782 if(accelIndex
!=-1 && accelIndex
< infoPtr
->AccelCount
-1){
783 KillTimer(hwnd
, TIMERID2
);
784 accelIndex
++; /* move to the next accel info */
785 temp
= infoPtr
->AccelVect
[accelIndex
].nSec
* 1000 + 1;
786 /* make sure we have at least 1ms intervals */
787 SetTimer(hwnd
, TIMERID2
, temp
, 0);
793 UPDOWN_CancelMode (hwnd
);
797 if(!UPDOWN_CancelMode(hwnd
))
800 SendMessageA(GetParent(hwnd
), dwStyle
& UDS_HORZ
? WM_HSCROLL
: WM_VSCROLL
,
801 MAKELONG(SB_ENDSCROLL
, infoPtr
->CurVal
), hwnd
);
803 /*If we released the mouse and our buddy is an edit */
804 /* we must select all text in it. */
805 if (!lstrcmpA (infoPtr
->szBuddyClass
, "Edit"))
806 SendMessageA(infoPtr
->Buddy
, EM_SETSEL
, 0, MAKELONG(0, -1));
811 if(UPDOWN_IsEnabled(hwnd
)){
813 pt
.x
= SLOWORD(lParam
);
814 pt
.y
= SHIWORD(lParam
);
815 UPDOWN_HandleMouseEvent (hwnd
, message
, pt
);
820 if((dwStyle
& UDS_ARROWKEYS
) && UPDOWN_IsEnabled(hwnd
)){
824 UPDOWN_GetBuddyInt (hwnd
);
825 UPDOWN_DoAction (hwnd
, 1, wParam
==VK_UP
);
832 UPDOWN_Paint (hwnd
, (HDC
)wParam
);
836 if (wParam
==0 && lParam
==0) /*if both zero, */
837 return infoPtr
->AccelCount
; /*just return the accel count*/
838 if (wParam
|| lParam
){
839 UNKNOWN_PARAM(UDM_GETACCEL
, wParam
, lParam
);
842 temp
= min(infoPtr
->AccelCount
, wParam
);
843 memcpy((void *)lParam
, infoPtr
->AccelVect
, temp
*sizeof(UDACCEL
));
847 TRACE("UpDown Ctrl new accel info, hwnd=%04x\n", hwnd
);
848 if(infoPtr
->AccelVect
){
849 COMCTL32_Free (infoPtr
->AccelVect
);
850 infoPtr
->AccelCount
= 0;
851 infoPtr
->AccelVect
= 0;
855 infoPtr
->AccelVect
= COMCTL32_Alloc (wParam
*sizeof(UDACCEL
));
856 if(infoPtr
->AccelVect
==0)
858 memcpy(infoPtr
->AccelVect
, (void*)lParam
, wParam
*sizeof(UDACCEL
));
862 if (wParam
|| lParam
)
863 UNKNOWN_PARAM(UDM_GETBASE
, wParam
, lParam
);
864 return infoPtr
->Base
;
867 TRACE("UpDown Ctrl new base(%d), hwnd=%04x\n",
869 if ( !(wParam
==10 || wParam
==16) || lParam
)
870 UNKNOWN_PARAM(UDM_SETBASE
, wParam
, lParam
);
871 if (wParam
==10 || wParam
==16){
872 temp
= infoPtr
->Base
;
873 infoPtr
->Base
= wParam
;
874 return temp
; /* return the prev base */
879 if (wParam
|| lParam
)
880 UNKNOWN_PARAM(UDM_GETBUDDY
, wParam
, lParam
);
881 return infoPtr
->Buddy
;
885 UNKNOWN_PARAM(UDM_SETBUDDY
, wParam
, lParam
);
886 temp
= infoPtr
->Buddy
;
887 UPDOWN_SetBuddy (hwnd
, wParam
);
888 TRACE("UpDown Ctrl new buddy(%04x), hwnd=%04x\n",
889 infoPtr
->Buddy
, hwnd
);
893 if (wParam
|| lParam
)
894 UNKNOWN_PARAM(UDM_GETPOS
, wParam
, lParam
);
895 temp
= UPDOWN_GetBuddyInt (hwnd
);
896 return MAKELONG(infoPtr
->CurVal
, temp
? 0 : 1);
899 if (wParam
|| HIWORD(lParam
))
900 UNKNOWN_PARAM(UDM_GETPOS
, wParam
, lParam
);
901 temp
= SLOWORD(lParam
);
902 TRACE("UpDown Ctrl new value(%d), hwnd=%04x\n",
904 if(!UPDOWN_InBounds(hwnd
, temp
)){
905 if(temp
< infoPtr
->MinVal
)
906 temp
= infoPtr
->MinVal
;
907 if(temp
> infoPtr
->MaxVal
)
908 temp
= infoPtr
->MaxVal
;
910 wParam
= infoPtr
->CurVal
; /* save prev value */
911 infoPtr
->CurVal
= temp
; /* set the new value */
912 if(dwStyle
& UDS_SETBUDDYINT
)
913 UPDOWN_SetBuddyInt (hwnd
);
914 return wParam
; /* return prev value */
917 if (wParam
|| lParam
)
918 UNKNOWN_PARAM(UDM_GETRANGE
, wParam
, lParam
);
919 return MAKELONG(infoPtr
->MaxVal
, infoPtr
->MinVal
);
923 UNKNOWN_PARAM(UDM_SETRANGE
, wParam
, lParam
); /* we must have: */
924 infoPtr
->MaxVal
= SLOWORD(lParam
); /* UD_MINVAL <= Max <= UD_MAXVAL */
925 infoPtr
->MinVal
= SHIWORD(lParam
); /* UD_MINVAL <= Min <= UD_MAXVAL */
926 /* |Max-Min| <= UD_MAXVAL */
927 TRACE("UpDown Ctrl new range(%d to %d), hwnd=%04x\n",
928 infoPtr
->MinVal
, infoPtr
->MaxVal
, hwnd
);
933 *(LPINT
)wParam
= infoPtr
->MinVal
;
935 *(LPINT
)lParam
= infoPtr
->MaxVal
;
939 infoPtr
->MinVal
= (INT
)wParam
;
940 infoPtr
->MaxVal
= (INT
)lParam
;
941 if (infoPtr
->MaxVal
<= infoPtr
->MinVal
)
942 infoPtr
->MaxVal
= infoPtr
->MinVal
+ 1;
943 TRACE("UpDown Ctrl new range(%d to %d), hwnd=%04x\n",
944 infoPtr
->MinVal
, infoPtr
->MaxVal
, hwnd
);
948 if (message
>= WM_USER
)
949 ERR("unknown msg %04x wp=%04x lp=%08lx\n",
950 message
, wParam
, lParam
);
951 return DefWindowProcA (hwnd
, message
, wParam
, lParam
);
957 /***********************************************************************
958 * UPDOWN_Buddy_SubclassProc used to handle messages sent to the buddy
962 UPDOWN_Buddy_SubclassProc (
968 WNDPROC superClassWndProc
= (WNDPROC
)GetPropA(hwnd
, BUDDY_SUPERCLASS_WNDPROC
);
969 TRACE("hwnd=%04x, wndProc=%d, uMsg=%04x, wParam=%d, lParam=%d\n",
970 hwnd
, (INT
)superClassWndProc
, uMsg
, wParam
, (UINT
)lParam
);
976 if ( ((int)wParam
== VK_UP
) || ((int)wParam
== VK_DOWN
) )
978 HWND upDownHwnd
= GetPropA(hwnd
, BUDDY_UPDOWN_HWND
);
979 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr(upDownHwnd
);
981 if (!lstrcmpA (infoPtr
->szBuddyClass
, "ListBox"))
983 /* if the buddy is a list window, we must update curr index */
984 INT oldVal
= SendMessageA(hwnd
, LB_GETCURSEL
, 0, 0);
985 SendMessageA(hwnd
, LB_SETCURSEL
, oldVal
+1, 0);
989 UPDOWN_GetBuddyInt(upDownHwnd
);
990 UPDOWN_DoAction(upDownHwnd
, 1, wParam
==VK_UP
);
995 /* else Fall Through */
998 return CallWindowProcA( superClassWndProc
, hwnd
, uMsg
, wParam
, lParam
);
1001 /***********************************************************************
1002 * UPDOWN_Register [Internal]
1004 * Registers the updown window class.
1008 UPDOWN_Register(void)
1012 ZeroMemory( &wndClass
, sizeof( WNDCLASSA
) );
1013 wndClass
.style
= CS_GLOBALCLASS
| CS_VREDRAW
;
1014 wndClass
.lpfnWndProc
= (WNDPROC
)UpDownWindowProc
;
1015 wndClass
.cbClsExtra
= 0;
1016 wndClass
.cbWndExtra
= sizeof(UPDOWN_INFO
*);
1017 wndClass
.hCursor
= LoadCursorA( 0, IDC_ARROWA
);
1018 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
1019 wndClass
.lpszClassName
= UPDOWN_CLASSA
;
1021 RegisterClassA( &wndClass
);
1025 /***********************************************************************
1026 * UPDOWN_Unregister [Internal]
1028 * Unregisters the updown window class.
1032 UPDOWN_Unregister (void)
1034 UnregisterClassA (UPDOWN_CLASSA
, (HINSTANCE
)NULL
);