4 * Copyright 1997 Dimitrie O. Paun
7 * - I think I do not handle correctly the WS_BORDER style.
8 * (Should be fixed. <ekohl@abo.rhein-zeitung.de>)
11 * Not much. The following have not been tested at all:
13 * - listbox as buddy window
16 * - integers with thousand separators.
17 * (fixed bugs. <noel@macadamian.com>)
19 * Even though the above list seems rather large, the control seems to
20 * behave very well so I am confident it does work in most (all) of the
34 #include "debugtools.h"
36 DEFAULT_DEBUG_CHANNEL(updown
);
38 #define UPDOWN_BUDDYCLASSNAMELEN 40
42 UINT AccelCount
; /* Number of elements in AccelVect */
43 UDACCEL
* AccelVect
; /* Vector containing AccelCount elements */
44 INT Base
; /* Base to display nr in the buddy window */
45 INT CurVal
; /* Current up-down value */
46 INT MinVal
; /* Minimum up-down value */
47 INT MaxVal
; /* Maximum up-down value */
48 HWND Buddy
; /* Handle to the buddy window */
49 CHAR szBuddyClass
[UPDOWN_BUDDYCLASSNAMELEN
]; /* Buddy window class name */
50 INT Flags
; /* Internal Flags FLAG_* */
53 /* Control configuration constants */
55 #define INITIAL_DELAY 500 /* initial timer until auto-increment kicks in */
56 #define REPEAT_DELAY 50 /* delay between auto-increments */
58 #define DEFAULT_WIDTH 14 /* default width of the ctrl */
59 #define DEFAULT_XSEP 0 /* default separation between buddy and crtl */
60 #define DEFAULT_ADDTOP 0 /* amount to extend above the buddy window */
61 #define DEFAULT_ADDBOT 0 /* amount to extend below the buddy window */
62 #define DEFAULT_BUDDYBORDER 2 /* Width/height of the buddy border */
67 #define FLAG_INCR 0x01
68 #define FLAG_DECR 0x02
69 #define FLAG_MOUSEIN 0x04
70 #define FLAG_CLICKED (FLAG_INCR | FLAG_DECR)
74 #define BUDDY_UPDOWN_HWND "buddyUpDownHWND"
75 #define BUDDY_SUPERCLASS_WNDPROC "buddySupperClassWndProc"
77 static int accelIndex
= -1;
79 #define UNKNOWN_PARAM(msg, wParam, lParam) WARN(\
80 "Unknown parameter(s) for message " #msg \
81 "(%04x): wp=%04x lp=%08lx\n", msg, wParam, lParam);
83 #define UPDOWN_GetInfoPtr(hwnd) ((UPDOWN_INFO *)GetWindowLongA (hwnd,0))
85 static LRESULT CALLBACK
86 UPDOWN_Buddy_SubclassProc (HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
);
88 /***********************************************************************
90 * Tests if a given value 'val' is between the Min&Max limits
92 static BOOL
UPDOWN_InBounds(HWND hwnd
, int val
)
94 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
96 if(infoPtr
->MaxVal
> infoPtr
->MinVal
)
97 return (infoPtr
->MinVal
<= val
) && (val
<= infoPtr
->MaxVal
);
99 return (infoPtr
->MaxVal
<= val
) && (val
<= infoPtr
->MinVal
);
102 /***********************************************************************
104 * Tests if we can change the current value by delta. If so, it changes
105 * it and returns TRUE. Else, it leaves it unchanged and returns FALSE.
107 static BOOL
UPDOWN_OffsetVal(HWND hwnd
, int delta
)
109 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
111 /* check if we can do the modification first */
112 if(!UPDOWN_InBounds (hwnd
, infoPtr
->CurVal
+delta
)){
113 if (GetWindowLongA (hwnd
, GWL_STYLE
) & UDS_WRAP
)
115 delta
+= (delta
< 0 ? -1 : 1) *
116 (infoPtr
->MaxVal
< infoPtr
->MinVal
? -1 : 1) *
117 (infoPtr
->MinVal
- infoPtr
->MaxVal
) +
118 (delta
< 0 ? 1 : -1);
124 infoPtr
->CurVal
+= delta
;
128 /***********************************************************************
129 * UPDOWN_HasBuddyBorder [Internal]
131 * When we have a buddy set and that we are aligned on our buddy, we
132 * want to draw a sunken edge to make like we are part of that control.
134 static BOOL
UPDOWN_HasBuddyBorder(HWND hwnd
)
136 UPDOWN_INFO
* infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
137 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
139 return ( ((dwStyle
& (UDS_ALIGNLEFT
| UDS_ALIGNRIGHT
)) != 0) &&
140 (SendMessageA(hwnd
, UDM_GETBUDDY
, 0, 0) != 0) &&
141 (lstrcmpiA(infoPtr
->szBuddyClass
, "EDIT") == 0 ) );
144 /***********************************************************************
145 * UPDOWN_GetArrowRect
146 * wndPtr - pointer to the up-down wnd
147 * rect - will hold the rectangle
148 * incr - TRUE get the "increment" rect (up or right)
149 * FALSE get the "decrement" rect (down or left)
152 static void UPDOWN_GetArrowRect (HWND hwnd
, RECT
*rect
, BOOL incr
)
154 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
155 int len
; /* will hold the width or height */
157 GetClientRect (hwnd
, rect
);
160 * Make sure we calculate the rectangle to fit even if we draw the
163 if (UPDOWN_HasBuddyBorder(hwnd
))
165 if (dwStyle
& UDS_ALIGNLEFT
)
166 rect
->left
+=DEFAULT_BUDDYBORDER
;
168 rect
->right
-=DEFAULT_BUDDYBORDER
;
170 InflateRect(rect
, 0, -DEFAULT_BUDDYBORDER
);
174 * We're calculating the midpoint to figure-out where the
175 * separation between the buttons will lay. We make sure that we
176 * round the uneven numbers by adding 1.
178 if (dwStyle
& UDS_HORZ
) {
179 len
= rect
->right
- rect
->left
+ 1; /* compute the width */
181 rect
->left
= rect
->left
+ len
/2;
183 rect
->right
= rect
->left
+ len
/2;
186 len
= rect
->bottom
- rect
->top
+ 1; /* compute the height */
188 rect
->bottom
= rect
->top
+ len
/2;
190 rect
->top
= rect
->top
+ len
/2;
194 /***********************************************************************
195 * UPDOWN_GetArrowFromPoint
196 * Returns the rectagle (for the up or down arrow) that contains pt.
197 * If it returns the up rect, it returns TRUE.
198 * If it returns the down rect, it returns FALSE.
201 UPDOWN_GetArrowFromPoint (HWND hwnd
, RECT
*rect
, POINT pt
)
203 UPDOWN_GetArrowRect (hwnd
, rect
, TRUE
);
204 if(PtInRect(rect
, pt
))
207 UPDOWN_GetArrowRect (hwnd
, rect
, FALSE
);
212 /***********************************************************************
213 * UPDOWN_GetThousandSep
214 * Returns the thousand sep. If an error occurs, it returns ','.
216 static char UPDOWN_GetThousandSep()
220 if(GetLocaleInfoA(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
,
221 sep
, sizeof(sep
)) != 1)
227 /***********************************************************************
229 * Tries to read the pos from the buddy window and if it succeeds,
230 * it stores it in the control's CurVal
232 * TRUE - if it read the integer from the buddy successfully
233 * FALSE - if an error occured
235 static BOOL
UPDOWN_GetBuddyInt (HWND hwnd
)
237 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
238 char txt
[20], sep
, *src
, *dst
;
241 if (!IsWindow(infoPtr
->Buddy
))
244 /*if the buddy is a list window, we must set curr index */
245 if (!lstrcmpA (infoPtr
->szBuddyClass
, "ListBox")){
246 newVal
= SendMessageA(infoPtr
->Buddy
, LB_GETCARETINDEX
, 0, 0);
251 /* we have a regular window, so will get the text */
252 if (!GetWindowTextA(infoPtr
->Buddy
, txt
, sizeof(txt
)))
255 sep
= UPDOWN_GetThousandSep();
257 /* now get rid of the separators */
258 for(src
= dst
= txt
; *src
; src
++)
263 /* try to convert the number and validate it */
264 newVal
= strtol(txt
, &src
, infoPtr
->Base
);
265 if(*src
|| !UPDOWN_InBounds (hwnd
, newVal
))
268 TRACE("new value(%d) read from buddy (old=%d)\n",
269 newVal
, infoPtr
->CurVal
);
272 infoPtr
->CurVal
= newVal
;
277 /***********************************************************************
279 * Tries to set the pos to the buddy window based on current pos
281 * TRUE - if it set the caption of the buddy successfully
282 * FALSE - if an error occured
284 static BOOL
UPDOWN_SetBuddyInt (HWND hwnd
)
286 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
290 if (!IsWindow(infoPtr
->Buddy
))
293 TRACE("set new value(%d) to buddy.\n",
296 /*if the buddy is a list window, we must set curr index */
297 if(!lstrcmpA (infoPtr
->szBuddyClass
, "ListBox")){
298 SendMessageA(infoPtr
->Buddy
, LB_SETCURSEL
, infoPtr
->CurVal
, 0);
300 else{ /* Regular window, so set caption to the number */
301 len
= sprintf(txt1
, (infoPtr
->Base
==16) ? "%X" : "%d", infoPtr
->CurVal
);
303 sep
= UPDOWN_GetThousandSep();
305 /* Do thousands seperation if necessary */
306 if (!(GetWindowLongA (hwnd
, GWL_STYLE
) & UDS_NOTHOUSANDS
) && (len
> 3)) {
307 char txt2
[20], *src
= txt1
, *dst
= txt2
;
309 lstrcpynA (dst
, src
, len
%3 + 1); /* need to include the null */
313 for(len
=0; *src
; len
++){
318 *dst
= 0; /* null terminate it */
319 strcpy(txt1
, txt2
); /* move it to the proper place */
321 SetWindowTextA(infoPtr
->Buddy
, txt1
);
327 /***********************************************************************
328 * UPDOWN_DrawBuddyBorder [Internal]
330 * When we have a buddy set and that we are aligned on our buddy, we
331 * want to draw a sunken edge to make like we are part of that control.
333 static void UPDOWN_DrawBuddyBorder (HWND hwnd
, HDC hdc
)
335 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
338 GetClientRect(hwnd
, &clientRect
);
340 if (dwStyle
& UDS_ALIGNLEFT
)
341 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_BOTTOM
| BF_LEFT
| BF_TOP
);
343 DrawEdge(hdc
, &clientRect
, EDGE_SUNKEN
, BF_BOTTOM
| BF_RIGHT
| BF_TOP
);
346 /***********************************************************************
347 * UPDOWN_Draw [Internal]
349 * Draw the arrows. The background need not be erased.
351 static void UPDOWN_Draw (HWND hwnd
, HDC hdc
)
353 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
354 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
359 * Draw the common border between ourselves and our buddy.
361 if (UPDOWN_HasBuddyBorder(hwnd
))
362 UPDOWN_DrawBuddyBorder(hwnd
, hdc
);
364 /* Draw the incr button */
365 UPDOWN_GetArrowRect (hwnd
, &rect
, TRUE
);
366 prssed
= (infoPtr
->Flags
& FLAG_INCR
) && (infoPtr
->Flags
& FLAG_MOUSEIN
);
367 DrawFrameControl(hdc
, &rect
, DFC_SCROLL
,
368 (dwStyle
& UDS_HORZ
? DFCS_SCROLLRIGHT
: DFCS_SCROLLUP
) |
369 (prssed
? DFCS_PUSHED
: 0) |
370 (dwStyle
&WS_DISABLED
? DFCS_INACTIVE
: 0) );
372 /* Draw the space between the buttons */
373 rect
.top
= rect
.bottom
; rect
.bottom
++;
374 DrawEdge(hdc
, &rect
, 0, BF_MIDDLE
);
376 /* Draw the decr button */
377 UPDOWN_GetArrowRect(hwnd
, &rect
, FALSE
);
378 prssed
= (infoPtr
->Flags
& FLAG_DECR
) && (infoPtr
->Flags
& FLAG_MOUSEIN
);
379 DrawFrameControl(hdc
, &rect
, DFC_SCROLL
,
380 (dwStyle
& UDS_HORZ
? DFCS_SCROLLLEFT
: DFCS_SCROLLDOWN
) |
381 (prssed
? DFCS_PUSHED
: 0) |
382 (dwStyle
& WS_DISABLED
? DFCS_INACTIVE
: 0) );
385 /***********************************************************************
386 * UPDOWN_Refresh [Internal]
388 * Synchronous drawing (must NOT be used in WM_PAINT).
391 static void UPDOWN_Refresh (HWND hwnd
)
396 UPDOWN_Draw (hwnd
, hdc
);
397 ReleaseDC (hwnd
, hdc
);
401 /***********************************************************************
402 * UPDOWN_Paint [Internal]
404 * Asynchronous drawing (must ONLY be used in WM_PAINT).
407 static void UPDOWN_Paint (HWND hwnd
, HDC passedDC
)
413 hdc
= BeginPaint (hwnd
, &ps
);
415 UPDOWN_Draw (hwnd
, hdc
);
418 EndPaint (hwnd
, &ps
);
421 /***********************************************************************
423 * Tests if 'hwndBud' is a valid window handle. If not, returns FALSE.
424 * Else, sets it as a new Buddy.
425 * Then, it should subclass the buddy
426 * If window has the UDS_ARROWKEYS, it subcalsses the buddy window to
427 * process the UP/DOWN arrow keys.
428 * If window has the UDS_ALIGNLEFT or UDS_ALIGNRIGHT style
429 * the size/pos of the buddy and the control are adjusted accordingly.
431 static BOOL
UPDOWN_SetBuddy (HWND hwnd
, HWND hwndBud
)
433 UPDOWN_INFO
* infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
434 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
435 RECT budRect
; /* new coord for the buddy */
436 int x
,width
; /* new x position and width for the up-down */
437 WNDPROC baseWndProc
, currWndProc
;
439 /* Is it a valid bud? */
440 if(!IsWindow(hwndBud
))
443 /* there is already a body assigned */
444 if ( infoPtr
->Buddy
)
445 RemovePropA(infoPtr
->Buddy
, BUDDY_UPDOWN_HWND
);
447 /* Store buddy window handle */
448 infoPtr
->Buddy
= hwndBud
;
450 /* keep upDown ctrl hwnd in a buddy property */
451 SetPropA( hwndBud
, BUDDY_UPDOWN_HWND
, hwnd
);
453 /* Store buddy window clas name */
454 memset(infoPtr
->szBuddyClass
, 0, UPDOWN_BUDDYCLASSNAMELEN
);
455 GetClassNameA (hwndBud
, infoPtr
->szBuddyClass
, UPDOWN_BUDDYCLASSNAMELEN
-1);
457 if(dwStyle
& UDS_ARROWKEYS
){
458 /* Note that I don't clear the BUDDY_SUPERCLASS_WNDPROC property
459 when we reset the upDown ctrl buddy to another buddy because it is not
460 good to break the window proc chain. */
462 currWndProc
= (WNDPROC
) GetWindowLongA(hwndBud
, GWL_WNDPROC
);
463 if (currWndProc
!= UPDOWN_Buddy_SubclassProc
)
465 // replace the buddy's WndProc with ours
466 baseWndProc
= (WNDPROC
)SetWindowLongA(hwndBud
, GWL_WNDPROC
,
467 (LPARAM
)UPDOWN_Buddy_SubclassProc
);
468 // and save the base class' WndProc
469 SetPropA(hwndBud
, BUDDY_SUPERCLASS_WNDPROC
, (HANDLE
)baseWndProc
);
472 // its already been subclassed, don't overwrite BUDDY_SUPERCLASS_WNDPROC
475 /* Get the rect of the buddy relative to its parent */
476 GetWindowRect(infoPtr
->Buddy
, &budRect
);
477 MapWindowPoints(HWND_DESKTOP
, GetParent(infoPtr
->Buddy
),
478 (POINT
*)(&budRect
.left
), 2);
480 /* now do the positioning */
481 if (dwStyle
& UDS_ALIGNLEFT
) {
483 budRect
.left
+= DEFAULT_WIDTH
+DEFAULT_XSEP
;
485 else if (dwStyle
& UDS_ALIGNRIGHT
){
486 budRect
.right
-= DEFAULT_WIDTH
+DEFAULT_XSEP
;
487 x
= budRect
.right
+DEFAULT_XSEP
;
490 x
= budRect
.right
+DEFAULT_XSEP
;
493 /* first adjust the buddy to accomodate the up/down */
494 SetWindowPos(infoPtr
->Buddy
, 0, budRect
.left
, budRect
.top
,
495 budRect
.right
- budRect
.left
, budRect
.bottom
- budRect
.top
,
496 SWP_NOACTIVATE
|SWP_NOZORDER
);
498 /* now position the up/down */
499 /* Since the UDS_ALIGN* flags were used, */
500 /* we will pick the position and size of the window. */
501 width
= DEFAULT_WIDTH
;
504 * If the updown has a buddy border, it has to overlap with the buddy
505 * to look as if it is integrated with the buddy control.
506 * We nudge the control or change it size to overlap.
508 if (UPDOWN_HasBuddyBorder(hwnd
))
510 if(dwStyle
& UDS_ALIGNLEFT
)
511 width
+=DEFAULT_BUDDYBORDER
;
513 x
-=DEFAULT_BUDDYBORDER
;
516 SetWindowPos (hwnd
, infoPtr
->Buddy
,
517 x
, budRect
.top
-DEFAULT_ADDTOP
,
518 width
, (budRect
.bottom
-budRect
.top
)+DEFAULT_ADDTOP
+DEFAULT_ADDBOT
,
524 /***********************************************************************
527 * This function increments/decrements the CurVal by the
528 * 'delta' amount according to the 'incr' flag
529 * It notifies the parent as required.
530 * It handles wraping and non-wraping correctly.
531 * It is assumed that delta>0
533 static void UPDOWN_DoAction (HWND hwnd
, int delta
, BOOL incr
)
535 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
536 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
539 TRACE("%s by %d\n", incr
? "inc" : "dec", delta
);
541 /* check if we can do the modification first */
542 delta
*= (incr
? 1 : -1) * (infoPtr
->MaxVal
< infoPtr
->MinVal
? -1 : 1);
544 /* We must notify parent now to obtain permission */
545 ni
.iPos
= infoPtr
->CurVal
;
547 ni
.hdr
.hwndFrom
= hwnd
;
548 ni
.hdr
.idFrom
= GetWindowLongA (hwnd
, GWL_ID
);
549 ni
.hdr
.code
= UDN_DELTAPOS
;
550 if (!SendMessageA(GetParent (hwnd
), WM_NOTIFY
,
551 (WPARAM
)ni
.hdr
.idFrom
, (LPARAM
)&ni
))
553 /* Parent said: OK to adjust */
555 /* Now adjust value with (maybe new) delta */
556 if (UPDOWN_OffsetVal (hwnd
, ni
.iDelta
))
558 /* Now take care about our buddy */
559 if(infoPtr
->Buddy
&& IsWindow(infoPtr
->Buddy
)
560 && (dwStyle
& UDS_SETBUDDYINT
) )
561 UPDOWN_SetBuddyInt (hwnd
);
565 /* Also, notify it. This message is sent in any case. */
566 SendMessageA (GetParent (hwnd
),
567 dwStyle
& UDS_HORZ
? WM_HSCROLL
: WM_VSCROLL
,
568 MAKELONG(SB_THUMBPOSITION
, infoPtr
->CurVal
), hwnd
);
571 /***********************************************************************
574 * Returns TRUE if it is enabled as well as its buddy (if any)
577 static BOOL
UPDOWN_IsEnabled (HWND hwnd
)
579 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
581 if(GetWindowLongA (hwnd
, GWL_STYLE
) & WS_DISABLED
)
584 return IsWindowEnabled(infoPtr
->Buddy
);
588 /***********************************************************************
591 * Deletes any timers, releases the mouse and does redraw if necessary.
592 * If the control is not in "capture" mode, it does nothing.
593 * If the control was not in cancel mode, it returns FALSE.
594 * If the control was in cancel mode, it returns TRUE.
596 static BOOL
UPDOWN_CancelMode (HWND hwnd
)
598 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
600 /* if not in 'capture' mode, do nothing */
601 if(!(infoPtr
->Flags
& FLAG_CLICKED
))
604 KillTimer (hwnd
, TIMERID1
); /* kill all possible timers */
605 KillTimer (hwnd
, TIMERID2
);
607 if (GetCapture() == hwnd
) /* let the mouse go */
608 ReleaseCapture(); /* if we still have it */
610 infoPtr
->Flags
= 0; /* get rid of any flags */
611 UPDOWN_Refresh (hwnd
); /* redraw the control just in case */
616 /***********************************************************************
617 * UPDOWN_HandleMouseEvent
619 * Handle a mouse event for the updown.
620 * 'pt' is the location of the mouse event in client or
621 * windows coordinates.
623 static void UPDOWN_HandleMouseEvent (HWND hwnd
, UINT msg
, POINT pt
)
625 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
626 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
632 case WM_LBUTTONDOWN
: /* Initialise mouse tracking */
633 /* If we are already in the 'clicked' mode, then nothing to do */
634 if(infoPtr
->Flags
& FLAG_CLICKED
)
637 /* If the buddy is an edit, will set focus to it */
638 if (!lstrcmpA (infoPtr
->szBuddyClass
, "Edit"))
639 SetFocus(infoPtr
->Buddy
);
641 /* Now see which one is the 'active' arrow */
642 temp
= UPDOWN_GetArrowFromPoint (hwnd
, &rect
, pt
);
644 /* Update the CurVal if necessary */
645 if (dwStyle
& UDS_SETBUDDYINT
)
646 UPDOWN_GetBuddyInt (hwnd
);
648 /* Set up the correct flags */
650 infoPtr
->Flags
|= temp
? FLAG_INCR
: FLAG_DECR
;
651 infoPtr
->Flags
|= FLAG_MOUSEIN
;
653 /* repaint the control */
654 UPDOWN_Refresh (hwnd
);
656 /* process the click */
657 UPDOWN_DoAction (hwnd
, 1, infoPtr
->Flags
& FLAG_INCR
);
659 /* now capture all mouse messages */
662 /* and startup the first timer */
663 SetTimer(hwnd
, TIMERID1
, INITIAL_DELAY
, 0);
667 /* If we are not in the 'clicked' mode, then nothing to do */
668 if(!(infoPtr
->Flags
& FLAG_CLICKED
))
671 /* save the flags to see if any got modified */
672 temp
= infoPtr
->Flags
;
674 /* Now get the 'active' arrow rectangle */
675 if (infoPtr
->Flags
& FLAG_INCR
)
676 UPDOWN_GetArrowRect (hwnd
, &rect
, TRUE
);
678 UPDOWN_GetArrowRect (hwnd
, &rect
, FALSE
);
680 /* Update the flags if we are in/out */
681 if(PtInRect(&rect
, pt
))
682 infoPtr
->Flags
|= FLAG_MOUSEIN
;
684 infoPtr
->Flags
&= ~FLAG_MOUSEIN
;
685 if(accelIndex
!= -1) /* if we have accel info */
686 accelIndex
= 0; /* reset it */
688 /* If state changed, redraw the control */
689 if(temp
!= infoPtr
->Flags
)
690 UPDOWN_Refresh (hwnd
);
694 ERR("Impossible case!\n");
699 /***********************************************************************
702 static LRESULT WINAPI
UpDownWindowProc(HWND hwnd
, UINT message
, WPARAM wParam
,
705 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr (hwnd
);
706 DWORD dwStyle
= GetWindowLongA (hwnd
, GWL_STYLE
);
708 if (!infoPtr
&& (message
!= WM_CREATE
) && (message
!= WM_NCCREATE
))
709 return DefWindowProcA (hwnd
, message
, wParam
, lParam
);
713 /* get rid of border, if any */
714 SetWindowLongA (hwnd
, GWL_STYLE
, dwStyle
& ~WS_BORDER
);
718 infoPtr
= (UPDOWN_INFO
*)COMCTL32_Alloc (sizeof(UPDOWN_INFO
));
719 SetWindowLongA (hwnd
, 0, (DWORD
)infoPtr
);
721 /* initialize the info struct */
722 infoPtr
->AccelCount
=0; infoPtr
->AccelVect
=0;
723 infoPtr
->CurVal
=0; infoPtr
->MinVal
=0; infoPtr
->MaxVal
=9999;
724 infoPtr
->Base
= 10; /* Default to base 10 */
725 infoPtr
->Buddy
= 0; /* No buddy window yet */
726 infoPtr
->Flags
= 0; /* And no flags */
728 /* Do we pick the buddy win ourselves? */
729 if (dwStyle
& UDS_AUTOBUDDY
)
730 UPDOWN_SetBuddy (hwnd
, GetWindow (hwnd
, GW_HWNDPREV
));
732 TRACE("UpDown Ctrl creation, hwnd=%04x\n", hwnd
);
736 if(infoPtr
->AccelVect
)
737 COMCTL32_Free (infoPtr
->AccelVect
);
739 if ( IsWindow(infoPtr
->Buddy
) ) /* Cleanup */
740 RemovePropA(infoPtr
->Buddy
, BUDDY_UPDOWN_HWND
);
742 COMCTL32_Free (infoPtr
);
743 SetWindowLongA (hwnd
, 0, 0);
744 TRACE("UpDown Ctrl destruction, hwnd=%04x\n", hwnd
);
748 if (dwStyle
& WS_DISABLED
)
749 UPDOWN_CancelMode (hwnd
);
751 UPDOWN_Refresh (hwnd
);
755 /* if initial timer, kill it and start the repeat timer */
756 if(wParam
== TIMERID1
){
757 KillTimer(hwnd
, TIMERID1
);
758 /* if no accel info given, used default timer */
759 if(infoPtr
->AccelCount
==0 || infoPtr
->AccelVect
==0){
764 accelIndex
= 0; /* otherwise, use it */
765 temp
= infoPtr
->AccelVect
[accelIndex
].nSec
* 1000 + 1;
767 SetTimer(hwnd
, TIMERID2
, temp
, 0);
770 /* now, if the mouse is above us, do the thing...*/
771 if(infoPtr
->Flags
& FLAG_MOUSEIN
){
772 temp
= accelIndex
==-1 ? 1 : infoPtr
->AccelVect
[accelIndex
].nInc
;
773 UPDOWN_DoAction(hwnd
, temp
, infoPtr
->Flags
& FLAG_INCR
);
775 if(accelIndex
!=-1 && accelIndex
< infoPtr
->AccelCount
-1){
776 KillTimer(hwnd
, TIMERID2
);
777 accelIndex
++; /* move to the next accel info */
778 temp
= infoPtr
->AccelVect
[accelIndex
].nSec
* 1000 + 1;
779 /* make sure we have at least 1ms intervals */
780 SetTimer(hwnd
, TIMERID2
, temp
, 0);
786 UPDOWN_CancelMode (hwnd
);
790 if(!UPDOWN_CancelMode(hwnd
))
793 SendMessageA(GetParent(hwnd
), dwStyle
& UDS_HORZ
? WM_HSCROLL
: WM_VSCROLL
,
794 MAKELONG(SB_ENDSCROLL
, infoPtr
->CurVal
), hwnd
);
796 /*If we released the mouse and our buddy is an edit */
797 /* we must select all text in it. */
798 if (!lstrcmpA (infoPtr
->szBuddyClass
, "Edit"))
799 SendMessageA(infoPtr
->Buddy
, EM_SETSEL
, 0, MAKELONG(0, -1));
804 if(UPDOWN_IsEnabled(hwnd
)){
806 pt
.x
= SLOWORD(lParam
);
807 pt
.y
= SHIWORD(lParam
);
808 UPDOWN_HandleMouseEvent (hwnd
, message
, pt
);
813 if((dwStyle
& UDS_ARROWKEYS
) && UPDOWN_IsEnabled(hwnd
)){
817 UPDOWN_GetBuddyInt (hwnd
);
818 /* Fixme: Paint the according button pressed for some time, like win95 does*/
819 UPDOWN_DoAction (hwnd
, 1, wParam
==VK_UP
);
826 UPDOWN_Paint (hwnd
, (HDC
)wParam
);
830 if (wParam
==0 && lParam
==0) /*if both zero, */
831 return infoPtr
->AccelCount
; /*just return the accel count*/
832 if (wParam
|| lParam
){
833 UNKNOWN_PARAM(UDM_GETACCEL
, wParam
, lParam
);
836 temp
= min(infoPtr
->AccelCount
, wParam
);
837 memcpy((void *)lParam
, infoPtr
->AccelVect
, temp
*sizeof(UDACCEL
));
841 TRACE("UpDown Ctrl new accel info, hwnd=%04x\n", hwnd
);
842 if(infoPtr
->AccelVect
){
843 COMCTL32_Free (infoPtr
->AccelVect
);
844 infoPtr
->AccelCount
= 0;
845 infoPtr
->AccelVect
= 0;
849 infoPtr
->AccelVect
= COMCTL32_Alloc (wParam
*sizeof(UDACCEL
));
850 if(infoPtr
->AccelVect
==0)
852 memcpy(infoPtr
->AccelVect
, (void*)lParam
, wParam
*sizeof(UDACCEL
));
856 if (wParam
|| lParam
)
857 UNKNOWN_PARAM(UDM_GETBASE
, wParam
, lParam
);
858 return infoPtr
->Base
;
861 TRACE("UpDown Ctrl new base(%d), hwnd=%04x\n",
863 if ( !(wParam
==10 || wParam
==16) || lParam
)
864 UNKNOWN_PARAM(UDM_SETBASE
, wParam
, lParam
);
865 if (wParam
==10 || wParam
==16){
866 temp
= infoPtr
->Base
;
867 infoPtr
->Base
= wParam
;
868 return temp
; /* return the prev base */
873 if (wParam
|| lParam
)
874 UNKNOWN_PARAM(UDM_GETBUDDY
, wParam
, lParam
);
875 return infoPtr
->Buddy
;
879 UNKNOWN_PARAM(UDM_SETBUDDY
, wParam
, lParam
);
880 temp
= infoPtr
->Buddy
;
881 UPDOWN_SetBuddy (hwnd
, wParam
);
882 TRACE("UpDown Ctrl new buddy(%04x), hwnd=%04x\n",
883 infoPtr
->Buddy
, hwnd
);
887 if (wParam
|| lParam
)
888 UNKNOWN_PARAM(UDM_GETPOS
, wParam
, lParam
);
889 temp
= UPDOWN_GetBuddyInt (hwnd
);
890 return MAKELONG(infoPtr
->CurVal
, temp
? 0 : 1);
893 if (wParam
|| HIWORD(lParam
))
894 UNKNOWN_PARAM(UDM_GETPOS
, wParam
, lParam
);
895 temp
= SLOWORD(lParam
);
896 TRACE("UpDown Ctrl new value(%d), hwnd=%04x\n",
898 if(!UPDOWN_InBounds(hwnd
, temp
)){
899 if(temp
< infoPtr
->MinVal
)
900 temp
= infoPtr
->MinVal
;
901 if(temp
> infoPtr
->MaxVal
)
902 temp
= infoPtr
->MaxVal
;
904 wParam
= infoPtr
->CurVal
; /* save prev value */
905 infoPtr
->CurVal
= temp
; /* set the new value */
906 if(dwStyle
& UDS_SETBUDDYINT
)
907 UPDOWN_SetBuddyInt (hwnd
);
908 return wParam
; /* return prev value */
911 if (wParam
|| lParam
)
912 UNKNOWN_PARAM(UDM_GETRANGE
, wParam
, lParam
);
913 return MAKELONG(infoPtr
->MaxVal
, infoPtr
->MinVal
);
917 UNKNOWN_PARAM(UDM_SETRANGE
, wParam
, lParam
); /* we must have: */
918 infoPtr
->MaxVal
= SLOWORD(lParam
); /* UD_MINVAL <= Max <= UD_MAXVAL */
919 infoPtr
->MinVal
= SHIWORD(lParam
); /* UD_MINVAL <= Min <= UD_MAXVAL */
920 /* |Max-Min| <= UD_MAXVAL */
921 TRACE("UpDown Ctrl new range(%d to %d), hwnd=%04x\n",
922 infoPtr
->MinVal
, infoPtr
->MaxVal
, hwnd
);
927 *(LPINT
)wParam
= infoPtr
->MinVal
;
929 *(LPINT
)lParam
= infoPtr
->MaxVal
;
933 infoPtr
->MinVal
= (INT
)wParam
;
934 infoPtr
->MaxVal
= (INT
)lParam
;
935 if (infoPtr
->MaxVal
<= infoPtr
->MinVal
)
936 infoPtr
->MaxVal
= infoPtr
->MinVal
+ 1;
937 TRACE("UpDown Ctrl new range(%d to %d), hwnd=%04x\n",
938 infoPtr
->MinVal
, infoPtr
->MaxVal
, hwnd
);
942 if ((LPBOOL
)lParam
!= NULL
)
943 *((LPBOOL
)lParam
) = TRUE
;
944 return infoPtr
->CurVal
;
947 if(!UPDOWN_InBounds(hwnd
, (int)lParam
)){
948 if((int)lParam
< infoPtr
->MinVal
)
949 lParam
= infoPtr
->MinVal
;
950 if((int)lParam
> infoPtr
->MaxVal
)
951 lParam
= infoPtr
->MaxVal
;
953 temp
= infoPtr
->CurVal
; /* save prev value */
954 infoPtr
->CurVal
= (int)lParam
; /* set the new value */
955 if(dwStyle
& UDS_SETBUDDYINT
)
956 UPDOWN_SetBuddyInt (hwnd
);
957 return temp
; /* return prev value */
960 if (message
>= WM_USER
)
961 ERR("unknown msg %04x wp=%04x lp=%08lx\n",
962 message
, wParam
, lParam
);
963 return DefWindowProcA (hwnd
, message
, wParam
, lParam
);
969 /***********************************************************************
970 * UPDOWN_Buddy_SubclassProc used to handle messages sent to the buddy
974 UPDOWN_Buddy_SubclassProc (
980 WNDPROC superClassWndProc
= (WNDPROC
)GetPropA(hwnd
, BUDDY_SUPERCLASS_WNDPROC
);
981 TRACE("hwnd=%04x, wndProc=%d, uMsg=%04x, wParam=%d, lParam=%d\n",
982 hwnd
, (INT
)superClassWndProc
, uMsg
, wParam
, (UINT
)lParam
);
988 if ( ((int)wParam
== VK_UP
) || ((int)wParam
== VK_DOWN
) )
990 HWND upDownHwnd
= GetPropA(hwnd
, BUDDY_UPDOWN_HWND
);
991 UPDOWN_INFO
*infoPtr
= UPDOWN_GetInfoPtr(upDownHwnd
);
993 if (!lstrcmpA (infoPtr
->szBuddyClass
, "ListBox"))
995 /* if the buddy is a list window, we must update curr index */
996 INT oldVal
= SendMessageA(hwnd
, LB_GETCURSEL
, 0, 0);
997 SendMessageA(hwnd
, LB_SETCURSEL
, oldVal
+1, 0);
1001 UPDOWN_GetBuddyInt(upDownHwnd
);
1002 UPDOWN_DoAction(upDownHwnd
, 1, wParam
==VK_UP
);
1007 /* else Fall Through */
1010 return CallWindowProcA( superClassWndProc
, hwnd
, uMsg
, wParam
, lParam
);
1013 /***********************************************************************
1014 * UPDOWN_Register [Internal]
1016 * Registers the updown window class.
1020 UPDOWN_Register(void)
1024 ZeroMemory( &wndClass
, sizeof( WNDCLASSA
) );
1025 wndClass
.style
= CS_GLOBALCLASS
| CS_VREDRAW
;
1026 wndClass
.lpfnWndProc
= (WNDPROC
)UpDownWindowProc
;
1027 wndClass
.cbClsExtra
= 0;
1028 wndClass
.cbWndExtra
= sizeof(UPDOWN_INFO
*);
1029 wndClass
.hCursor
= LoadCursorA( 0, IDC_ARROWA
);
1030 wndClass
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
1031 wndClass
.lpszClassName
= UPDOWN_CLASSA
;
1033 RegisterClassA( &wndClass
);
1037 /***********************************************************************
1038 * UPDOWN_Unregister [Internal]
1040 * Unregisters the updown window class.
1044 UPDOWN_Unregister (void)
1046 UnregisterClassA (UPDOWN_CLASSA
, (HINSTANCE
)NULL
);