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 /* Get the rect of the buddy relative to its parent */
482 GetWindowRect(infoPtr
->Buddy
, &budRect
);
483 MapWindowPoints(HWND_DESKTOP
, GetParent(infoPtr
->Buddy
),
484 (POINT
*)(&budRect
.left
), 2);
486 /* now do the positioning */
487 if (dwStyle
& UDS_ALIGNLEFT
) {
489 budRect
.left
+= DEFAULT_WIDTH
+DEFAULT_XSEP
;
491 else if (dwStyle
& UDS_ALIGNRIGHT
){
492 budRect
.right
-= DEFAULT_WIDTH
+DEFAULT_XSEP
;
493 x
= budRect
.right
+DEFAULT_XSEP
;
496 x
= budRect
.right
+DEFAULT_XSEP
;
499 /* first adjust the buddy to accomodate the up/down */
500 SetWindowPos(infoPtr
->Buddy
, 0, budRect
.left
, budRect
.top
,
501 budRect
.right
- budRect
.left
, budRect
.bottom
- budRect
.top
,
502 SWP_NOACTIVATE
|SWP_NOZORDER
);
504 /* now position the up/down */
505 /* Since the UDS_ALIGN* flags were used, */
506 /* we will pick the position and size of the window. */
507 width
= DEFAULT_WIDTH
;
510 * If the updown has a buddy border, it has to overlap with the buddy
511 * to look as if it is integrated with the buddy control.
512 * We nudge the control or change it size to overlap.
514 if (UPDOWN_HasBuddyBorder(hwnd
))
516 if(dwStyle
& UDS_ALIGNLEFT
)
517 width
+=DEFAULT_BUDDYBORDER
;
519 x
-=DEFAULT_BUDDYBORDER
;
522 SetWindowPos (hwnd
, infoPtr
->Buddy
,
523 x
, budRect
.top
-DEFAULT_ADDTOP
,
524 width
, (budRect
.bottom
-budRect
.top
)+DEFAULT_ADDTOP
+DEFAULT_ADDBOT
,
530 /***********************************************************************
533 * This function increments/decrements the CurVal by the
534 * 'delta' amount according to the 'incr' flag
535 * It notifies the parent as required.
536 * It handles wraping and non-wraping correctly.
537 * It is assumed that delta>0
539 static void UPDOWN_DoAction (HWND hwnd
, int delta
, BOOL incr
)
541 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
542 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
545 TRACE("%s by %d\n", incr
? "inc" : "dec", delta
);
547 /* check if we can do the modification first */
548 delta
*= (incr
? 1 : -1) * (infoPtr
->MaxVal
< infoPtr
->MinVal
? -1 : 1);
550 /* We must notify parent now to obtain permission */
551 ni
.iPos
= infoPtr
->CurVal
;
553 ni
.hdr
.hwndFrom
= hwnd
;
554 ni
.hdr
.idFrom
= GetWindowLongA (hwnd
, GWL_ID
);
555 ni
.hdr
.code
= UDN_DELTAPOS
;
556 if (!SendMessageA(GetParent (hwnd
), WM_NOTIFY
,
557 (WPARAM
)ni
.hdr
.idFrom
, (LPARAM
)&ni
))
559 /* Parent said: OK to adjust */
561 /* Now adjust value with (maybe new) delta */
562 if (UPDOWN_OffsetVal (hwnd
, ni
.iDelta
))
564 /* Now take care about our buddy */
565 if(infoPtr
->Buddy
&& IsWindow(infoPtr
->Buddy
)
566 && (dwStyle
& UDS_SETBUDDYINT
) )
567 UPDOWN_SetBuddyInt (hwnd
);
571 /* Also, notify it. This message is sent in any case. */
572 SendMessageA (GetParent (hwnd
),
573 dwStyle
& UDS_HORZ
? WM_HSCROLL
: WM_VSCROLL
,
574 MAKELONG(SB_THUMBPOSITION
, infoPtr
->CurVal
), hwnd
);
577 /***********************************************************************
580 * Returns TRUE if it is enabled as well as its buddy (if any)
583 static BOOL
UPDOWN_IsEnabled (HWND hwnd
)
585 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
587 if(GetWindowLongA (hwnd
, GWL_STYLE
) & WS_DISABLED
)
590 return IsWindowEnabled(infoPtr
->Buddy
);
594 /***********************************************************************
597 * Deletes any timers, releases the mouse and does redraw if necessary.
598 * If the control is not in "capture" mode, it does nothing.
599 * If the control was not in cancel mode, it returns FALSE.
600 * If the control was in cancel mode, it returns TRUE.
602 static BOOL
UPDOWN_CancelMode (HWND hwnd
)
604 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
606 /* if not in 'capture' mode, do nothing */
607 if(!(infoPtr
->Flags
& FLAG_CLICKED
))
610 KillTimer (hwnd
, TIMERID1
); /* kill all possible timers */
611 KillTimer (hwnd
, TIMERID2
);
613 if (GetCapture() == hwnd
) /* let the mouse go */
614 ReleaseCapture(); /* if we still have it */
616 infoPtr
->Flags
= 0; /* get rid of any flags */
617 UPDOWN_Refresh (hwnd
); /* redraw the control just in case */
622 /***********************************************************************
623 * UPDOWN_HandleMouseEvent
625 * Handle a mouse event for the updown.
626 * 'pt' is the location of the mouse event in client or
627 * windows coordinates.
629 static void UPDOWN_HandleMouseEvent (HWND hwnd
, UINT msg
, POINT pt
)
631 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
632 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
638 case WM_LBUTTONDOWN
: /* Initialise mouse tracking */
639 /* If we are already in the 'clicked' mode, then nothing to do */
640 if(infoPtr
->Flags
& FLAG_CLICKED
)
643 /* If the buddy is an edit, will set focus to it */
644 if (!lstrcmpA (infoPtr
->szBuddyClass
, "Edit"))
645 SetFocus(infoPtr
->Buddy
);
647 /* Now see which one is the 'active' arrow */
648 temp
= UPDOWN_GetArrowFromPoint (hwnd
, &rect
, pt
);
650 /* Update the CurVal if necessary */
651 if (dwStyle
& UDS_SETBUDDYINT
)
652 UPDOWN_GetBuddyInt (hwnd
);
654 /* Set up the correct flags */
656 infoPtr
->Flags
|= temp
? FLAG_INCR
: FLAG_DECR
;
657 infoPtr
->Flags
|= FLAG_MOUSEIN
;
659 /* repaint the control */
660 UPDOWN_Refresh (hwnd
);
662 /* process the click */
663 UPDOWN_DoAction (hwnd
, 1, infoPtr
->Flags
& FLAG_INCR
);
665 /* now capture all mouse messages */
668 /* and startup the first timer */
669 SetTimer(hwnd
, TIMERID1
, INITIAL_DELAY
, 0);
673 /* If we are not in the 'clicked' mode, then nothing to do */
674 if(!(infoPtr
->Flags
& FLAG_CLICKED
))
677 /* save the flags to see if any got modified */
678 temp
= infoPtr
->Flags
;
680 /* Now get the 'active' arrow rectangle */
681 if (infoPtr
->Flags
& FLAG_INCR
)
682 UPDOWN_GetArrowRect (hwnd
, &rect
, TRUE
);
684 UPDOWN_GetArrowRect (hwnd
, &rect
, FALSE
);
686 /* Update the flags if we are in/out */
687 if(PtInRect(&rect
, pt
))
688 infoPtr
->Flags
|= FLAG_MOUSEIN
;
690 infoPtr
->Flags
&= ~FLAG_MOUSEIN
;
691 if(accelIndex
!= -1) /* if we have accel info */
692 accelIndex
= 0; /* reset it */
694 /* If state changed, redraw the control */
695 if(temp
!= infoPtr
->Flags
)
696 UPDOWN_Refresh (hwnd
);
700 ERR("Impossible case!\n");
705 /***********************************************************************
708 static LRESULT WINAPI
UpDownWindowProc(HWND hwnd
, UINT message
, WPARAM wParam
,
711 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
712 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
714 if (!infoPtr
&& (message
!= WM_CREATE
) && (message
!= WM_NCCREATE
))
715 return DefWindowProcA (hwnd
, message
, wParam
, lParam
);
719 /* get rid of border, if any */
720 SetWindowLongA (hwnd
, GWL_STYLE
, dwStyle
& ~WS_BORDER
);
724 infoPtr
= (UPDOWN_INFO
*)COMCTL32_Alloc (sizeof(UPDOWN_INFO
));
725 SetWindowLongA (hwnd
, 0, (DWORD
)infoPtr
);
727 /* initialize the info struct */
728 infoPtr
->AccelCount
=0; infoPtr
->AccelVect
=0;
729 infoPtr
->CurVal
=0; infoPtr
->MinVal
=0; infoPtr
->MaxVal
=100; /*FIXME*/
730 infoPtr
->Base
= 10; /* Default to base 10 */
731 infoPtr
->Buddy
= 0; /* No buddy window yet */
732 infoPtr
->Flags
= 0; /* And no flags */
734 /* Do we pick the buddy win ourselves? */
735 if (dwStyle
& UDS_AUTOBUDDY
)
736 UPDOWN_SetBuddy (hwnd
, GetWindow (hwnd
, GW_HWNDPREV
));
738 TRACE("UpDown Ctrl creation, hwnd=%04x\n", hwnd
);
742 if(infoPtr
->AccelVect
)
743 COMCTL32_Free (infoPtr
->AccelVect
);
745 if ( IsWindow(infoPtr
->Buddy
) ) /* Cleanup */
746 RemovePropA(infoPtr
->Buddy
, BUDDY_UPDOWN_HWND
);
748 COMCTL32_Free (infoPtr
);
749 SetWindowLongA (hwnd
, 0, 0);
750 TRACE("UpDown Ctrl destruction, hwnd=%04x\n", hwnd
);
754 if (dwStyle
& WS_DISABLED
)
755 UPDOWN_CancelMode (hwnd
);
757 UPDOWN_Refresh (hwnd
);
761 /* if initial timer, kill it and start the repeat timer */
762 if(wParam
== TIMERID1
){
763 KillTimer(hwnd
, TIMERID1
);
764 /* if no accel info given, used default timer */
765 if(infoPtr
->AccelCount
==0 || infoPtr
->AccelVect
==0){
770 accelIndex
= 0; /* otherwise, use it */
771 temp
= infoPtr
->AccelVect
[accelIndex
].nSec
* 1000 + 1;
773 SetTimer(hwnd
, TIMERID2
, temp
, 0);
776 /* now, if the mouse is above us, do the thing...*/
777 if(infoPtr
->Flags
& FLAG_MOUSEIN
){
778 temp
= accelIndex
==-1 ? 1 : infoPtr
->AccelVect
[accelIndex
].nInc
;
779 UPDOWN_DoAction(hwnd
, temp
, infoPtr
->Flags
& FLAG_INCR
);
781 if(accelIndex
!=-1 && accelIndex
< infoPtr
->AccelCount
-1){
782 KillTimer(hwnd
, TIMERID2
);
783 accelIndex
++; /* move to the next accel info */
784 temp
= infoPtr
->AccelVect
[accelIndex
].nSec
* 1000 + 1;
785 /* make sure we have at least 1ms intervals */
786 SetTimer(hwnd
, TIMERID2
, temp
, 0);
792 UPDOWN_CancelMode (hwnd
);
796 if(!UPDOWN_CancelMode(hwnd
))
799 SendMessageA(GetParent(hwnd
), dwStyle
& UDS_HORZ
? WM_HSCROLL
: WM_VSCROLL
,
800 MAKELONG(SB_ENDSCROLL
, infoPtr
->CurVal
), hwnd
);
802 /*If we released the mouse and our buddy is an edit */
803 /* we must select all text in it. */
804 if (!lstrcmpA (infoPtr
->szBuddyClass
, "Edit"))
805 SendMessageA(infoPtr
->Buddy
, EM_SETSEL
, 0, MAKELONG(0, -1));
810 if(UPDOWN_IsEnabled(hwnd
)){
812 pt
.x
= SLOWORD(lParam
);
813 pt
.y
= SHIWORD(lParam
);
814 UPDOWN_HandleMouseEvent (hwnd
, message
, pt
);
819 if((dwStyle
& UDS_ARROWKEYS
) && UPDOWN_IsEnabled(hwnd
)){
823 UPDOWN_GetBuddyInt (hwnd
);
824 /* Fixme: Paint the according button pressed for some time, like win95 does*/
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
);