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