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