Release 980628
[wine] / controls / updown.c
1 /*              
2  * Updown control
3  *
4  * Copyright 1997 Dimitrie O. Paun
5  *
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 don not handle correctly the WS_BORDER style.
12  *     (Should be fixed. <ekohl@abo.rhein-zeitung.de>)
13  *
14  * Testing:
15  *   Not much. The following  have not been tested at all:
16  *     - horizontal arrows
17  *     - listbox as buddy window
18  *     - acceleration
19  *     - base 16
20  *     - UDS_ALIGNLEFT, ~UDS_WRAP
21  *     - integers with thousand separators.
22  *   Even though the above list seems rather large, the control seems to
23  *   behave very well so I am confident it does work in most (all) of the
24  *   untested cases.
25  * Problems:
26  *   I do not like the arrows yet, I'll work more on them later on.
27  */
28
29 #include <stdlib.h>
30 #include <assert.h>
31 #include <string.h>
32 #include "windows.h"
33 #include "winnls.h"
34 #include "sysmetrics.h"
35 #include "updown.h"
36 #include "graphics.h"
37 #include "heap.h"
38 #include "win.h"
39 #include "debug.h"
40
41 /* Control configuration constants */
42
43 #define INITIAL_DELAY    500 /* initial timer until auto-increment kicks in */
44 #define REPEAT_DELAY     50  /* delay between auto-increments */
45
46 #define DEFAULT_WIDTH    14  /* default width of the ctrl */
47 #define DEFAULT_XSEP      0  /* default separation between buddy and crtl */
48 #define DEFAULT_ADDTOP    0  /* amount to extend above the buddy window */
49 #define DEFAULT_ADDBOT    0  /* amount to extend below the buddy window */
50
51
52 /* Work constants */
53
54 #define FLAG_INCR        0x01
55 #define FLAG_DECR        0x02
56 #define FLAG_MOUSEIN     0x04
57 #define FLAG_CLICKED     (FLAG_INCR | FLAG_DECR)
58
59 #define TIMERID1         1
60 #define TIMERID2         2
61
62 static int accelIndex = -1;
63
64 #define UNKNOWN_PARAM(msg, wParam, lParam) WARN(updown, \
65         "UpDown Ctrl: Unknown parameter(s) for message " #msg     \
66         "(%04x): wp=%04x lp=%08lx\n", msg, wParam, lParam);
67
68 #define UPDOWN_GetInfoPtr(wndPtr) ((UPDOWN_INFO *)wndPtr->wExtra[0])
69
70
71 /***********************************************************************
72  *           UPDOWN_InBounds
73  * Tests if a given value 'val' is between the Min&Max limits
74  */
75 static BOOL32 UPDOWN_InBounds(WND *wndPtr, int val)
76 {
77   UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
78
79   if(infoPtr->MaxVal > infoPtr->MinVal)
80     return (infoPtr->MinVal <= val) && (val <= infoPtr->MaxVal);
81   else
82     return (infoPtr->MaxVal <= val) && (val <= infoPtr->MinVal);
83 }
84
85 /***********************************************************************
86  *           UPDOWN_OffsetVal
87  * Tests if we can change the current value by delta. If so, it changes
88  * it and returns TRUE. Else, it leaves it unchanged and returns FALSE.
89  */
90 static BOOL32 UPDOWN_OffsetVal(WND *wndPtr, int delta)
91 {
92   UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
93
94   /* check if we can do the modification first */
95   if(!UPDOWN_InBounds(wndPtr, infoPtr->CurVal+delta)){
96     if(wndPtr->dwStyle & UDS_WRAP)
97       delta += (delta < 0 ? -1 : 1) *
98         (infoPtr->MaxVal < infoPtr->MinVal ? -1 : 1) *
99         (infoPtr->MinVal - infoPtr->MaxVal) +
100         (delta < 0 ? 1 : -1);
101     else
102       return FALSE;
103   }
104
105   infoPtr->CurVal += delta;
106   return TRUE;
107 }
108
109 /***********************************************************************
110  *           UPDOWN_GetArrowRect
111  * wndPtr   - pointer to the up-down wnd
112  * rect     - will hold the rectangle
113  * incr     - TRUE  get the "increment" rect (up or right)
114  *            FALSE get the "decrement" rect (down or left)
115  *          
116  */
117 static void UPDOWN_GetArrowRect(WND *wndPtr, RECT32 *rect, BOOL32 incr)
118 {
119   int len; /* will hold the width or height */
120
121   GetClientRect32(wndPtr->hwndSelf, rect);
122
123   if (wndPtr->dwStyle & UDS_HORZ) {
124     len = rect->right - rect->left; /* compute the width */
125     if (incr)
126       rect->left = len/2+1; 
127     else
128       rect->right = len/2;
129   }
130   else {
131     len = rect->bottom - rect->top; /* compute the height */
132     if (incr)
133       rect->bottom = len/2;
134     else
135       rect->top = len/2+1;
136   }
137 }
138
139 /***********************************************************************
140  *           UPDOWN_GetArrowFromPoint
141  * Returns the rectagle (for the up or down arrow) that contains pt.
142  * If it returns the up rect, it returns TRUE.
143  * If it returns the down rect, it returns FALSE.
144  */
145 static int UPDOWN_GetArrowFromPoint(WND *wndPtr, RECT32 *rect, POINT32 pt)
146 {
147   UPDOWN_GetArrowRect(wndPtr, rect, TRUE);
148   if(PtInRect32(rect, pt))
149     return TRUE;
150
151   UPDOWN_GetArrowRect(wndPtr, rect, FALSE);
152   return FALSE;
153 }
154
155
156 /***********************************************************************
157  *           UPDOWN_GetThousandSep
158  * Returns the thousand sep. If an error occurs, it returns ','.
159  */
160 static char UPDOWN_GetThousandSep()
161 {
162   char sep[2];
163
164   if(GetLocaleInfo32A(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, 
165                       sep, sizeof(sep)) != 1)
166     return ',';
167
168   return sep[0];
169 }
170
171 /***********************************************************************
172  *           UPDOWN_GetBuddyInt
173  * Tries to read the pos from the buddy window and if it succeeds,
174  * it stores it in the control's CurVal
175  * returns:
176  *   TRUE  - if it read the integer from the buddy successfully
177  *   FALSE - if an error occured
178  */
179 static BOOL32 UPDOWN_GetBuddyInt(WND *wndPtr)
180 {
181   UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
182   char txt[20], sep, *src, *dst;
183   int newVal;
184
185   if (!IsWindow32(infoPtr->Buddy))
186     return FALSE;
187
188   /*if the buddy is a list window, we must set curr index */
189   if(WIDGETS_IsControl32(WIN_FindWndPtr(infoPtr->Buddy), BIC32_LISTBOX)){
190     newVal = SendMessage32A(infoPtr->Buddy, LB_GETCARETINDEX32, 0, 0);
191     if(newVal < 0)
192       return FALSE;
193   }
194   else{
195     /* we have a regular window, so will get the text */
196     if (!GetWindowText32A(infoPtr->Buddy, txt, sizeof(txt)))
197       return FALSE;
198
199     sep = UPDOWN_GetThousandSep(); 
200
201     /* now get rid of the separators */
202     for(src = dst = txt; *src; src++)
203       if(*src != sep)
204         *dst++ = *src;
205     *dst = 0;
206
207     /* try to convert the number and validate it */
208     newVal = strtol(txt, &src, infoPtr->Base);
209     if(*src || !UPDOWN_InBounds(wndPtr, newVal)) 
210       return FALSE;
211
212     TRACE(updown, "new value(%d) read from buddy (old=%d)\n", 
213                  newVal, infoPtr->CurVal);
214   }
215   
216   infoPtr->CurVal = newVal;
217   return TRUE;
218 }
219
220
221 /***********************************************************************
222  *           UPDOWN_SetBuddyInt
223  * Tries to set the pos to the buddy window based on current pos
224  * returns:
225  *   TRUE  - if it set the caption of the  buddy successfully
226  *   FALSE - if an error occured
227  */
228 static BOOL32 UPDOWN_SetBuddyInt(WND *wndPtr)
229 {
230   UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
231   char txt1[20], sep;
232   int len;
233
234   if (!IsWindow32(infoPtr->Buddy)) 
235     return FALSE;
236
237   TRACE(updown, "set new value(%d) to buddy.\n",
238                infoPtr->CurVal);
239
240   /*if the buddy is a list window, we must set curr index */
241   if(WIDGETS_IsControl32(WIN_FindWndPtr(infoPtr->Buddy), BIC32_LISTBOX)){
242     SendMessage32A(infoPtr->Buddy, LB_SETCURSEL32, infoPtr->CurVal, 0);
243   }
244   else{ /* Regular window, so set caption to the number */
245     len = sprintf(txt1, (infoPtr->Base==16) ? "%X" : "%d", infoPtr->CurVal);
246
247     sep = UPDOWN_GetThousandSep(); 
248
249     if (!(wndPtr->dwStyle & UDS_NOTHOUSANDS)) {
250       char txt2[20], *src = txt1, *dst = txt2;
251       if(len%3 > 0){
252         strncpy(dst, src, len%3);
253         dst += len%3;
254         src += len%3;
255       }
256       for(len=0; *src; len++,src++){
257         if(len%3==0)
258           *dst++ = sep;
259         *dst++ = *src++;
260       }
261       *dst = 0;           /* null terminate it */
262       strcpy(txt1, txt2); /* move it to the proper place */
263     }
264     SetWindowText32A(infoPtr->Buddy, txt1);
265   }
266
267   return TRUE;
268
269
270 /***********************************************************************
271  * UPDOWN_Draw [Internal]
272  *
273  * Draw the arrows. The background need not be erased.
274  */
275 static void UPDOWN_Draw (WND *wndPtr, HDC32 hdc)
276 {
277   UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
278   BOOL32 prssed;
279   RECT32 rect;
280   
281   /* Draw the incr button */
282   UPDOWN_GetArrowRect(wndPtr, &rect, TRUE);
283   prssed = (infoPtr->Flags & FLAG_INCR) && (infoPtr->Flags & FLAG_MOUSEIN);
284   DrawFrameControl32(hdc, &rect, DFC_SCROLL, 
285         (wndPtr->dwStyle & UDS_HORZ ? DFCS_SCROLLLEFT : DFCS_SCROLLUP) |
286         (prssed ? DFCS_PUSHED : 0) |
287         (wndPtr->dwStyle&WS_DISABLED ? DFCS_INACTIVE : 0) );
288
289   /* Draw the space between the buttons */
290   rect.top = rect.bottom; rect.bottom++;
291   DrawEdge32(hdc, &rect, 0, BF_MIDDLE);
292                     
293   /* Draw the decr button */
294   UPDOWN_GetArrowRect(wndPtr, &rect, FALSE);
295   prssed = (infoPtr->Flags & FLAG_DECR) && (infoPtr->Flags & FLAG_MOUSEIN);
296   DrawFrameControl32(hdc, &rect, DFC_SCROLL, 
297         (wndPtr->dwStyle & UDS_HORZ ? DFCS_SCROLLRIGHT : DFCS_SCROLLDOWN) |
298         (prssed ? DFCS_PUSHED : 0) |
299         (wndPtr->dwStyle&WS_DISABLED ? DFCS_INACTIVE : 0) );
300 }
301
302 /***********************************************************************
303  * UPDOWN_Refresh [Internal]
304  *
305  * Synchronous drawing (must NOT be used in WM_PAINT).
306  * Calls UPDOWN_Draw.
307  */
308 static void UPDOWN_Refresh (WND *wndPtr)
309 {
310     HDC32 hdc;
311   
312     hdc = GetDC32 (wndPtr->hwndSelf);
313     UPDOWN_Draw (wndPtr, hdc);
314     ReleaseDC32 (wndPtr->hwndSelf, hdc);
315 }
316
317
318 /***********************************************************************
319  * UPDOWN_Paint [Internal]
320  *
321  * Asynchronous drawing (must ONLY be used in WM_PAINT).
322  * Calls UPDOWN_Draw.
323  */
324 static void UPDOWN_Paint (WND *wndPtr)
325 {
326     PAINTSTRUCT32 ps;
327     HDC32 hdc;
328   
329     hdc = BeginPaint32 (wndPtr->hwndSelf, &ps);
330     UPDOWN_Draw (wndPtr, hdc);
331     EndPaint32 (wndPtr->hwndSelf, &ps);
332 }
333
334 /***********************************************************************
335  *           UPDOWN_SetBuddy
336  * Tests if 'hwndBud' is a valid window handle. If not, returns FALSE.
337  * Else, sets it as a new Buddy.
338  * Then, it should subclass the buddy 
339  * If window has the UDS_ARROWKEYS, it subcalsses the buddy window to
340  * process the UP/DOWN arrow keys.
341  * If window has the UDS_ALIGNLEFT or UDS_ALIGNRIGHT style
342  * the size/pos of the buddy and the control are adjusted accordingly.
343  */
344 static BOOL32 UPDOWN_SetBuddy(WND *wndPtr, HWND32 hwndBud)
345 {
346   UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr); 
347   RECT32 budRect; /* new coord for the buddy */
348   int x;          /* new x position and width for the up-down */
349           
350   /* Is is a valid bud? */
351   if(!IsWindow32(hwndBud))
352     return FALSE;
353
354   if(wndPtr->dwStyle & UDS_ARROWKEYS){
355     FIXME(updown, "we need to subclass the buddy to process the arrow keys.\n");
356   }
357
358   /* do we need to do any adjustments? */
359   if(!(wndPtr->dwStyle & (UDS_ALIGNLEFT | UDS_ALIGNRIGHT)))
360     return TRUE;
361
362   /* Get the rect of the buddy relative to its parent */
363   GetWindowRect32(infoPtr->Buddy, &budRect);
364   MapWindowPoints32(HWND_DESKTOP, GetParent32(infoPtr->Buddy),
365                   (POINT32 *)(&budRect.left), 2);
366           
367   /* now do the positioning */
368   if(wndPtr->dwStyle & UDS_ALIGNRIGHT){
369     budRect.right -= DEFAULT_WIDTH+DEFAULT_XSEP;
370     x  = budRect.right+DEFAULT_XSEP;
371   }
372   else{ /* UDS_ALIGNLEFT */
373     x  = budRect.left;
374     budRect.left += DEFAULT_WIDTH+DEFAULT_XSEP;
375   }
376
377   /* first adjust the buddy to accomodate the up/down */
378   SetWindowPos32(infoPtr->Buddy, 0, budRect.left, budRect.top,
379                budRect.right  - budRect.left, budRect.bottom - budRect.top, 
380                SWP_NOACTIVATE|SWP_NOZORDER);
381
382   /* now position the up/down */
383   /* Since the UDS_ALIGN* flags were used, */
384   /* we will pick the position and size of the window. */
385
386   SetWindowPos32(wndPtr->hwndSelf,0,x,budRect.top-DEFAULT_ADDTOP,DEFAULT_WIDTH,
387                  (budRect.bottom-budRect.top)+DEFAULT_ADDTOP+DEFAULT_ADDBOT,
388                  SWP_NOACTIVATE|SWP_NOZORDER);
389
390   return TRUE;
391 }         
392
393 /***********************************************************************
394  *           UPDOWN_DoAction
395  *
396  * This function increments/decrements the CurVal by the 
397  * 'delta' amount according to the 'incr' flag
398  * It notifies the parent as required.
399  * It handles wraping and non-wraping correctly.
400  * It is assumed that delta>0
401  */
402 static void UPDOWN_DoAction(WND *wndPtr, int delta, BOOL32 incr)
403 {
404   UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr); 
405   int old_val = infoPtr->CurVal;
406   NM_UPDOWN ni;
407
408   TRACE(updown, "%s by %d\n", incr ? "inc" : "dec", delta);
409
410   /* check if we can do the modification first */
411   delta *= (incr ? 1 : -1) * (infoPtr->MaxVal < infoPtr->MinVal ? -1 : 1);
412   if(!UPDOWN_OffsetVal(wndPtr, delta))
413     return;
414
415   /* so, if we can do the change, recompute delta and restore old value */
416   delta = infoPtr->CurVal - old_val;
417   infoPtr->CurVal = old_val;
418
419   /* We must notify parent now to obtain permission */
420   ni.iPos = infoPtr->CurVal;
421   ni.iDelta = delta;
422   ni.hdr.hwndFrom = wndPtr->hwndSelf;
423   ni.hdr.idFrom = wndPtr->wIDmenu;
424   ni.hdr.code = UDN_DELTAPOS; 
425   if(SendMessage32A(wndPtr->parent->hwndSelf, 
426                     WM_NOTIFY, wndPtr->wIDmenu, (LPARAM)&ni))
427     return; /* we are not allowed to change */
428   
429   /* Now adjust value with (maybe new) delta */
430   if(!UPDOWN_OffsetVal(wndPtr, ni.iDelta))
431     return;
432
433   /* Now take care about our buddy */
434   if(!IsWindow32(infoPtr->Buddy)) 
435     return; /* Nothing else to do */
436
437
438   if(wndPtr->dwStyle & UDS_SETBUDDYINT)
439     UPDOWN_SetBuddyInt(wndPtr);
440
441   /* Also, notify it */
442   /* FIXME: do we need to send the notification only if
443             we do not have the UDS_SETBUDDYINT style set? */
444
445   SendMessage32A(GetParent32 (wndPtr->hwndSelf), 
446                  wndPtr->dwStyle & UDS_HORZ ? WM_HSCROLL : WM_VSCROLL, 
447                  MAKELONG(incr ? SB_LINEUP : SB_LINEDOWN, infoPtr->CurVal),
448                  wndPtr->hwndSelf);
449 }
450
451 /***********************************************************************
452  *           UPDOWN_IsEnabled
453  *
454  * Returns TRUE if it is enabled as well as its buddy (if any)
455  *         FALSE otherwise
456  */
457 static BOOL32 UPDOWN_IsEnabled(WND *wndPtr)
458 {
459   UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
460
461   if(wndPtr->dwStyle & WS_DISABLED)
462     return FALSE;
463   return IsWindowEnabled32(infoPtr->Buddy);
464 }
465
466 /***********************************************************************
467  *           UPDOWN_CancelMode
468  *
469  * Deletes any timers, releases the mouse and does  redraw if necessary.
470  * If the control is not in "capture" mode, it does nothing.
471  * If the control was not in cancel mode, it returns FALSE. 
472  * If the control was in cancel mode, it returns TRUE.
473  */
474 static BOOL32 UPDOWN_CancelMode(WND *wndPtr)
475 {
476   UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
477  
478   /* if not in 'capture' mode, do nothing */
479   if(!(infoPtr->Flags & FLAG_CLICKED))
480     return FALSE;
481
482   KillTimer32(wndPtr->hwndSelf, TIMERID1); /* kill all possible timers */
483   KillTimer32(wndPtr->hwndSelf, TIMERID2);
484   
485   if(GetCapture32() == wndPtr->hwndSelf)   /* let the mouse go         */
486     ReleaseCapture();          /* if we still have it      */  
487   
488   infoPtr->Flags = 0;          /* get rid of any flags     */
489   UPDOWN_Refresh (wndPtr);     /* redraw the control just in case */
490   
491   return TRUE;
492 }
493
494 /***********************************************************************
495  *           UPDOWN_HandleMouseEvent
496  *
497  * Handle a mouse event for the updown.
498  * 'pt' is the location of the mouse event in client or
499  * windows coordinates. 
500  */
501 static void UPDOWN_HandleMouseEvent(WND *wndPtr, UINT32 msg, POINT32 pt)
502 {
503   UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
504   RECT32 rect;
505   int temp;
506
507   switch(msg)
508     {
509     case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
510       /* If we are already in the 'clicked' mode, then nothing to do */
511       if(infoPtr->Flags & FLAG_CLICKED)
512         return;
513
514       /* If the buddy is an edit, will set focus to it */
515       if(WIDGETS_IsControl32(WIN_FindWndPtr(infoPtr->Buddy), BIC32_EDIT))
516         SetFocus32(infoPtr->Buddy);
517
518       /* Now see which one is the 'active' arrow */
519       temp = UPDOWN_GetArrowFromPoint(wndPtr, &rect, pt);
520
521       /* Update the CurVal if necessary */
522       if(wndPtr->dwStyle & UDS_SETBUDDYINT)
523         UPDOWN_GetBuddyInt(wndPtr);
524         
525       /* Before we proceed, see if we can spin... */
526       if(!(wndPtr->dwStyle & UDS_WRAP))
527         if(( temp && infoPtr->CurVal==infoPtr->MaxVal) ||
528            (!temp && infoPtr->CurVal==infoPtr->MinVal))
529           return;
530
531       /* Set up the correct flags */
532       infoPtr->Flags  = 0; 
533       infoPtr->Flags |= temp ? FLAG_INCR : FLAG_DECR;
534       infoPtr->Flags |= FLAG_MOUSEIN;
535       
536       /* repaint the control */
537       UPDOWN_Refresh (wndPtr);
538
539       /* process the click */
540       UPDOWN_DoAction(wndPtr, 1, infoPtr->Flags & FLAG_INCR);
541
542       /* now capture all mouse messages */
543       SetCapture32(wndPtr->hwndSelf);
544
545       /* and startup the first timer */
546       SetTimer32(wndPtr->hwndSelf, TIMERID1, INITIAL_DELAY, 0); 
547       break;
548
549     case WM_MOUSEMOVE:
550       /* If we are not in the 'clicked' mode, then nothing to do */
551       if(!(infoPtr->Flags & FLAG_CLICKED))
552         return;
553
554       /* save the flags to see if any got modified */
555       temp = infoPtr->Flags;
556
557       /* Now get the 'active' arrow rectangle */
558       if (infoPtr->Flags & FLAG_INCR)
559         UPDOWN_GetArrowRect(wndPtr, &rect, TRUE);
560       else
561         UPDOWN_GetArrowRect(wndPtr, &rect, FALSE);
562
563       /* Update the flags if we are in/out */
564       if(PtInRect32(&rect, pt))
565         infoPtr->Flags |=  FLAG_MOUSEIN;
566       else{
567         infoPtr->Flags &= ~FLAG_MOUSEIN;
568         if(accelIndex != -1) /* if we have accel info */
569           accelIndex = 0;    /* reset it              */
570       }
571       /* If state changed, redraw the control */
572       if(temp != infoPtr->Flags)
573         UPDOWN_Refresh (wndPtr);
574       break;
575
576       default:
577         ERR(updown, "Impossible case!\n");
578     }
579
580 }
581
582 /***********************************************************************
583  *           UpDownWndProc
584  */
585 LRESULT WINAPI UpDownWindowProc(HWND32 hwnd, UINT32 message, WPARAM32 wParam,
586                                 LPARAM lParam)
587 {
588   WND *wndPtr = WIN_FindWndPtr(hwnd);
589   UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr); 
590   int temp;
591
592   switch(message)
593     {
594     case WM_NCCREATE:
595       /* get rid of border, if any */
596       wndPtr->dwStyle &= ~WS_BORDER;
597       return TRUE;
598
599     case WM_CREATE:
600       infoPtr =
601         (UPDOWN_INFO*)HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY,
602                                  sizeof(UPDOWN_INFO));
603       wndPtr->wExtra[0] = (DWORD)infoPtr;
604
605       /* initialize the info struct */
606       infoPtr->AccelCount=0; infoPtr->AccelVect=0; 
607       infoPtr->CurVal=0; infoPtr->MinVal=0; infoPtr->MaxVal=100; /*FIXME*/
608       infoPtr->Base  = 10; /* Default to base 10  */
609       infoPtr->Buddy = 0;  /* No buddy window yet */
610       infoPtr->Flags = 0;  /* And no flags        */
611
612       /* Do we pick the buddy win ourselves? */
613       if(wndPtr->dwStyle & UDS_AUTOBUDDY)
614         UPDOWN_SetBuddy(wndPtr, GetWindow32(wndPtr->hwndSelf, GW_HWNDPREV));
615         
616       TRACE(updown, "UpDown Ctrl creation, hwnd=%04x\n", hwnd);
617       break;
618     
619     case WM_DESTROY:
620       if(infoPtr->AccelVect)
621         free(infoPtr->AccelVect);
622
623       HeapFree (GetProcessHeap (), 0, infoPtr);
624       wndPtr->wExtra[0] = 0;
625
626       TRACE(updown, "UpDown Ctrl destruction, hwnd=%04x\n", hwnd);
627       break;
628         
629     case WM_ENABLE:
630       if(wndPtr->dwStyle & WS_DISABLED)
631         UPDOWN_CancelMode(wndPtr);
632       UPDOWN_Paint(wndPtr);
633       break;
634
635     case WM_TIMER:
636       /* if initial timer, kill it and start the repeat timer */
637       if(wParam == TIMERID1){
638         KillTimer32(hwnd, TIMERID1);
639         /* if no accel info given, used default timer */
640         if(infoPtr->AccelCount==0 || infoPtr->AccelVect==0){
641           accelIndex = -1;
642           temp = REPEAT_DELAY;
643         }
644         else{
645           accelIndex = 0; /* otherwise, use it */
646           temp = infoPtr->AccelVect[accelIndex].nSec * 1000 + 1;
647         }
648         SetTimer32(hwnd, TIMERID2, temp, 0);
649       }
650
651       /* now, if the mouse is above us, do the thing...*/
652       if(infoPtr->Flags & FLAG_MOUSEIN){
653         temp = accelIndex==-1 ? 1 : infoPtr->AccelVect[accelIndex].nInc;
654         UPDOWN_DoAction(wndPtr, temp, infoPtr->Flags & FLAG_INCR);
655         
656         if(accelIndex!=-1 && accelIndex < infoPtr->AccelCount-1){
657           KillTimer32(hwnd, TIMERID2);
658           accelIndex++; /* move to the next accel info */
659           temp = infoPtr->AccelVect[accelIndex].nSec * 1000 + 1;
660           /* make sure we have at least 1ms intervals */
661           SetTimer32(hwnd, TIMERID2, temp, 0);      
662         }
663       }
664       break;
665
666     case WM_CANCELMODE:
667       UPDOWN_CancelMode(wndPtr);
668       break;
669
670     case WM_LBUTTONUP:
671       if(!UPDOWN_CancelMode(wndPtr))
672         break;
673       /*If we released the mouse and our buddy is an edit */
674       /* we must select all text in it.                   */
675       if(WIDGETS_IsControl32(WIN_FindWndPtr(infoPtr->Buddy), BIC32_EDIT))
676         SendMessage32A(infoPtr->Buddy, EM_SETSEL32, 0, MAKELONG(0, -1));
677       break;
678       
679     case WM_LBUTTONDOWN:
680     case WM_MOUSEMOVE:
681       if(UPDOWN_IsEnabled(wndPtr)){
682         POINT32 pt;
683         CONV_POINT16TO32( (POINT16 *)&lParam, &pt );
684         UPDOWN_HandleMouseEvent( wndPtr, message, pt );
685       }
686     break;
687
688     case WM_KEYDOWN:
689       if((wndPtr->dwStyle & UDS_ARROWKEYS) && UPDOWN_IsEnabled(wndPtr)){
690         switch(wParam){
691         case VK_UP:  
692         case VK_DOWN:
693           UPDOWN_GetBuddyInt(wndPtr);
694           UPDOWN_DoAction(wndPtr, 1, wParam==VK_UP);
695           break;
696         }
697       }
698       break;
699       
700     case WM_PAINT:
701       UPDOWN_Paint(wndPtr);
702       break;
703     
704     case UDM_GETACCEL:
705       if (wParam==0 && lParam==0)    /*if both zero, */
706         return infoPtr->AccelCount;  /*just return the accel count*/
707       if (wParam || lParam){
708         UNKNOWN_PARAM(UDM_GETACCEL, wParam, lParam);
709         return 0;
710       }
711       temp = MIN(infoPtr->AccelCount, wParam);
712       memcpy((void *)lParam, infoPtr->AccelVect, temp*sizeof(UDACCEL));
713       return temp;
714
715     case UDM_SETACCEL:
716       TRACE(updown, "UpDown Ctrl new accel info, hwnd=%04x\n", hwnd);
717       if(infoPtr->AccelVect){
718         free(infoPtr->AccelVect);
719         infoPtr->AccelCount = 0;
720         infoPtr->AccelVect  = 0;
721       }
722       if(wParam==0)
723         return TRUE;
724       infoPtr->AccelVect = malloc(wParam*sizeof(UDACCEL));
725       if(infoPtr->AccelVect==0)
726         return FALSE;
727       memcpy(infoPtr->AccelVect, (void*)lParam, wParam*sizeof(UDACCEL));
728       return TRUE;
729
730     case UDM_GETBASE:
731       if (wParam || lParam)
732         UNKNOWN_PARAM(UDM_GETBASE, wParam, lParam);
733       return infoPtr->Base;
734
735     case UDM_SETBASE:
736       TRACE(updown, "UpDown Ctrl new base(%d), hwnd=%04x\n", 
737                      wParam, hwnd);
738       if ( !(wParam==10 || wParam==16) || lParam)
739         UNKNOWN_PARAM(UDM_SETBASE, wParam, lParam);
740       if (wParam==10 || wParam==16){
741         temp = infoPtr->Base;
742         infoPtr->Base = wParam;
743         return temp;       /* return the prev base */
744       }
745       break;
746
747     case UDM_GETBUDDY:
748       if (wParam || lParam)
749         UNKNOWN_PARAM(UDM_GETBUDDY, wParam, lParam);
750       return infoPtr->Buddy;
751
752     case UDM_SETBUDDY:
753       if (lParam)
754         UNKNOWN_PARAM(UDM_SETBUDDY, wParam, lParam);
755       temp = infoPtr->Buddy;
756       infoPtr->Buddy = wParam;
757       UPDOWN_SetBuddy(wndPtr, wParam);
758       TRACE(updown, "UpDown Ctrl new buddy(%04x), hwnd=%04x\n", 
759                      infoPtr->Buddy, hwnd);
760       return temp;
761
762     case UDM_GETPOS:
763       if (wParam || lParam)
764         UNKNOWN_PARAM(UDM_GETPOS, wParam, lParam);
765       temp = UPDOWN_GetBuddyInt(wndPtr);
766       return MAKELONG(infoPtr->CurVal, temp ? 0 : 1);
767
768     case UDM_SETPOS:
769       if (wParam || HIWORD(lParam))
770         UNKNOWN_PARAM(UDM_GETPOS, wParam, lParam);
771       temp = SLOWORD(lParam);
772       TRACE(updown, "UpDown Ctrl new value(%d), hwnd=%04x\n",
773                      temp, hwnd);
774       if(!UPDOWN_InBounds(wndPtr, temp)){
775         if(temp < infoPtr->MinVal)  
776           temp = infoPtr->MinVal;
777         if(temp > infoPtr->MaxVal)
778           temp = infoPtr->MaxVal;
779       }
780       wParam = infoPtr->CurVal; /* save prev value   */
781       infoPtr->CurVal = temp;   /* set the new value */
782       if(wndPtr->dwStyle & UDS_SETBUDDYINT)
783         UPDOWN_SetBuddyInt(wndPtr);
784       return wParam;            /* return prev value */
785       
786     case UDM_GETRANGE:
787       if (wParam || lParam)
788         UNKNOWN_PARAM(UDM_GETRANGE, wParam, lParam);
789       return MAKELONG(infoPtr->MaxVal, infoPtr->MinVal);
790
791     case UDM_SETRANGE:
792       if (wParam)
793         UNKNOWN_PARAM(UDM_SETRANGE, wParam, lParam); /* we must have:     */
794       infoPtr->MaxVal = SLOWORD(lParam); /* UD_MINVAL <= Max <= UD_MAXVAL */
795       infoPtr->MinVal = SHIWORD(lParam); /* UD_MINVAL <= Min <= UD_MAXVAL */
796                                          /* |Max-Min| <= UD_MAXVAL        */
797       TRACE(updown, "UpDown Ctrl new range(%d to %d), hwnd=%04x\n", 
798                      infoPtr->MinVal, infoPtr->MaxVal, hwnd);
799       break;                             
800
801     case UDM_GETRANGE32:
802       if (wParam)
803         *(LPINT32)wParam = infoPtr->MinVal;
804       if (lParam)
805         *(LPINT32)lParam = infoPtr->MaxVal;
806       break;
807
808     case UDM_SETRANGE32:
809       infoPtr->MinVal = (INT32)wParam;
810       infoPtr->MaxVal = (INT32)lParam;
811       if (infoPtr->MaxVal <= infoPtr->MinVal)
812         infoPtr->MaxVal = infoPtr->MinVal + 1;
813       TRACE(updown, "UpDown Ctrl new range(%d to %d), hwnd=%04x\n", 
814                      infoPtr->MinVal, infoPtr->MaxVal, hwnd);
815       break;
816
817     default: 
818       if (message >= WM_USER) 
819         WARN(updown, "unknown msg %04x wp=%04x lp=%08lx\n", 
820                      message, wParam, lParam );
821       return DefWindowProc32A( hwnd, message, wParam, lParam ); 
822     } 
823
824     return 0;
825 }
826
827 /***********************************************************************
828  * UPDOWN_Register [Internal]
829  *
830  * Registers the updown window class.
831  */
832 void UPDOWN_Register(void)
833 {
834     WNDCLASS32A wndClass;
835
836     if( GlobalFindAtom32A( UPDOWN_CLASS32A ) ) return;
837
838     ZeroMemory( &wndClass, sizeof( WNDCLASS32A ) );
839     wndClass.style         = CS_GLOBALCLASS | CS_VREDRAW;
840     wndClass.lpfnWndProc   = (WNDPROC32)UpDownWindowProc;
841     wndClass.cbClsExtra    = 0;
842     wndClass.cbWndExtra    = sizeof(UPDOWN_INFO*);
843     wndClass.hCursor       = LoadCursor32A( 0, IDC_ARROW32A );
844     wndClass.hbrBackground = (HBRUSH32)(COLOR_3DFACE + 1);
845     wndClass.lpszClassName = UPDOWN_CLASS32A;
846  
847     RegisterClass32A( &wndClass );
848 }
849