Release 970914
[wine.git] / controls / updown.c
blob4282300f3552d2c36bfe726fbc92efcfdafad3ac
1 /*
2 * Updown control
4 * Copyright 1997 Dimitrie O. Paun
6 * TODO:
7 * - subclass the buddy window (in UPDOWN_SetBuddy) to process the
8 * arrow keys
9 * - I am not sure about the default values for the Min, Max, Pos
10 * (in the UPDOWN_INFO the fields: MinVal, MaxVal, CurVal)
11 * - I think I do not handle correctly the WS_BORDER style.
12 * Testing:
13 * Not much. The following have not been tested at all:
14 * - horizontal arrows
15 * - listbox as buddy window
16 * - acceleration
17 * - base 16
18 * - UDS_ALIGNLEFT, ~UDS_WRAP
19 * - integers with thousand separators.
20 * Even though the above list seems rather large, the control seems to
21 * behave very well so I am confident it does work in most (all) of the
22 * untested cases.
23 * Problems:
24 * I do not like the arrows yet, I'll work more on them later on.
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <assert.h>
30 #include <string.h>
31 #include "windows.h"
32 #include "winnls.h"
33 #include "syscolor.h"
34 #include "sysmetrics.h"
35 #include "updown.h"
36 #include "graphics.h"
37 #include "heap.h"
38 #include "win.h"
39 #include "stddebug.h"
40 /*#define DEBUG_UPDOWN*/
41 #include "debug.h"
43 /* Control configuration constants */
45 #define INITIAL_DELAY 500 /* initial timer until auto-increment kicks in */
46 #define REPEAT_DELAY 50 /* delay between auto-increments */
48 #define DEFAULT_WIDTH 14 /* default width of the ctrl */
49 #define DEFAULT_XSEP 0 /* default separation between buddy and crtl */
50 #define DEFAULT_ADDTOP 0 /* amount to extend above the buddy window */
51 #define DEFAULT_ADDBOT 0 /* amount to extend below the buddy window */
54 /* Work constants */
56 #define FLAG_INCR 0x01
57 #define FLAG_DECR 0x02
58 #define FLAG_MOUSEIN 0x04
59 #define FLAG_CLICKED (FLAG_INCR | FLAG_DECR)
61 #define TIMERID1 1
62 #define TIMERID2 2
64 static int accelIndex = -1;
66 #define UNKNOWN_PARAM(msg, wParam, lParam) dprintf_updown(stddeb, \
67 "UpDown Ctrl: Unknown parameter(s) for message " #msg \
68 "(%04x): wp=%04x lp=%08lx\n", msg, wParam, lParam);
70 #define UPDOWN_GetInfoPtr(wndPtr) ((UPDOWN_INFO *)wndPtr->wExtra)
72 /***********************************************************************
73 * UPDOWN_InBounds
74 * Tests if a given value 'val' is between the Min&Max limits
76 static BOOL32 UPDOWN_InBounds(WND *wndPtr, int val)
78 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
80 if(infoPtr->MaxVal > infoPtr->MinVal)
81 return (infoPtr->MinVal <= val) && (val <= infoPtr->MaxVal);
82 else
83 return (infoPtr->MaxVal <= val) && (val <= infoPtr->MinVal);
86 /***********************************************************************
87 * UPDOWN_OffsetVal
88 * Tests if we can change the current value by delta. If so, it changes
89 * it and returns TRUE. Else, it leaves it unchanged and returns FALSE.
91 static BOOL32 UPDOWN_OffsetVal(WND *wndPtr, int delta)
93 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
95 /* check if we can do the modification first */
96 if(!UPDOWN_InBounds(wndPtr, infoPtr->CurVal+delta)){
97 if(wndPtr->dwStyle & UDS_WRAP)
98 delta += (delta < 0 ? -1 : 1) *
99 (infoPtr->MaxVal < infoPtr->MinVal ? -1 : 1) *
100 (infoPtr->MinVal - infoPtr->MaxVal) +
101 (delta < 0 ? 1 : -1);
102 else
103 return FALSE;
106 infoPtr->CurVal += delta;
107 return TRUE;
110 /***********************************************************************
111 * UPDOWN_GetArrawRect
112 * wndPtr - pointer to the up-down wnd
113 * rect - will hold the rectangle
114 * incr - TRUE get the "increment" rect (up or right)
115 * FALSE get the "decrement" rect (down or left)
118 static void UPDOWN_GetArrowRect(WND *wndPtr, RECT32 *rect, BOOL32 incr)
120 int len; /* will hold the width or height */
122 GetClientRect32(wndPtr->hwndSelf, rect);
124 if (wndPtr->dwStyle & UDS_HORZ) {
125 len = rect->right - rect->left; /* compute the width */
126 if (incr)
127 rect->left = len/2+1;
128 else
129 rect->right = len/2;
131 else {
132 len = rect->bottom - rect->top; /* compute the height */
133 if (incr)
134 rect->bottom = len/2;
135 else
136 rect->top = len/2+1;
140 /***********************************************************************
141 * UPDOWN_GetArrowFromPoint
142 * Returns the rectagle (for the up or down arrow) that contains pt.
143 * If it returns the up rect, it returns TRUE.
144 * If it returns the down rect, it returns FALSE.
146 static int UPDOWN_GetArrowFromPoint(WND *wndPtr, RECT32 *rect, POINT32 pt)
148 UPDOWN_GetArrowRect(wndPtr, rect, TRUE);
149 if(PtInRect32(rect, pt))
150 return TRUE;
152 UPDOWN_GetArrowRect(wndPtr, rect, FALSE);
153 return FALSE;
157 /***********************************************************************
158 * UPDOWN_GetThousandSep
159 * Returns the thousand sep. If an error occurs, it returns ','.
161 static char UPDOWN_GetThousandSep()
163 char sep[2];
165 if(GetLocaleInfo32A(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND,
166 sep, sizeof(sep)) != 1)
167 return ',';
169 return sep[0];
172 /***********************************************************************
173 * UPDOWN_GetBuddyInt
174 * Tries to read the pos from the buddy window and if it succeeds,
175 * it stores it in the control's CurVal
176 * returns:
177 * TRUE - if it read the integer from the buddy successfully
178 * FALSE - if an error occured
180 static BOOL32 UPDOWN_GetBuddyInt(WND *wndPtr)
182 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
183 char txt[20], sep, *src, *dst;
184 int newVal;
186 if (!IsWindow32(infoPtr->Buddy))
187 return FALSE;
189 /*if the buddy is a list window, we must set curr index */
190 if(WIDGETS_IsControl32(WIN_FindWndPtr(infoPtr->Buddy), BIC32_LISTBOX)){
191 newVal = SendMessage32A(infoPtr->Buddy, LB_GETCARETINDEX32, 0, 0);
192 if(newVal < 0)
193 return FALSE;
195 else{
196 /* we have a regural window, so will get the text */
197 if (!GetWindowText32A(infoPtr->Buddy, txt, sizeof(txt)))
198 return FALSE;
200 sep = UPDOWN_GetThousandSep();
202 /* now get rid of the separators */
203 for(src = dst = txt; *src; src++)
204 if(*src != sep)
205 *dst++ = *src;
206 *dst = 0;
208 /* try to convert the number and validate it */
209 newVal = strtol(txt, &src, infoPtr->Base);
210 if(*src || !UPDOWN_InBounds(wndPtr, newVal))
211 return FALSE;
213 dprintf_updown(stddeb, "UpDown Ctrl: new value(%d) read from buddy "
214 "(old=%d)\n", newVal, infoPtr->CurVal);
217 infoPtr->CurVal = newVal;
218 return TRUE;
222 /***********************************************************************
223 * UPDOWN_SetBuddyInt
224 * Tries to set the pos to the buddy window based on current pos
225 * returns:
226 * TRUE - if it set the caption of the buddy successfully
227 * FALSE - if an error occured
229 static BOOL32 UPDOWN_SetBuddyInt(WND *wndPtr)
231 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
232 char txt1[20], sep;
233 int len;
235 if (!IsWindow32(infoPtr->Buddy))
236 return FALSE;
238 dprintf_updown(stddeb, "UpDown Ctrl: set new value(%d) to buddy.\n",
239 infoPtr->CurVal);
241 /*if the buddy is a list window, we must set curr index */
242 if(WIDGETS_IsControl32(WIN_FindWndPtr(infoPtr->Buddy), BIC32_LISTBOX)){
243 SendMessage32A(infoPtr->Buddy, LB_SETCURSEL32, infoPtr->CurVal, 0);
245 else{ /* Regural window, so set caption to the number */
246 len = sprintf(txt1, (infoPtr->Base==16) ? "%X" : "%d", infoPtr->CurVal);
248 sep = UPDOWN_GetThousandSep();
250 if (!(wndPtr->dwStyle & UDS_NOTHOUSANDS)) {
251 char txt2[20], *src = txt1, *dst = txt2;
252 if(len%3 > 0){
253 strncpy(dst, src, len%3);
254 dst += len%3;
255 src += len%3;
257 for(len=0; *src; len++,src++){
258 if(len%3==0)
259 *dst++ = sep;
260 *dst++ = *src++;
262 *dst = 0; /* null terminate it */
263 strcpy(txt1, txt2); /* move it to the proper place */
265 SetWindowText32A(infoPtr->Buddy, txt1);
268 return TRUE;
271 /***********************************************************************
272 * UPDOWN_DrawArraw
273 * Draw the arrows for the up-down control. The arrows are drawn with the
274 * current pen and filled with the current brush.
275 * Input:
276 * hdc - the DC to draw on
277 * rect - rectangle holding the arrow
278 * incr - TRUE if we draw the "increment" arrow
279 * FALSE if we draw the "decrement" arrow
280 * pressed - TRUE if the arrow is pressed (clicked)
281 * FALSE if the arrow is not pressed (clicked)
282 * horz - TRUE if the arrow is horizontal
283 * FLASE if the arrow is vertical
285 static void UPDOWN_DrawArrow(HDC32 hdc, RECT32 *rect, BOOL32 incr,
286 BOOL32 pressed, BOOL32 horz)
288 const int rw = rect->right - rect->left;
289 const int rh = rect->bottom - rect->top;
290 int offset = pressed ? 1 : 0;
291 int th, x, y, len;
293 /* compute max extents of the triangle */
294 if(horz){ /* horizontal arrows */
295 th = (3*rh)/5-2*4;
296 if(th > rw/2)
297 th = rw/2;
298 if(th < 2)
299 th = 2;
301 /* compute the position of the tip */
302 y = (rect->top+rect->bottom+1)/2 + offset;
303 if(incr)
304 x = (rect->left+rect->right+1)/2 + (2*th)/3 + offset;
305 else
306 x = (rect->left+rect->right)/2 + th/3 + offset;
308 for(len=1; th>0; th--, len+=2){
309 MoveToEx32(hdc, x, y, 0);
310 LineTo32(hdc, x, y+len);
311 if(incr) x--;
312 else x++;
313 y++;
316 else{ /* vertical arrows */
317 th = (3*rw)/5-2*4;
318 if(th > rh/2)
319 th = rh/2;
320 if(th < 2)
321 th = 2;
323 /* compute the position of the tip */
324 x = (rect->left+rect->right+1)/2 + offset;
325 if(incr)
326 y = (rect->top+rect->bottom+1)/2 - th/3 + offset;
327 else
328 y = (rect->top+rect->bottom)/2 + (2*th)/3 + offset;
330 for(len=1; th>0; th--, len+=2){
331 MoveToEx32(hdc, x, y, 0);
332 LineTo32(hdc, x+len, y);
333 if(incr) y++;
334 else y--;
335 x--;
342 /***********************************************************************
343 * UPDOWN_Paint
344 * Draw the arrows. The background need not be erased.
346 static void UPDOWN_Paint(WND *wndPtr)
348 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
349 PAINTSTRUCT32 ps;
350 BOOL32 prssed;
351 RECT32 rect;
352 HDC32 hdc;
353 HBRUSH32 oldBrush;
355 hdc = BeginPaint32( wndPtr->hwndSelf, &ps );
357 /* First select the proper brush */
358 oldBrush = wndPtr->dwStyle & WS_DISABLED ? GRAY_BRUSH : BLACK_BRUSH;
359 oldBrush = SelectObject32(hdc, GetStockObject32(oldBrush));
361 /* Draw the incr button */
362 UPDOWN_GetArrowRect(wndPtr, &rect, TRUE);
363 prssed = (infoPtr->Flags & FLAG_INCR) && (infoPtr->Flags & FLAG_MOUSEIN);
364 DrawEdge32(hdc, &rect, prssed?EDGE_SUNKEN:EDGE_RAISED, BF_RECT|BF_MIDDLE);
365 UPDOWN_DrawArrow(hdc, &rect, TRUE, prssed, wndPtr->dwStyle & UDS_HORZ);
367 /* Draw the space between the buttons */
368 rect.top = rect.bottom; rect.bottom++;
369 DrawEdge32(hdc, &rect, 0, BF_MIDDLE);
371 /* Draw the decr button */
372 UPDOWN_GetArrowRect(wndPtr, &rect, FALSE);
373 prssed = (infoPtr->Flags & FLAG_DECR) && (infoPtr->Flags & FLAG_MOUSEIN);
374 DrawEdge32(hdc, &rect, prssed ? EDGE_SUNKEN : EDGE_RAISED,
375 BF_RECT | BF_SOFT | BF_MIDDLE);
376 UPDOWN_DrawArrow(hdc, &rect, FALSE, prssed, wndPtr->dwStyle & UDS_HORZ);
378 /* clean-up */
379 SelectObject32(hdc, oldBrush);
380 EndPaint32( wndPtr->hwndSelf, &ps );
383 /***********************************************************************
384 * UPDOWN_SetBuddy
385 * Tests if 'hwndBud' is a valid window handle. If not, returns FALSE.
386 * Else, sets it as a new Buddy.
387 * Then, it should subclass the buddy
388 * If window has the UDS_ARROWKEYS, it subcalsses the buddy window to
389 * process the UP/DOWN arrow keys.
390 * If window has the UDS_ALIGNLEFT or UDS_ALIGNRIGHT style
391 * the size/pos of the buddy and the control are adjusted accordingly.
393 static BOOL32 UPDOWN_SetBuddy(WND *wndPtr, HWND32 hwndBud)
395 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
396 RECT32 budRect; /* new coord for the buddy */
397 int x; /* new x position and width for the up-down */
399 /* Is is a valid bud? */
400 if(!IsWindow32(hwndBud))
401 return FALSE;
403 if(wndPtr->dwStyle & UDS_ARROWKEYS){
404 /* FIXME: we need to subclass the buddy to process the arrow keys. */
405 fprintf(stderr, "UpDown Ctrl: we should subclass the buddy window!\n");
408 /* do we need to do any adjustments? */
409 if(!(wndPtr->dwStyle & (UDS_ALIGNLEFT | UDS_ALIGNRIGHT)))
410 return TRUE;
412 /* Get the rect of the buddy relative to its parent */
413 GetWindowRect32(infoPtr->Buddy, &budRect);
414 MapWindowPoints32(HWND_DESKTOP, GetParent32(infoPtr->Buddy),
415 (POINT32 *)(&budRect.left), 2);
417 /* now do the positioning */
418 if(wndPtr->dwStyle & UDS_ALIGNRIGHT){
419 budRect.right -= DEFAULT_WIDTH+DEFAULT_XSEP;
420 x = budRect.right+DEFAULT_XSEP;
422 else{ /* UDS_ALIGNLEFT */
423 x = budRect.left;
424 budRect.left += DEFAULT_WIDTH+DEFAULT_XSEP;
427 /* first adjust the buddy to accomodate the up/down */
428 SetWindowPos32(infoPtr->Buddy, 0, budRect.left, budRect.top,
429 budRect.right - budRect.left, budRect.bottom - budRect.top,
430 SWP_NOACTIVATE|SWP_NOZORDER);
432 /* now position the up/down */
433 /* Since the UDS_ALIGN* flags were used, */
434 /* we will pick the position and size of the window. */
435 SetWindowPos32(wndPtr->hwndSelf,0,x,budRect.top-DEFAULT_ADDTOP,DEFAULT_WIDTH,
436 (budRect.bottom-budRect.top)+DEFAULT_ADDTOP+DEFAULT_ADDBOT,
437 SWP_NOACTIVATE|SWP_NOZORDER);
439 return TRUE;
442 /***********************************************************************
443 * UPDOWN_DoAction
445 * This function increments/decrements the CurVal by the
446 * 'delta' amount according to the 'incr' flag
447 * It notifies the parent as required.
448 * It handles wraping and non-wraping correctly.
449 * It is assumed that delta>0
451 static void UPDOWN_DoAction(WND *wndPtr, int delta, BOOL32 incr)
453 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
454 int old_val = infoPtr->CurVal;
455 NM_UPDOWN ni;
457 dprintf_updown(stddeb, "UpDown Ctrl action: %s by %d\n",
458 incr ? "inc" : "dec", delta);
460 /* check if we can do the modification first */
461 delta *= (incr ? 1 : -1) * (infoPtr->MaxVal < infoPtr->MinVal ? -1 : 1);
462 if(!UPDOWN_OffsetVal(wndPtr, delta))
463 return;
465 /* so, if we can do the change, recompute delta and restore old value */
466 delta = infoPtr->CurVal - old_val;
467 infoPtr->CurVal = old_val;
469 /* We must notify parent now to obtain permission */
470 ni.iPos = infoPtr->CurVal;
471 ni.iDelta = delta;
472 ni.hdr.hwndFrom = wndPtr->hwndSelf;
473 ni.hdr.idFrom = wndPtr->wIDmenu;
474 ni.hdr.code = UDN_DELTAPOS;
475 if(SendMessage32A(wndPtr->parent->hwndSelf,
476 WM_NOTIFY, wndPtr->wIDmenu, (LPARAM)&ni))
477 return; /* we are not allowed to change */
479 /* Now adjust value with (maybe new) delta */
480 if(!UPDOWN_OffsetVal(wndPtr, ni.iDelta))
481 return;
483 /* Now take care about our buddy */
484 if(!IsWindow32(infoPtr->Buddy))
485 return; /* Nothing else to do */
488 if(wndPtr->dwStyle & UDS_SETBUDDYINT)
489 UPDOWN_SetBuddyInt(wndPtr);
491 /* Also, notify it */
492 /* FIXME: do we need to send the notification only if
493 we do not have the UDS_SETBUDDYINT style set? */
494 SendMessage32A(infoPtr->Buddy,
495 wndPtr->dwStyle & UDS_HORZ ? WM_HSCROLL : WM_VSCROLL,
496 MAKELONG(incr ? SB_LINEUP : SB_LINEDOWN, infoPtr->CurVal),
497 wndPtr->hwndSelf);
500 /***********************************************************************
501 * UPDOWN_IsEnabled
503 * Returns TRUE if it is enabled as well as its buddy (if any)
504 * FALSE otherwise
506 static BOOL32 UPDOWN_IsEnabled(WND *wndPtr)
508 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
510 if(wndPtr->dwStyle & WS_DISABLED)
511 return FALSE;
512 return IsWindowEnabled32(infoPtr->Buddy);
515 /***********************************************************************
516 * UPDOWN_CancelMode
518 * Deletes any timers, releases the mouse and does redraw if necessary.
519 * If the control is not in "capture" mode, it does nothing.
520 * If the control was not in cancel mode, it returns FALSE.
521 * If the control was in cancel mode, it returns TRUE.
523 static BOOL32 UPDOWN_CancelMode(WND *wndPtr)
525 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
527 /* if not in 'capture' mode, do nothing */
528 if(!(infoPtr->Flags & FLAG_CLICKED))
529 return FALSE;
531 KillTimer32(wndPtr->hwndSelf, TIMERID1); /* kill all possible timers */
532 KillTimer32(wndPtr->hwndSelf, TIMERID2);
534 if(GetCapture32() == wndPtr->hwndSelf) /* let the mouse go */
535 ReleaseCapture(); /* if we still have it */
537 infoPtr->Flags = 0; /* get rid of any flags */
538 UPDOWN_Paint(wndPtr); /* redraw the control just in case */
540 return TRUE;
543 /***********************************************************************
544 * UPDOWN_HandleMouseEvent
546 * Handle a mouse event for the updown.
547 * 'pt' is the location of the mouse event in client or
548 * windows coordinates.
550 static void UPDOWN_HandleMouseEvent(WND *wndPtr, UINT32 msg, POINT32 pt)
552 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
553 RECT32 rect;
554 int temp;
556 switch(msg)
558 case WM_LBUTTONDOWN: /* Initialise mouse tracking */
559 /* If we are already in the 'clicked' mode, then nothing to do */
560 if(infoPtr->Flags & FLAG_CLICKED)
561 return;
563 /* If the buddy is an edit, will set focus to it */
564 if(WIDGETS_IsControl32(WIN_FindWndPtr(infoPtr->Buddy), BIC32_EDIT))
565 SetFocus32(infoPtr->Buddy);
567 /* Now see which one is the 'active' arrow */
568 temp = UPDOWN_GetArrowFromPoint(wndPtr, &rect, pt);
570 /* Update the CurVal if necessary */
571 if(wndPtr->dwStyle & UDS_SETBUDDYINT)
572 UPDOWN_GetBuddyInt(wndPtr);
574 /* Before we proceed, see if we can spin... */
575 if(!(wndPtr->dwStyle & UDS_WRAP))
576 if(( temp && infoPtr->CurVal==infoPtr->MaxVal) ||
577 (!temp && infoPtr->CurVal==infoPtr->MinVal))
578 return;
580 /* Set up the correct flags */
581 infoPtr->Flags = 0;
582 infoPtr->Flags |= temp ? FLAG_INCR : FLAG_DECR;
583 infoPtr->Flags |= FLAG_MOUSEIN;
585 /* repaint the control */
586 UPDOWN_Paint(wndPtr);
588 /* process the click */
589 UPDOWN_DoAction(wndPtr, 1, infoPtr->Flags & FLAG_INCR);
591 /* now capture all mouse messages */
592 SetCapture32(wndPtr->hwndSelf);
594 /* and startup the first timer */
595 SetTimer32(wndPtr->hwndSelf, TIMERID1, INITIAL_DELAY, 0);
596 break;
598 case WM_MOUSEMOVE:
599 /* If we are not in the 'clicked' mode, then nothing to do */
600 if(!(infoPtr->Flags & FLAG_CLICKED))
601 return;
603 /* save the flags to see if any got modified */
604 temp = infoPtr->Flags;
606 /* Now get the 'active' arrow rectangle */
607 if (infoPtr->Flags & FLAG_INCR)
608 UPDOWN_GetArrowRect(wndPtr, &rect, TRUE);
609 else
610 UPDOWN_GetArrowRect(wndPtr, &rect, FALSE);
612 /* Update the flags if we are in/out */
613 if(PtInRect32(&rect, pt))
614 infoPtr->Flags |= FLAG_MOUSEIN;
615 else{
616 infoPtr->Flags &= ~FLAG_MOUSEIN;
617 if(accelIndex != -1) /* if we have accel info */
618 accelIndex = 0; /* reset it */
620 /* If state changed, redraw the control */
621 if(temp != infoPtr->Flags)
622 UPDOWN_Paint(wndPtr);
623 break;
625 default:
626 fprintf(stderr, "UpDown: Impossible case in proc "
627 "UPDOWN_HandleMouseEvent");
632 /***********************************************************************
633 * UpDownWndProc
635 LRESULT WINAPI UpDownWindowProc(HWND32 hwnd, UINT32 message, WPARAM32 wParam,
636 LPARAM lParam)
638 WND *wndPtr = WIN_FindWndPtr(hwnd);
639 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
640 int temp;
642 switch(message)
644 case WM_CREATE:
645 /* get rid of border, if any */
646 wndPtr->dwStyle &= ~WS_BORDER;
648 /* initialize the info struct */
649 infoPtr->AccelCount=0; infoPtr->AccelVect=0;
650 infoPtr->CurVal=0; infoPtr->MinVal=0; infoPtr->MaxVal=100; /*FIXME*/
651 infoPtr->Base = 10; /* Default to base 10 */
652 infoPtr->Buddy = 0; /* No buddy window yet */
653 infoPtr->Flags = 0; /* And no flags */
655 /* Do we pick the buddy win ourselves? */
656 if(wndPtr->dwStyle & UDS_AUTOBUDDY)
657 UPDOWN_SetBuddy(wndPtr, GetWindow32(wndPtr->hwndSelf, GW_HWNDPREV));
659 dprintf_updown(stddeb, "UpDown Ctrl creation, hwnd=%04x\n", hwnd);
660 break;
662 case WM_DESTROY:
663 if(infoPtr->AccelVect)
664 free(infoPtr->AccelVect);
665 dprintf_updown(stddeb, "UpDown Ctrl destruction, hwnd=%04x\n", hwnd);
666 break;
668 case WM_ENABLE:
669 if(wndPtr->dwStyle & WS_DISABLED)
670 UPDOWN_CancelMode(wndPtr);
671 UPDOWN_Paint(wndPtr);
672 break;
674 case WM_TIMER:
675 /* if initial timer, kill it and start the repeat timer */
676 if(wParam == TIMERID1){
677 KillTimer32(hwnd, TIMERID1);
678 /* if no accel info given, used default timer */
679 if(infoPtr->AccelCount==0 || infoPtr->AccelVect==0){
680 accelIndex = -1;
681 temp = REPEAT_DELAY;
683 else{
684 accelIndex = 0; /* otherwise, use it */
685 temp = infoPtr->AccelVect[accelIndex].nSec * 1000 + 1;
687 SetTimer32(hwnd, TIMERID2, temp, 0);
690 /* now, if the mouse is above us, do the thing...*/
691 if(infoPtr->Flags & FLAG_MOUSEIN){
692 temp = accelIndex==-1 ? 1 : infoPtr->AccelVect[accelIndex].nInc;
693 UPDOWN_DoAction(wndPtr, temp, infoPtr->Flags & FLAG_INCR);
695 if(accelIndex!=-1 && accelIndex < infoPtr->AccelCount-1){
696 KillTimer32(hwnd, TIMERID2);
697 accelIndex++; /* move to the next accel info */
698 temp = infoPtr->AccelVect[accelIndex].nSec * 1000 + 1;
699 /* make sure we have at least 1ms intervals */
700 SetTimer32(hwnd, TIMERID2, temp, 0);
703 break;
705 case WM_CANCELMODE:
706 UPDOWN_CancelMode(wndPtr);
707 break;
709 case WM_LBUTTONUP:
710 if(!UPDOWN_CancelMode(wndPtr))
711 break;
712 /*If we released the mouse and our buddy is an edit */
713 /* we must select all text in it. */
714 if(WIDGETS_IsControl32(WIN_FindWndPtr(infoPtr->Buddy), BIC32_EDIT))
715 SendMessage32A(infoPtr->Buddy, EM_SETSEL32, 0, MAKELONG(0, -1));
716 break;
718 case WM_LBUTTONDOWN:
719 case WM_MOUSEMOVE:
720 if(UPDOWN_IsEnabled(wndPtr)){
721 POINT32 pt;
722 CONV_POINT16TO32( (POINT16 *)&lParam, &pt );
723 UPDOWN_HandleMouseEvent( wndPtr, message, pt );
725 break;
727 case WM_KEYDOWN:
728 if((wndPtr->dwStyle & UDS_ARROWKEYS) && UPDOWN_IsEnabled(wndPtr)){
729 switch(wParam){
730 case VK_UP:
731 case VK_DOWN:
732 UPDOWN_GetBuddyInt(wndPtr);
733 UPDOWN_DoAction(wndPtr, 1, wParam==VK_UP);
734 break;
737 break;
739 case WM_PAINT:
740 UPDOWN_Paint(wndPtr);
741 break;
743 case UDM_GETACCEL:
744 if (wParam==0 && lParam==0) /*if both zero, */
745 return infoPtr->AccelCount; /*just return the accel count*/
746 if (wParam || lParam){
747 UNKNOWN_PARAM(UDM_GETACCEL, wParam, lParam);
748 return 0;
750 temp = MIN(infoPtr->AccelCount, wParam);
751 memcpy((void *)lParam, infoPtr->AccelVect, temp*sizeof(UDACCEL));
752 return temp;
754 case UDM_SETACCEL:
755 dprintf_updown(stddeb, "UpDown Ctrl new accel info, hwnd=%04x\n", hwnd);
756 if(infoPtr->AccelVect){
757 free(infoPtr->AccelVect);
758 infoPtr->AccelCount = 0;
759 infoPtr->AccelVect = 0;
761 if(wParam==0)
762 return TRUE;
763 infoPtr->AccelVect = malloc(wParam*sizeof(UDACCEL));
764 if(infoPtr->AccelVect==0)
765 return FALSE;
766 memcpy(infoPtr->AccelVect, (void*)lParam, wParam*sizeof(UDACCEL));
767 return TRUE;
769 case UDM_GETBASE:
770 if (wParam || lParam)
771 UNKNOWN_PARAM(UDM_GETBASE, wParam, lParam);
772 return infoPtr->Base;
774 case UDM_SETBASE:
775 dprintf_updown(stddeb, "UpDown Ctrl new base(%d), hwnd=%04x\n",
776 wParam, hwnd);
777 if ( !(wParam==10 || wParam==16) || lParam)
778 UNKNOWN_PARAM(UDM_SETBASE, wParam, lParam);
779 if (wParam==10 || wParam==16){
780 temp = infoPtr->Base;
781 infoPtr->Base = wParam;
782 return temp; /* return the prev base */
784 break;
786 case UDM_GETBUDDY:
787 if (wParam || lParam)
788 UNKNOWN_PARAM(UDM_GETBUDDY, wParam, lParam);
789 return infoPtr->Buddy;
791 case UDM_SETBUDDY:
792 if (lParam)
793 UNKNOWN_PARAM(UDM_SETBUDDY, wParam, lParam);
794 temp = infoPtr->Buddy;
795 infoPtr->Buddy = wParam;
796 UPDOWN_SetBuddy(wndPtr, wParam);
797 dprintf_updown(stddeb, "UpDown Ctrl new buddy(%04x), hwnd=%04x\n",
798 infoPtr->Buddy, hwnd);
799 return temp;
801 case UDM_GETPOS:
802 if (wParam || lParam)
803 UNKNOWN_PARAM(UDM_GETPOS, wParam, lParam);
804 temp = UPDOWN_GetBuddyInt(wndPtr);
805 return MAKELONG(infoPtr->CurVal, temp ? 0 : 1);
807 case UDM_SETPOS:
808 if (wParam || HIWORD(lParam))
809 UNKNOWN_PARAM(UDM_GETPOS, wParam, lParam);
810 temp = SLOWORD(lParam);
811 dprintf_updown(stddeb, "UpDown Ctrl new value(%d), hwnd=%04x\n",
812 temp, hwnd);
813 if(!UPDOWN_InBounds(wndPtr, temp)){
814 if(temp < infoPtr->MinVal)
815 temp = infoPtr->MinVal;
816 if(temp > infoPtr->MaxVal)
817 temp = infoPtr->MaxVal;
819 wParam = infoPtr->CurVal; /* save prev value */
820 infoPtr->CurVal = temp; /* set the new value */
821 if(wndPtr->dwStyle & UDS_SETBUDDYINT)
822 UPDOWN_SetBuddyInt(wndPtr);
823 return wParam; /* return prev value */
825 case UDM_GETRANGE:
826 if (wParam || lParam)
827 UNKNOWN_PARAM(UDM_GETRANGE, wParam, lParam);
828 return MAKELONG(infoPtr->MaxVal, infoPtr->MinVal);
830 case UDM_SETRANGE:
831 if (wParam)
832 UNKNOWN_PARAM(UDM_SETRANGE, wParam, lParam); /* we must have: */
833 infoPtr->MaxVal = SLOWORD(lParam); /* UD_MINVAL <= Max <= UD_MAXVAL */
834 infoPtr->MinVal = SHIWORD(lParam); /* UD_MINVAL <= Min <= UD_MAXVAL */
835 /* |Max-Min| <= UD_MAXVAL */
836 dprintf_updown(stddeb, "UpDown Ctrl new range(%d to %d), hwnd=%04x\n",
837 infoPtr->MinVal, infoPtr->MaxVal, hwnd);
838 break;
840 default:
841 if (message >= WM_USER)
842 fprintf( stderr, "UpDown Ctrl: unknown msg %04x wp=%04x lp=%08lx\n",
843 message, wParam, lParam );
844 return DefWindowProc32A( hwnd, message, wParam, lParam );
847 return 0;
850 /***********************************************************************
851 * CreateUpDownControl (COMCTL32.14)
853 HWND32 WINAPI CreateUpDownControl( DWORD style, INT32 x, INT32 y,
854 INT32 cx, INT32 cy, HWND32 parent,
855 INT32 id, HINSTANCE32 inst, HWND32 buddy,
856 INT32 maxVal, INT32 minVal, INT32 curVal )
858 HWND32 hUD = CreateWindow32A(UPDOWN_CLASS32A, 0, style, x, y, cx, cy,
859 parent, id, inst, 0);
860 if(hUD){
861 SendMessage32A(hUD, UDM_SETBUDDY, buddy, 0);
862 SendMessage32A(hUD, UDM_SETRANGE, 0, MAKELONG(maxVal, minVal));
863 SendMessage32A(hUD, UDM_SETPOS, 0, MAKELONG(curVal, 0));
866 return hUD;