- New implementation of SendMessage, ReceiveMessage, ReplyMessage functions
[wine] / dlls / comctl32 / 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 do 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  *       (tested - they work)
22  *     - integers with thousand separators.
23  *       (fixed bugs. <noel@macadamian.com>)
24  *
25  *   Even though the above list seems rather large, the control seems to
26  *   behave very well so I am confident it does work in most (all) of the
27  *   untested cases.
28  * Problems:
29  *   I do not like the arrows yet, I'll work more on them later on.
30  */
31
32 #include <stdlib.h>
33 #include "commctrl.h"
34 #include "winnls.h"
35 #include "sysmetrics.h"
36 #include "updown.h"
37 #include "win.h"
38 #include "debug.h"
39
40 /* Control configuration constants */
41
42 #define INITIAL_DELAY    500 /* initial timer until auto-increment kicks in */
43 #define REPEAT_DELAY     50  /* delay between auto-increments */
44
45 #define DEFAULT_WIDTH    14  /* default width of the ctrl */
46 #define DEFAULT_XSEP      0  /* default separation between buddy and crtl */
47 #define DEFAULT_ADDTOP    0  /* amount to extend above the buddy window */
48 #define DEFAULT_ADDBOT    0  /* amount to extend below the buddy window */
49
50
51 /* Work constants */
52
53 #define FLAG_INCR        0x01
54 #define FLAG_DECR        0x02
55 #define FLAG_MOUSEIN     0x04
56 #define FLAG_CLICKED     (FLAG_INCR | FLAG_DECR)
57
58 #define TIMERID1         1
59 #define TIMERID2         2
60
61 static int accelIndex = -1;
62
63 #define UNKNOWN_PARAM(msg, wParam, lParam) WARN(updown, \
64         "UpDown Ctrl: Unknown parameter(s) for message " #msg     \
65         "(%04x): wp=%04x lp=%08lx\n", msg, wParam, lParam);
66
67 #define UPDOWN_GetInfoPtr(wndPtr) ((UPDOWN_INFO *)wndPtr->wExtra[0])
68
69
70 /***********************************************************************
71  *           UPDOWN_InBounds
72  * Tests if a given value 'val' is between the Min&Max limits
73  */
74 static BOOL32 UPDOWN_InBounds(WND *wndPtr, int val)
75 {
76   UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
77
78   if(infoPtr->MaxVal > infoPtr->MinVal)
79     return (infoPtr->MinVal <= val) && (val <= infoPtr->MaxVal);
80   else
81     return (infoPtr->MaxVal <= val) && (val <= infoPtr->MinVal);
82 }
83
84 /***********************************************************************
85  *           UPDOWN_OffsetVal
86  * Tests if we can change the current value by delta. If so, it changes
87  * it and returns TRUE. Else, it leaves it unchanged and returns FALSE.
88  */
89 static BOOL32 UPDOWN_OffsetVal(WND *wndPtr, int delta)
90 {
91   UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
92
93   /* check if we can do the modification first */
94   if(!UPDOWN_InBounds(wndPtr, infoPtr->CurVal+delta)){
95     if(wndPtr->dwStyle & UDS_WRAP)
96       delta += (delta < 0 ? -1 : 1) *
97         (infoPtr->MaxVal < infoPtr->MinVal ? -1 : 1) *
98         (infoPtr->MinVal - infoPtr->MaxVal) +
99         (delta < 0 ? 1 : -1);
100     else
101       return FALSE;
102   }
103
104   infoPtr->CurVal += delta;
105   return TRUE;
106 }
107
108 /***********************************************************************
109  *           UPDOWN_GetArrowRect
110  * wndPtr   - pointer to the up-down wnd
111  * rect     - will hold the rectangle
112  * incr     - TRUE  get the "increment" rect (up or right)
113  *            FALSE get the "decrement" rect (down or left)
114  *          
115  */
116 static void UPDOWN_GetArrowRect(WND *wndPtr, RECT32 *rect, BOOL32 incr)
117 {
118   int len; /* will hold the width or height */
119
120   GetClientRect32(wndPtr->hwndSelf, rect);
121
122   if (wndPtr->dwStyle & UDS_HORZ) {
123     len = rect->right - rect->left; /* compute the width */
124     if (incr)
125       rect->left = len/2+1; 
126     else
127       rect->right = len/2;
128   }
129   else {
130     len = rect->bottom - rect->top; /* compute the height */
131     if (incr)
132       rect->bottom = len/2;
133     else
134       rect->top = len/2+1;
135   }
136 }
137
138 /***********************************************************************
139  *           UPDOWN_GetArrowFromPoint
140  * Returns the rectagle (for the up or down arrow) that contains pt.
141  * If it returns the up rect, it returns TRUE.
142  * If it returns the down rect, it returns FALSE.
143  */
144 static int UPDOWN_GetArrowFromPoint(WND *wndPtr, RECT32 *rect, POINT32 pt)
145 {
146   UPDOWN_GetArrowRect(wndPtr, rect, TRUE);
147   if(PtInRect32(rect, pt))
148     return TRUE;
149
150   UPDOWN_GetArrowRect(wndPtr, rect, FALSE);
151   return FALSE;
152 }
153
154
155 /***********************************************************************
156  *           UPDOWN_GetThousandSep
157  * Returns the thousand sep. If an error occurs, it returns ','.
158  */
159 static char UPDOWN_GetThousandSep()
160 {
161   char sep[2];
162
163   if(GetLocaleInfo32A(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, 
164                       sep, sizeof(sep)) != 1)
165     return ',';
166
167   return sep[0];
168 }
169
170 /***********************************************************************
171  *           UPDOWN_GetBuddyInt
172  * Tries to read the pos from the buddy window and if it succeeds,
173  * it stores it in the control's CurVal
174  * returns:
175  *   TRUE  - if it read the integer from the buddy successfully
176  *   FALSE - if an error occured
177  */
178 static BOOL32 UPDOWN_GetBuddyInt(WND *wndPtr)
179 {
180   UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
181   char txt[20], sep, *src, *dst;
182   int newVal;
183
184   if (!IsWindow32(infoPtr->Buddy))
185     return FALSE;
186
187   /*if the buddy is a list window, we must set curr index */
188   if(WIDGETS_IsControl32(WIN_FindWndPtr(infoPtr->Buddy), BIC32_LISTBOX)){
189     newVal = SendMessage32A(infoPtr->Buddy, LB_GETCARETINDEX32, 0, 0);
190     if(newVal < 0)
191       return FALSE;
192   }
193   else{
194     /* we have a regular window, so will get the text */
195     if (!GetWindowText32A(infoPtr->Buddy, txt, sizeof(txt)))
196       return FALSE;
197
198     sep = UPDOWN_GetThousandSep(); 
199
200     /* now get rid of the separators */
201     for(src = dst = txt; *src; src++)
202       if(*src != sep)
203         *dst++ = *src;
204     *dst = 0;
205
206     /* try to convert the number and validate it */
207     newVal = strtol(txt, &src, infoPtr->Base);
208     if(*src || !UPDOWN_InBounds(wndPtr, newVal)) 
209       return FALSE;
210
211     TRACE(updown, "new value(%d) read from buddy (old=%d)\n", 
212                  newVal, infoPtr->CurVal);
213   }
214   
215   infoPtr->CurVal = newVal;
216   return TRUE;
217 }
218
219
220 /***********************************************************************
221  *           UPDOWN_SetBuddyInt
222  * Tries to set the pos to the buddy window based on current pos
223  * returns:
224  *   TRUE  - if it set the caption of the  buddy successfully
225  *   FALSE - if an error occured
226  */
227 static BOOL32 UPDOWN_SetBuddyInt(WND *wndPtr)
228 {
229   UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(wndPtr);
230   char txt1[20], sep;
231   int len;
232
233   if (!IsWindow32(infoPtr->Buddy)) 
234     return FALSE;
235
236   TRACE(updown, "set new value(%d) to buddy.\n",
237                infoPtr->CurVal);
238
239   /*if the buddy is a list window, we must set curr index */
240   if(WIDGETS_IsControl32(WIN_FindWndPtr(infoPtr->Buddy), BIC32_LISTBOX)){
241     SendMessage32A(infoPtr->Buddy, LB_SETCURSEL32, infoPtr->CurVal, 0);
242   }
243   else{ /* Regular window, so set caption to the number */
244     len = sprintf(txt1, (infoPtr->Base==16) ? "%X" : "%d", infoPtr->CurVal);
245
246     sep = UPDOWN_GetThousandSep(); 
247
248     /* Do thousands seperation if necessary */
249     if (!(wndPtr->dwStyle & UDS_NOTHOUSANDS) && (len > 3)) {
250       char txt2[20], *src = txt1, *dst = txt2;
251       if(len%3 > 0){
252         lstrcpyn32A (dst, src, len%3 + 1);      /* need to include the null */ 
253         dst += len%3;
254         src += len%3;
255       }
256       for(len=0; *src; len++){
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 = (UPDOWN_INFO*)COMCTL32_Alloc (sizeof(UPDOWN_INFO));
601       wndPtr->wExtra[0] = (DWORD)infoPtr;
602
603       /* initialize the info struct */
604       infoPtr->AccelCount=0; infoPtr->AccelVect=0; 
605       infoPtr->CurVal=0; infoPtr->MinVal=0; infoPtr->MaxVal=100; /*FIXME*/
606       infoPtr->Base  = 10; /* Default to base 10  */
607       infoPtr->Buddy = 0;  /* No buddy window yet */
608       infoPtr->Flags = 0;  /* And no flags        */
609
610       /* Do we pick the buddy win ourselves? */
611       if(wndPtr->dwStyle & UDS_AUTOBUDDY)
612         UPDOWN_SetBuddy(wndPtr, GetWindow32(wndPtr->hwndSelf, GW_HWNDPREV));
613         
614       TRACE(updown, "UpDown Ctrl creation, hwnd=%04x\n", hwnd);
615       break;
616     
617     case WM_DESTROY:
618       if(infoPtr->AccelVect)
619         COMCTL32_Free (infoPtr->AccelVect);
620
621       COMCTL32_Free (infoPtr);
622       wndPtr->wExtra[0] = 0;
623
624       TRACE(updown, "UpDown Ctrl destruction, hwnd=%04x\n", hwnd);
625       break;
626         
627     case WM_ENABLE:
628       if(wndPtr->dwStyle & WS_DISABLED)
629         UPDOWN_CancelMode(wndPtr);
630       UPDOWN_Paint(wndPtr);
631       break;
632
633     case WM_TIMER:
634       /* if initial timer, kill it and start the repeat timer */
635       if(wParam == TIMERID1){
636         KillTimer32(hwnd, TIMERID1);
637         /* if no accel info given, used default timer */
638         if(infoPtr->AccelCount==0 || infoPtr->AccelVect==0){
639           accelIndex = -1;
640           temp = REPEAT_DELAY;
641         }
642         else{
643           accelIndex = 0; /* otherwise, use it */
644           temp = infoPtr->AccelVect[accelIndex].nSec * 1000 + 1;
645         }
646         SetTimer32(hwnd, TIMERID2, temp, 0);
647       }
648
649       /* now, if the mouse is above us, do the thing...*/
650       if(infoPtr->Flags & FLAG_MOUSEIN){
651         temp = accelIndex==-1 ? 1 : infoPtr->AccelVect[accelIndex].nInc;
652         UPDOWN_DoAction(wndPtr, temp, infoPtr->Flags & FLAG_INCR);
653         
654         if(accelIndex!=-1 && accelIndex < infoPtr->AccelCount-1){
655           KillTimer32(hwnd, TIMERID2);
656           accelIndex++; /* move to the next accel info */
657           temp = infoPtr->AccelVect[accelIndex].nSec * 1000 + 1;
658           /* make sure we have at least 1ms intervals */
659           SetTimer32(hwnd, TIMERID2, temp, 0);      
660         }
661       }
662       break;
663
664     case WM_CANCELMODE:
665       UPDOWN_CancelMode(wndPtr);
666       break;
667
668     case WM_LBUTTONUP:
669       if(!UPDOWN_CancelMode(wndPtr))
670         break;
671       /*If we released the mouse and our buddy is an edit */
672       /* we must select all text in it.                   */
673       if(WIDGETS_IsControl32(WIN_FindWndPtr(infoPtr->Buddy), BIC32_EDIT))
674         SendMessage32A(infoPtr->Buddy, EM_SETSEL32, 0, MAKELONG(0, -1));
675       break;
676       
677     case WM_LBUTTONDOWN:
678     case WM_MOUSEMOVE:
679       if(UPDOWN_IsEnabled(wndPtr)){
680         POINT32 pt;
681         CONV_POINT16TO32( (POINT16 *)&lParam, &pt );
682         UPDOWN_HandleMouseEvent( wndPtr, message, pt );
683       }
684     break;
685
686     case WM_KEYDOWN:
687       if((wndPtr->dwStyle & UDS_ARROWKEYS) && UPDOWN_IsEnabled(wndPtr)){
688         switch(wParam){
689         case VK_UP:  
690         case VK_DOWN:
691           UPDOWN_GetBuddyInt(wndPtr);
692           UPDOWN_DoAction(wndPtr, 1, wParam==VK_UP);
693           break;
694         }
695       }
696       break;
697       
698     case WM_PAINT:
699       UPDOWN_Paint(wndPtr);
700       break;
701     
702     case UDM_GETACCEL:
703       if (wParam==0 && lParam==0)    /*if both zero, */
704         return infoPtr->AccelCount;  /*just return the accel count*/
705       if (wParam || lParam){
706         UNKNOWN_PARAM(UDM_GETACCEL, wParam, lParam);
707         return 0;
708       }
709       temp = MIN(infoPtr->AccelCount, wParam);
710       memcpy((void *)lParam, infoPtr->AccelVect, temp*sizeof(UDACCEL));
711       return temp;
712
713     case UDM_SETACCEL:
714       TRACE(updown, "UpDown Ctrl new accel info, hwnd=%04x\n", hwnd);
715       if(infoPtr->AccelVect){
716         COMCTL32_Free (infoPtr->AccelVect);
717         infoPtr->AccelCount = 0;
718         infoPtr->AccelVect  = 0;
719       }
720       if(wParam==0)
721         return TRUE;
722       infoPtr->AccelVect = COMCTL32_Alloc (wParam*sizeof(UDACCEL));
723       if(infoPtr->AccelVect==0)
724         return FALSE;
725       memcpy(infoPtr->AccelVect, (void*)lParam, wParam*sizeof(UDACCEL));
726       return TRUE;
727
728     case UDM_GETBASE:
729       if (wParam || lParam)
730         UNKNOWN_PARAM(UDM_GETBASE, wParam, lParam);
731       return infoPtr->Base;
732
733     case UDM_SETBASE:
734       TRACE(updown, "UpDown Ctrl new base(%d), hwnd=%04x\n", 
735                      wParam, hwnd);
736       if ( !(wParam==10 || wParam==16) || lParam)
737         UNKNOWN_PARAM(UDM_SETBASE, wParam, lParam);
738       if (wParam==10 || wParam==16){
739         temp = infoPtr->Base;
740         infoPtr->Base = wParam;
741         return temp;       /* return the prev base */
742       }
743       break;
744
745     case UDM_GETBUDDY:
746       if (wParam || lParam)
747         UNKNOWN_PARAM(UDM_GETBUDDY, wParam, lParam);
748       return infoPtr->Buddy;
749
750     case UDM_SETBUDDY:
751       if (lParam)
752         UNKNOWN_PARAM(UDM_SETBUDDY, wParam, lParam);
753       temp = infoPtr->Buddy;
754       infoPtr->Buddy = wParam;
755       UPDOWN_SetBuddy(wndPtr, wParam);
756       TRACE(updown, "UpDown Ctrl new buddy(%04x), hwnd=%04x\n", 
757                      infoPtr->Buddy, hwnd);
758       return temp;
759
760     case UDM_GETPOS:
761       if (wParam || lParam)
762         UNKNOWN_PARAM(UDM_GETPOS, wParam, lParam);
763       temp = UPDOWN_GetBuddyInt(wndPtr);
764       return MAKELONG(infoPtr->CurVal, temp ? 0 : 1);
765
766     case UDM_SETPOS:
767       if (wParam || HIWORD(lParam))
768         UNKNOWN_PARAM(UDM_GETPOS, wParam, lParam);
769       temp = SLOWORD(lParam);
770       TRACE(updown, "UpDown Ctrl new value(%d), hwnd=%04x\n",
771                      temp, hwnd);
772       if(!UPDOWN_InBounds(wndPtr, temp)){
773         if(temp < infoPtr->MinVal)  
774           temp = infoPtr->MinVal;
775         if(temp > infoPtr->MaxVal)
776           temp = infoPtr->MaxVal;
777       }
778       wParam = infoPtr->CurVal; /* save prev value   */
779       infoPtr->CurVal = temp;   /* set the new value */
780       if(wndPtr->dwStyle & UDS_SETBUDDYINT)
781         UPDOWN_SetBuddyInt(wndPtr);
782       return wParam;            /* return prev value */
783       
784     case UDM_GETRANGE:
785       if (wParam || lParam)
786         UNKNOWN_PARAM(UDM_GETRANGE, wParam, lParam);
787       return MAKELONG(infoPtr->MaxVal, infoPtr->MinVal);
788
789     case UDM_SETRANGE:
790       if (wParam)
791         UNKNOWN_PARAM(UDM_SETRANGE, wParam, lParam); /* we must have:     */
792       infoPtr->MaxVal = SLOWORD(lParam); /* UD_MINVAL <= Max <= UD_MAXVAL */
793       infoPtr->MinVal = SHIWORD(lParam); /* UD_MINVAL <= Min <= UD_MAXVAL */
794                                          /* |Max-Min| <= UD_MAXVAL        */
795       TRACE(updown, "UpDown Ctrl new range(%d to %d), hwnd=%04x\n", 
796                      infoPtr->MinVal, infoPtr->MaxVal, hwnd);
797       break;                             
798
799     case UDM_GETRANGE32:
800       if (wParam)
801         *(LPINT32)wParam = infoPtr->MinVal;
802       if (lParam)
803         *(LPINT32)lParam = infoPtr->MaxVal;
804       break;
805
806     case UDM_SETRANGE32:
807       infoPtr->MinVal = (INT32)wParam;
808       infoPtr->MaxVal = (INT32)lParam;
809       if (infoPtr->MaxVal <= infoPtr->MinVal)
810         infoPtr->MaxVal = infoPtr->MinVal + 1;
811       TRACE(updown, "UpDown Ctrl new range(%d to %d), hwnd=%04x\n", 
812                      infoPtr->MinVal, infoPtr->MaxVal, hwnd);
813       break;
814
815     default: 
816       if (message >= WM_USER) 
817         ERR (updown, "unknown msg %04x wp=%04x lp=%08lx\n", 
818              message, wParam, lParam);
819       return DefWindowProc32A (hwnd, message, wParam, lParam); 
820     } 
821
822     return 0;
823 }
824
825
826 /***********************************************************************
827  *              UPDOWN_Register [Internal]
828  *
829  * Registers the updown window class.
830  */
831
832 VOID
833 UPDOWN_Register(void)
834 {
835     WNDCLASS32A wndClass;
836
837     if( GlobalFindAtom32A( UPDOWN_CLASS32A ) ) return;
838
839     ZeroMemory( &wndClass, sizeof( WNDCLASS32A ) );
840     wndClass.style         = CS_GLOBALCLASS | CS_VREDRAW;
841     wndClass.lpfnWndProc   = (WNDPROC32)UpDownWindowProc;
842     wndClass.cbClsExtra    = 0;
843     wndClass.cbWndExtra    = sizeof(UPDOWN_INFO*);
844     wndClass.hCursor       = LoadCursor32A( 0, IDC_ARROW32A );
845     wndClass.hbrBackground = (HBRUSH32)(COLOR_3DFACE + 1);
846     wndClass.lpszClassName = UPDOWN_CLASS32A;
847  
848     RegisterClass32A( &wndClass );
849 }
850
851
852 /***********************************************************************
853  *              UPDOWN_Unregister       [Internal]
854  *
855  * Unregisters the updown window class.
856  */
857
858 VOID
859 UPDOWN_Unregister (VOID)
860 {
861     if (GlobalFindAtom32A (UPDOWN_CLASS32A))
862         UnregisterClass32A (UPDOWN_CLASS32A, (HINSTANCE32)NULL);
863 }
864