source: trunk/src/comctl32/updown.c@ 942

Last change on this file since 942 was 942, checked in by cbratschi, 26 years ago

bugs fixed

File size: 33.9 KB
Line 
1/* $Id: updown.c,v 1.8 1999-09-15 16:31:49 cbratschi Exp $ */
2/*
3 * Updown control
4 *
5 * Copyright 1997 Dimitrie O. Paun
6 * Copyright 1999 Achim Hasenmueller
7 * Copyright 1999 Christoph Bratschi
8 *
9 * TODO:
10 * - I am not sure about the default values for the Min, Max, Pos
11 * (in the UPDOWN_INFO the fields: MinVal, MaxVal, CurVal)
12 * - I think I do not handle correctly the WS_BORDER style.
13 * (Should be fixed. <ekohl@abo.rhein-zeitung.de>)
14 *
15 * Testing:
16 * Not much. The following have not been tested at all:
17 * - horizontal arrows
18 * - listbox as buddy window
19 * - acceleration
20 * - base 16 -> bug: 9->B (should be A)
21 * - UDS_ALIGNLEFT, ~UDS_WRAP
22 * (tested - they work)
23 * - integers with thousand separators.
24 * (fixed bugs. <noel@macadamian.com>)
25 *
26 * Even though the above list seems rather large, the control seems to
27 * behave very well so I am confident it does work in most (all) of the
28 * untested cases.
29 * Problems:
30 * I do not like the arrows yet, I'll work more on them later on.
31 */
32
33/* CB: Odin32 problems
34 - Open32 doesn't look for CS_DBLCLKS -> double clicks always sent
35*/
36
37#include <stdlib.h>
38#include <stdio.h>
39#include <string.h>
40
41#include "winbase.h"
42#include "winuser.h"
43#include "commctrl.h"
44#include "winnls.h"
45#include "updown.h"
46
47/* Control configuration constants */
48
49#define INITIAL_DELAY 500 /* initial timer until auto-increment kicks in */
50#define REPEAT_DELAY 50 /* delay between auto-increments */
51
52#define DEFAULT_WIDTH 14 /* default width of the ctrl */
53#define DEFAULT_XSEP 0 /* default separation between buddy and crtl */
54#define DEFAULT_ADDTOP 0 /* amount to extend above the buddy window */
55#define DEFAULT_ADDBOT 0 /* amount to extend below the buddy window */
56#define DEFAULT_BUDDYBORDER 2 /* Width/height of the buddy border */
57
58
59/* Work constants */
60
61#define FLAG_INCR 0x01
62#define FLAG_DECR 0x02
63#define FLAG_MOUSEIN 0x04
64#define FLAG_CLICKED (FLAG_INCR | FLAG_DECR)
65
66#define TIMERID1 1
67#define TIMERID2 2
68#define BUDDY_UPDOWN_HWND "buddyUpDownHWND"
69#define BUDDY_SUPERCLASS_WNDPROC "buddySupperClassWndProc"
70
71static int accelIndex = -1;
72
73//#define UNKNOWN_PARAM(msg, wParam, lParam) WARN(updown, \
74// "UpDown Ctrl: Unknown parameter(s) for message " #msg \
75// "(%04x): wp=%04x lp=%08lx\n", msg, wParam, lParam);
76#define UNKNOWN_PARAM(msg, wParam, lParam)
77
78#define UPDOWN_GetInfoPtr(hwnd) ((UPDOWN_INFO *)GetWindowLongA(hwnd,0))
79
80static LRESULT CALLBACK
81UPDOWN_Buddy_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
82
83/***********************************************************************
84 * UPDOWN_InBounds
85 * Tests if a given value 'val' is between the Min&Max limits
86 */
87static BOOL UPDOWN_InBounds(HWND hwnd, int val)
88{
89 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd);
90
91 if(infoPtr->MaxVal > infoPtr->MinVal)
92 return (infoPtr->MinVal <= val) && (val <= infoPtr->MaxVal);
93 else
94 return (infoPtr->MaxVal <= val) && (val <= infoPtr->MinVal);
95}
96
97/***********************************************************************
98 * UPDOWN_OffsetVal
99 * Tests if we can change the current value by delta. If so, it changes
100 * it and returns TRUE. Else, it leaves it unchanged and returns FALSE.
101 */
102static BOOL UPDOWN_OffsetVal(HWND hwnd, int delta)
103{
104 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd);
105
106 /* check if we can do the modification first */
107 if(!UPDOWN_InBounds (hwnd, infoPtr->CurVal+delta)){
108 if (GetWindowLongA (hwnd, GWL_STYLE) & UDS_WRAP)
109 {
110 delta += (delta < 0 ? -1 : 1) *
111 (infoPtr->MaxVal < infoPtr->MinVal ? -1 : 1) *
112 (infoPtr->MinVal - infoPtr->MaxVal) +
113 (delta < 0 ? 1 : -1);
114 }
115 else
116 return FALSE;
117 }
118
119 infoPtr->CurVal += delta;
120 return TRUE;
121}
122
123/***********************************************************************
124 * UPDOWN_HasBuddyBorder [Internal]
125 *
126 * When we have a buddy set and that we are aligned on our buddy, we
127 * want to draw a sunken edge to make like we are part of that control.
128 */
129static BOOL UPDOWN_HasBuddyBorder(HWND hwnd)
130{
131 UPDOWN_INFO* infoPtr = UPDOWN_GetInfoPtr (hwnd);
132 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
133
134 return ( ((dwStyle & (UDS_ALIGNLEFT | UDS_ALIGNRIGHT)) != 0) &&
135 (SendMessageA(hwnd, UDM_GETBUDDY, 0, 0) != 0) &&
136 (lstrcmpiA(infoPtr->szBuddyClass, "EDIT") == 0 ) );
137}
138
139/***********************************************************************
140 * UPDOWN_GetArrowRect
141 * wndPtr - pointer to the up-down wnd
142 * rect - will hold the rectangle
143 * incr - TRUE get the "increment" rect (up or right)
144 * FALSE get the "decrement" rect (down or left)
145 *
146 */
147static void UPDOWN_GetArrowRect (HWND hwnd, RECT *rect, BOOL incr)
148{
149 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
150 int len; /* will hold the width or height */
151
152 GetClientRect (hwnd, rect);
153
154 /*
155 * Make sure we calculate the rectangle to fit even if we draw the
156 * border.
157 */
158 if (UPDOWN_HasBuddyBorder(hwnd))
159 {
160 if (dwStyle & UDS_ALIGNLEFT)
161 rect->left+=DEFAULT_BUDDYBORDER;
162 else
163 rect->right-=DEFAULT_BUDDYBORDER;
164
165 InflateRect(rect, 0, -DEFAULT_BUDDYBORDER);
166 }
167
168 /*
169 * We're calculating the midpoint to figure-out where the
170 * separation between the buttons will lay. We make sure that we
171 * round the uneven numbers by adding 1.
172 */
173 if (dwStyle & UDS_HORZ) {
174 len = rect->right - rect->left + 1; /* compute the width */
175 if (incr)
176 rect->left = rect->left + len/2;
177 else
178 rect->right = rect->left + len/2;
179 }
180 else {
181 len = rect->bottom - rect->top + 1; /* compute the height */
182 if (incr)
183 rect->bottom = rect->top + len/2;
184 else
185 rect->top = rect->top + len/2;
186 }
187}
188
189/***********************************************************************
190 * UPDOWN_GetArrowFromPoint
191 * Returns the rectagle (for the up or down arrow) that contains pt.
192 * If it returns the up rect, it returns TRUE.
193 * If it returns the down rect, it returns FALSE.
194 */
195static BOOL
196UPDOWN_GetArrowFromPoint (HWND hwnd, RECT *rect, POINT pt)
197{
198 UPDOWN_GetArrowRect (hwnd, rect, TRUE);
199 if(PtInRect(rect, pt))
200 return TRUE;
201
202 UPDOWN_GetArrowRect (hwnd, rect, FALSE);
203 return FALSE;
204}
205
206
207/***********************************************************************
208 * UPDOWN_GetThousandSep
209 * Returns the thousand sep. If an error occurs, it returns ','.
210 */
211static char UPDOWN_GetThousandSep(VOID)
212{
213 char sep[2];
214
215 if(GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND,
216 sep, sizeof(sep)) != 1)
217 return ',';
218
219 return sep[0];
220}
221
222/***********************************************************************
223 * UPDOWN_GetBuddyInt
224 * Tries to read the pos from the buddy window and if it succeeds,
225 * it stores it in the control's CurVal
226 * returns:
227 * TRUE - if it read the integer from the buddy successfully
228 * FALSE - if an error occured
229 */
230static BOOL UPDOWN_GetBuddyInt (HWND hwnd)
231{
232 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd);
233 char txt[20], sep, *src, *dst;
234 int newVal;
235
236 if (!IsWindow(infoPtr->Buddy)) return FALSE;
237
238 /*if the buddy is a list window, we must set curr index */
239 if (!lstrcmpiA(infoPtr->szBuddyClass,"ListBox"))
240 {
241 newVal = SendMessageA(infoPtr->Buddy,LB_GETCARETINDEX,0,0);
242 if(newVal < 0) return FALSE;
243 } else
244 {
245 /* we have a regular window, so will get the text */
246 if (!GetWindowTextA(infoPtr->Buddy,txt,sizeof(txt))) return FALSE;
247
248 sep = UPDOWN_GetThousandSep();
249
250 /* now get rid of the separators */
251 for(src = dst = txt;*src;src++)
252 if (*src != sep)
253 *dst++ = *src;
254 *dst = 0;
255
256 /* try to convert the number and validate it */
257 newVal = strtol(txt,&src,infoPtr->Base);
258 if (*src || !UPDOWN_InBounds(hwnd,newVal)) return FALSE;
259
260// TRACE(updown, "new value(%d) read from buddy (old=%d)\n",
261// newVal, infoPtr->CurVal);
262 }
263
264 infoPtr->CurVal = newVal;
265
266 return TRUE;
267}
268
269
270/***********************************************************************
271 * UPDOWN_SetBuddyInt
272 * Tries to set the pos to the buddy window based on current pos
273 * returns:
274 * TRUE - if it set the caption of the buddy successfully
275 * FALSE - if an error occured
276 */
277static BOOL UPDOWN_SetBuddyInt (HWND hwnd)
278{
279 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
280 char txt1[20],sep;
281 int len;
282
283 if (!IsWindow(infoPtr->Buddy)) return FALSE;
284
285// TRACE(updown, "set new value(%d) to buddy.\n",
286// infoPtr->CurVal);
287
288 /*if the buddy is a list window, we must set curr index */
289 if(!lstrcmpiA(infoPtr->szBuddyClass, "ListBox"))
290 {
291 SendMessageA(infoPtr->Buddy,LB_SETCURSEL,infoPtr->CurVal,0);
292 } else
293 { /* Regular window, so set caption to the number */
294 len = sprintf(txt1,(infoPtr->Base == 16) ? "%X" : "%d",infoPtr->CurVal);
295
296 sep = UPDOWN_GetThousandSep();
297
298 /* Do thousands seperation if necessary */
299 if (!(GetWindowLongA (hwnd, GWL_STYLE) & UDS_NOTHOUSANDS) && (len > 3)) {
300 char txt2[20], *src = txt1, *dst = txt2;
301 if(len%3 > 0){
302 lstrcpynA (dst, src, len%3 + 1); /* need to include the null */
303 dst += len%3;
304 src += len%3;
305 }
306 for(len=0; *src; len++){
307 if(len%3==0)
308 *dst++ = sep;
309 *dst++ = *src++;
310 }
311 *dst = 0; /* null terminate it */
312 strcpy(txt1, txt2); /* move it to the proper place */
313 }
314 SetWindowTextA(infoPtr->Buddy, txt1);
315 }
316
317 return TRUE;
318}
319
320/***********************************************************************
321 * UPDOWN_DrawBuddyBorder [Internal]
322 *
323 * When we have a buddy set and that we are aligned on our buddy, we
324 * want to draw a sunken edge to make like we are part of that control.
325 */
326static void UPDOWN_DrawBuddyBorder (HWND hwnd, HDC hdc)
327{
328 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
329 RECT clientRect;
330
331 GetClientRect(hwnd, &clientRect);
332
333 if (dwStyle & UDS_ALIGNLEFT)
334 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_BOTTOM | BF_LEFT | BF_TOP);
335 else
336 DrawEdge(hdc, &clientRect, EDGE_SUNKEN, BF_BOTTOM | BF_RIGHT | BF_TOP);
337}
338
339/***********************************************************************
340 * UPDOWN_Draw [Internal]
341 *
342 * Draw the arrows. The background need not be erased.
343 */
344static void UPDOWN_Draw(HWND hwnd,HDC hdc)
345{
346 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
347 DWORD dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
348 BOOL prssed;
349 RECT rect;
350
351 /*
352 * Draw the common border between ourselves and our buddy.
353 */
354 if (UPDOWN_HasBuddyBorder(hwnd))
355 UPDOWN_DrawBuddyBorder(hwnd, hdc);
356
357 /* Draw the incr button */
358 UPDOWN_GetArrowRect (hwnd, &rect, TRUE);
359 prssed = (infoPtr->Flags & FLAG_INCR) && (infoPtr->Flags & FLAG_MOUSEIN);
360 DrawFrameControl(hdc, &rect, DFC_SCROLL,
361 (dwStyle & UDS_HORZ ? DFCS_SCROLLLEFT : DFCS_SCROLLUP) |
362 (prssed ? DFCS_PUSHED : 0) |
363 (dwStyle&WS_DISABLED ? DFCS_INACTIVE : 0) );
364
365 /* Draw the space between the buttons */
366 rect.top = rect.bottom;
367 rect.bottom++;
368 DrawEdge(hdc, &rect, 0, BF_MIDDLE);
369
370 /* Draw the decr button */
371 UPDOWN_GetArrowRect(hwnd, &rect, FALSE);
372 prssed = (infoPtr->Flags & FLAG_DECR) && (infoPtr->Flags & FLAG_MOUSEIN);
373 DrawFrameControl(hdc, &rect, DFC_SCROLL,
374 (dwStyle & UDS_HORZ ? DFCS_SCROLLRIGHT : DFCS_SCROLLDOWN) |
375 (prssed ? DFCS_PUSHED : 0) |
376 (dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) );
377}
378
379/***********************************************************************
380 * UPDOWN_Refresh [Internal]
381 *
382 * Synchronous drawing (must NOT be used in WM_PAINT).
383 * Calls UPDOWN_Draw.
384 */
385static void UPDOWN_Refresh (HWND hwnd)
386{
387 HDC hdc;
388
389 hdc = GetDC (hwnd);
390 UPDOWN_Draw (hwnd, hdc);
391 ReleaseDC (hwnd, hdc);
392}
393
394
395/***********************************************************************
396 * UPDOWN_Paint [Internal]
397 *
398 * Asynchronous drawing (must ONLY be used in WM_PAINT).
399 * Calls UPDOWN_Draw.
400 */
401static void UPDOWN_Paint (HWND hwnd, HDC passedDC)
402{
403 PAINTSTRUCT ps;
404 HDC hdc = passedDC;
405
406 if (passedDC == 0)
407 hdc = BeginPaint (hwnd, &ps);
408
409 UPDOWN_Draw (hwnd, hdc);
410
411 if (passedDC == 0)
412 EndPaint (hwnd, &ps);
413}
414
415/***********************************************************************
416 * UPDOWN_SetBuddyHandle
417 * CB: UPDOWN_SetBuddy == message handler
418 * Tests if 'hwndBud' is a valid window handle. If not, returns FALSE.
419 * Else, sets it as a new Buddy.
420 * Then, it should subclass the buddy
421 * If window has the UDS_ARROWKEYS, it subcalsses the buddy window to
422 * process the UP/DOWN arrow keys.
423 * If window has the UDS_ALIGNLEFT or UDS_ALIGNRIGHT style
424 * the size/pos of the buddy and the control are adjusted accordingly.
425 */
426static BOOL UPDOWN_SetBuddyHandle (HWND hwnd, HWND hwndBud)
427{
428 UPDOWN_INFO* infoPtr = UPDOWN_GetInfoPtr (hwnd);
429 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
430 RECT budRect; /* new coord for the buddy */
431 int x,width; /* new x position and width for the up-down */
432
433 /* Is it a valid bud? */
434 if(!IsWindow(hwndBud))
435 return FALSE;
436
437 /* there is already a body assigned */
438 if ( infoPtr->Buddy )
439 RemovePropA(infoPtr->Buddy, BUDDY_UPDOWN_HWND);
440
441 /* Store buddy window handle */
442 infoPtr->Buddy = hwndBud;
443
444 /* keep upDown ctrl hwnd in a buddy property */
445 SetPropA( hwndBud, BUDDY_UPDOWN_HWND, hwnd);
446
447 /* Store buddy window clas name */
448 memset(infoPtr->szBuddyClass, 0, UPDOWN_BUDDYCLASSNAMELEN);
449 GetClassNameA (hwndBud, infoPtr->szBuddyClass, UPDOWN_BUDDYCLASSNAMELEN-1);
450
451 if(dwStyle & UDS_ARROWKEYS){
452 /* Note that I don't clear the BUDDY_SUPERCLASS_WNDPROC property
453 when we reset the upDown ctrl buddy to another buddy because it is not
454 good to break the window proc chain. */
455
456 /* keep buddy supperclass wndproc in prop instead of in ptr struct
457 to prevent accessing freed memory */
458 SetPropA(
459 hwndBud,
460 BUDDY_SUPERCLASS_WNDPROC,
461 (LONG)GetWindowLongA(hwndBud, GWL_WNDPROC) );
462
463 /* Assign the buddy wndproc to local wndproc in order to override
464 keyboard's up and down arrow */
465 SetWindowLongA(
466 hwndBud,
467 GWL_WNDPROC,
468 (LONG)UPDOWN_Buddy_SubclassProc);
469 }
470
471 /* do we need to do any adjustments? */
472 if(!(dwStyle & (UDS_ALIGNLEFT | UDS_ALIGNRIGHT)))
473 return TRUE;
474
475 /* Get the rect of the buddy relative to its parent */
476 GetWindowRect(infoPtr->Buddy, &budRect);
477 MapWindowPoints(HWND_DESKTOP, GetParent(infoPtr->Buddy),
478 (POINT *)(&budRect.left), 2);
479
480 /* now do the positioning */
481 if(dwStyle & UDS_ALIGNRIGHT){
482 budRect.right -= DEFAULT_WIDTH+DEFAULT_XSEP;
483 x = budRect.right+DEFAULT_XSEP;
484 }
485 else{ /* UDS_ALIGNLEFT */
486 x = budRect.left;
487 budRect.left += DEFAULT_WIDTH+DEFAULT_XSEP;
488 }
489
490 /* first adjust the buddy to accomodate the up/down */
491 SetWindowPos(infoPtr->Buddy, 0, budRect.left, budRect.top,
492 budRect.right - budRect.left, budRect.bottom - budRect.top,
493 SWP_NOACTIVATE|SWP_NOZORDER);
494
495 /* now position the up/down */
496 /* Since the UDS_ALIGN* flags were used, */
497 /* we will pick the position and size of the window. */
498 width = DEFAULT_WIDTH;
499
500 /*
501 * If the updown has a buddy border, it has to overlap with the buddy
502 * to look as if it is integrated with the buddy control.
503 * We nudge the control or change it size to overlap.
504 */
505 if (UPDOWN_HasBuddyBorder(hwnd))
506 {
507 if(dwStyle & UDS_ALIGNRIGHT)
508 x-=DEFAULT_BUDDYBORDER;
509 else
510 width+=DEFAULT_BUDDYBORDER;
511 }
512
513 SetWindowPos (hwnd, infoPtr->Buddy,
514 x, budRect.top-DEFAULT_ADDTOP,
515 width, (budRect.bottom-budRect.top)+DEFAULT_ADDTOP+DEFAULT_ADDBOT,
516 SWP_NOACTIVATE);
517
518 return TRUE;
519}
520
521/***********************************************************************
522 * UPDOWN_DoAction
523 *
524 * This function increments/decrements the CurVal by the
525 * 'delta' amount according to the 'incr' flag
526 * It notifies the parent as required.
527 * It handles wraping and non-wraping correctly.
528 * It is assumed that delta>0
529 */
530static void UPDOWN_DoAction (HWND hwnd, int delta, BOOL incr)
531{
532 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
533 DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
534 int old_val = infoPtr->CurVal;
535 NM_UPDOWN ni;
536
537// TRACE(updown, "%s by %d\n", incr ? "inc" : "dec", delta);
538
539 /* check if we can do the modification first */
540 delta *= (incr ? 1 : -1) * (infoPtr->MaxVal < infoPtr->MinVal ? -1 : 1);
541 if(!UPDOWN_OffsetVal(hwnd,delta)) return;
542
543 /* so, if we can do the change, recompute delta and restore old value */
544 delta = infoPtr->CurVal-old_val;
545 infoPtr->CurVal = old_val;
546
547 /* We must notify parent now to obtain permission */
548 ni.iPos = infoPtr->CurVal;
549 ni.iDelta = delta;
550 ni.hdr.hwndFrom = hwnd;
551 ni.hdr.idFrom = GetWindowLongA(hwnd,GWL_ID);
552 ni.hdr.code = UDN_DELTAPOS;
553 if (SendMessageA(GetParent(hwnd),WM_NOTIFY,
554 (WPARAM)ni.hdr.idFrom,(LPARAM)&ni))
555 return; /* we are not allowed to change */
556
557 /* Now adjust value with (maybe new) delta */
558 if (!UPDOWN_OffsetVal (hwnd,ni.iDelta)) return;
559
560 /* Now take care about our buddy */
561 if(!IsWindow(infoPtr->Buddy))
562 return; /* Nothing else to do */
563
564
565 if (dwStyle & UDS_SETBUDDYINT) UPDOWN_SetBuddyInt(hwnd);
566
567 /* Also, notify it */
568 /* FIXME: do we need to send the notification only if
569 we do not have the UDS_SETBUDDYINT style set? */
570
571 SendMessageA (GetParent (hwnd),
572 dwStyle & UDS_HORZ ? WM_HSCROLL : WM_VSCROLL,
573 MAKELONG(incr ? SB_LINEUP : SB_LINEDOWN, infoPtr->CurVal),
574 hwnd);
575}
576
577/***********************************************************************
578 * UPDOWN_IsEnabled
579 *
580 * Returns TRUE if it is enabled as well as its buddy (if any)
581 * FALSE otherwise
582 */
583static BOOL UPDOWN_IsEnabled (HWND hwnd)
584{
585 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd);
586
587 if(GetWindowLongA (hwnd, GWL_STYLE) & WS_DISABLED)
588 return FALSE;
589 return IsWindowEnabled(infoPtr->Buddy);
590}
591
592/***********************************************************************
593 * UPDOWN_CancelMode
594 *
595 * Deletes any timers, releases the mouse and does redraw if necessary.
596 * If the control is not in "capture" mode, it does nothing.
597 * If the control was not in cancel mode, it returns FALSE.
598 * If the control was in cancel mode, it returns TRUE.
599 */
600static BOOL UPDOWN_CancelMode (HWND hwnd)
601{
602 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd);
603
604 /* if not in 'capture' mode, do nothing */
605 if(!(infoPtr->Flags & FLAG_CLICKED))
606 return FALSE;
607
608 KillTimer (hwnd, TIMERID1); /* kill all possible timers */
609 KillTimer (hwnd, TIMERID2);
610
611 if (GetCapture() == hwnd) /* let the mouse go */
612 ReleaseCapture(); /* if we still have it */
613
614 infoPtr->Flags = 0; /* get rid of any flags */
615 UPDOWN_Refresh (hwnd); /* redraw the control just in case */
616
617 return TRUE;
618}
619
620/***********************************************************************
621 * UPDOWN_HandleMouseEvent
622 *
623 * Handle a mouse event for the updown.
624 * 'pt' is the location of the mouse event in client or
625 * windows coordinates.
626 */
627static void UPDOWN_HandleMouseEvent (HWND hwnd, UINT msg, POINT pt)
628{
629 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd);
630 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
631 RECT rect;
632 int temp;
633
634 switch(msg)
635 {
636 case WM_LBUTTONDOWN: /* Initialise mouse tracking */
637 /* If we are already in the 'clicked' mode, then nothing to do */
638 if (infoPtr->Flags & FLAG_CLICKED) return;
639
640 /* If the buddy is an edit, will set focus to it */
641 if (!lstrcmpiA(infoPtr->szBuddyClass,"Edit")) SetFocus(infoPtr->Buddy);
642
643 /* Now see which one is the 'active' arrow */
644 temp = UPDOWN_GetArrowFromPoint(hwnd,&rect,pt);
645
646 /* Update the CurVal if necessary */
647 if (dwStyle & UDS_SETBUDDYINT) UPDOWN_GetBuddyInt(hwnd);
648
649 /* Before we proceed, see if we can spin... */
650 if (!(dwStyle & UDS_WRAP))
651 if ((temp && infoPtr->CurVal == infoPtr->MaxVal) ||
652 (!temp && infoPtr->CurVal == infoPtr->MinVal))
653 return;
654
655 /* Set up the correct flags */
656 infoPtr->Flags = 0;
657 infoPtr->Flags |= temp ? FLAG_INCR : FLAG_DECR;
658 infoPtr->Flags |= FLAG_MOUSEIN;
659
660 /* repaint the control */
661 UPDOWN_Refresh (hwnd);
662
663 /* process the click */
664 UPDOWN_DoAction (hwnd,1,infoPtr->Flags & FLAG_INCR);
665
666 /* now capture all mouse messages */
667 SetCapture (hwnd);
668
669 /* and startup the first timer */
670 SetTimer(hwnd,TIMERID1,INITIAL_DELAY,0);
671 break;
672
673 case WM_MOUSEMOVE:
674 /* If we are not in the 'clicked' mode, then nothing to do */
675 if (!(infoPtr->Flags & FLAG_CLICKED)) return;
676
677 /* save the flags to see if any got modified */
678 temp = infoPtr->Flags;
679
680 /* Now get the 'active' arrow rectangle */
681 if (infoPtr->Flags & FLAG_INCR)
682 UPDOWN_GetArrowRect (hwnd, &rect, TRUE);
683 else
684 UPDOWN_GetArrowRect (hwnd, &rect, FALSE);
685
686 /* Update the flags if we are in/out */
687 if(PtInRect(&rect, pt))
688 infoPtr->Flags |= FLAG_MOUSEIN;
689 else{
690 infoPtr->Flags &= ~FLAG_MOUSEIN;
691 if(accelIndex != -1) /* if we have accel info */
692 accelIndex = 0; /* reset it */
693 }
694 /* If state changed, redraw the control */
695 if(temp != infoPtr->Flags)
696 UPDOWN_Refresh (hwnd);
697 break;
698
699 default:
700// ERR(updown, "Impossible case!\n");
701 break;
702 }
703
704}
705
706//Message handling
707
708static LRESULT UPDOWN_NCCreate(HWND hwnd,WPARAM wParam,LPARAM lParam)
709{
710 DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
711
712 /* get rid of border, if any */
713 SetWindowLongA(hwnd,GWL_STYLE,dwStyle & ~WS_BORDER);
714 return TRUE;
715}
716
717static LRESULT UPDOWN_Create(HWND hwnd,WPARAM wParam,LPARAM lParam)
718{
719 UPDOWN_INFO *infoPtr;
720 DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
721
722 infoPtr = (UPDOWN_INFO*)COMCTL32_Alloc(sizeof(UPDOWN_INFO));
723 SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
724
725 /* initialize the info struct */
726 infoPtr->AccelCount = 0;
727 infoPtr->AccelVect = 0;
728 infoPtr->CurVal = 0;
729 infoPtr->MinVal = 0;
730 infoPtr->MaxVal = 100; /*FIXME*/
731 infoPtr->Base = 10; /* Default to base 10 */
732 infoPtr->Buddy = 0; /* No buddy window yet */
733 infoPtr->Flags = 0; /* And no flags */
734
735 /* Do we pick the buddy win ourselves? */
736 if (dwStyle & UDS_AUTOBUDDY) UPDOWN_SetBuddyHandle(hwnd,GetWindow(hwnd,GW_HWNDPREV));
737
738// TRACE(updown, "UpDown Ctrl creation, hwnd=%04x\n", hwnd);
739
740 return 0;
741}
742
743static LRESULT UPDOWN_Destroy(HWND hwnd,WPARAM wParam,LPARAM lParam)
744{
745 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
746
747 if(infoPtr->AccelVect) COMCTL32_Free(infoPtr->AccelVect);
748
749 if ( IsWindow(infoPtr->Buddy) ) /* Cleanup */
750 RemovePropA(infoPtr->Buddy, BUDDY_UPDOWN_HWND);
751
752 COMCTL32_Free (infoPtr);
753
754// TRACE(updown, "UpDown Ctrl destruction, hwnd=%04x\n", hwnd);
755
756 return 0;
757}
758
759static LRESULT UPDOWN_Enable(HWND hwnd,WPARAM wParam,LPARAM lParam)
760{
761 DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
762
763 if (dwStyle & WS_DISABLED) UPDOWN_CancelMode(hwnd);
764
765 UPDOWN_Refresh(hwnd);
766
767 return 0;
768}
769
770static LRESULT UPDOWN_Timer(HWND hwnd,WPARAM wParam,LPARAM lParam)
771{
772 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
773 int temp;
774
775 /* if initial timer, kill it and start the repeat timer */
776 if(wParam == TIMERID1)
777 {
778 KillTimer(hwnd, TIMERID1);
779 /* if no accel info given, used default timer */
780 if(infoPtr->AccelCount==0 || infoPtr->AccelVect==0)
781 {
782 accelIndex = -1;
783 temp = REPEAT_DELAY;
784 } else
785 {
786 accelIndex = 0; /* otherwise, use it */
787 temp = infoPtr->AccelVect[accelIndex].nSec * 1000 + 1;
788 }
789 SetTimer(hwnd, TIMERID2, temp, 0);
790 }
791
792 /* now, if the mouse is above us, do the thing...*/
793 if(infoPtr->Flags & FLAG_MOUSEIN)
794 {
795 temp = accelIndex==-1 ? 1 : infoPtr->AccelVect[accelIndex].nInc;
796 UPDOWN_DoAction(hwnd, temp, infoPtr->Flags & FLAG_INCR);
797
798 if(accelIndex!=-1 && accelIndex < infoPtr->AccelCount-1)
799 {
800 KillTimer(hwnd, TIMERID2);
801 accelIndex++; /* move to the next accel info */
802 temp = infoPtr->AccelVect[accelIndex].nSec * 1000 + 1;
803 /* make sure we have at least 1ms intervals */
804 SetTimer(hwnd, TIMERID2, temp, 0);
805 }
806 }
807
808 return 0;
809}
810
811static LRESULT UPDOWN_LButtonUp(HWND hwnd,WPARAM wParam,LPARAM lParam)
812{
813 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
814
815 if (!UPDOWN_CancelMode(hwnd)) return 0;
816 /*If we released the mouse and our buddy is an edit */
817 /* we must select all text in it. */
818 if (!lstrcmpiA(infoPtr->szBuddyClass,"Edit"))
819 SendMessageA(infoPtr->Buddy,EM_SETSEL,0,MAKELONG(0,-1));
820
821 return 0;
822}
823
824static LRESULT UPDOWN_KeyDown(HWND hwnd,WPARAM wParam,LPARAM lParam)
825{
826 DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
827
828 if((dwStyle & UDS_ARROWKEYS) && UPDOWN_IsEnabled(hwnd))
829 {
830 switch(wParam)
831 {
832 case VK_UP:
833 case VK_DOWN:
834 UPDOWN_GetBuddyInt (hwnd);
835 UPDOWN_DoAction (hwnd, 1, wParam==VK_UP);
836 break;
837 }
838 }
839
840 return 0;
841}
842
843static LRESULT UPDOWN_GetAccel(HWND hwnd,WPARAM wParam,LPARAM lParam)
844{
845 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
846 INT temp;
847
848 if (wParam == 0 && lParam == 0) /*if both zero, */
849 return infoPtr->AccelCount; /*just return the accel count*/
850 if (wParam || lParam)
851 {
852 UNKNOWN_PARAM(UDM_GETACCEL,wParam,lParam);
853 return 0;
854 }
855 temp = MIN(infoPtr->AccelCount,wParam);
856 memcpy((void *)lParam,infoPtr->AccelVect,temp*sizeof(UDACCEL));
857
858 return temp;
859}
860
861static LRESULT UPDOWN_SetAccel(HWND hwnd,WPARAM wParam,LPARAM lParam)
862{
863 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
864
865 // TRACE(updown, "UpDown Ctrl new accel info, hwnd=%04x\n", hwnd);
866 if(infoPtr->AccelVect)
867 {
868 COMCTL32_Free(infoPtr->AccelVect);
869 infoPtr->AccelCount = 0;
870 infoPtr->AccelVect = 0;
871 }
872 if(wParam == 0) return TRUE;
873 infoPtr->AccelVect = COMCTL32_Alloc(wParam*sizeof(UDACCEL));
874 if(infoPtr->AccelVect == 0) return FALSE;
875 memcpy(infoPtr->AccelVect,(void*)lParam,wParam*sizeof(UDACCEL));
876
877 return TRUE;
878}
879
880static LRESULT UPDOWN_GetBase(HWND hwnd,WPARAM wParam,LPARAM lParam)
881{
882 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
883
884 if (wParam || lParam) UNKNOWN_PARAM(UDM_GETBASE,wParam,lParam);
885
886 return infoPtr->Base;
887}
888
889static LRESULT UPDOWN_SetBase(HWND hwnd,WPARAM wParam,LPARAM lParam)
890{
891 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
892 INT temp;
893
894 // TRACE(updown, "UpDown Ctrl new base(%d), hwnd=%04x\n",
895 // wParam, hwnd);
896 if (!(wParam==10 || wParam==16) || lParam) UNKNOWN_PARAM(UDM_SETBASE,wParam,lParam);
897 if (wParam==10 || wParam==16)
898 {
899 temp = infoPtr->Base;
900 infoPtr->Base = wParam;
901 return temp; /* return the prev base */
902 }
903
904 return 0;
905}
906
907static LRESULT UPDOWN_GetBuddy(HWND hwnd,WPARAM wParam,LPARAM lParam)
908{
909 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
910
911 if (wParam || lParam) UNKNOWN_PARAM(UDM_GETBUDDY,wParam,lParam);
912
913 return infoPtr->Buddy;
914}
915
916
917static LRESULT UPDOWN_SetBuddy(HWND hwnd,WPARAM wParam,LPARAM lParam)
918{
919 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
920 INT temp;
921
922 if (lParam) UNKNOWN_PARAM(UDM_SETBUDDY,wParam,lParam);
923 temp = infoPtr->Buddy;
924 UPDOWN_SetBuddyHandle(hwnd,wParam);
925// TRACE(updown, "UpDown Ctrl new buddy(%04x), hwnd=%04x\n",
926// infoPtr->Buddy, hwnd);
927
928 return temp;
929}
930
931static LRESULT UPDOWN_GetPos(HWND hwnd,WPARAM wParam,LPARAM lParam)
932{
933 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
934 INT temp;
935
936 if (wParam || lParam) UNKNOWN_PARAM(UDM_GETPOS, wParam, lParam);
937 temp = UPDOWN_GetBuddyInt(hwnd);
938
939 return MAKELONG(infoPtr->CurVal,temp ? 0:1);
940}
941
942static LRESULT UPDOWN_SetPos(HWND hwnd,WPARAM wParam,LPARAM lParam)
943{
944 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
945 DWORD dwStyle = GetWindowLongA(hwnd,GWL_STYLE);
946 INT temp;
947
948 if (wParam || HIWORD(lParam)) UNKNOWN_PARAM(UDM_GETPOS,wParam,lParam);
949 temp = SLOWORD(lParam);
950// TRACE(updown, "UpDown Ctrl new value(%d), hwnd=%04x\n",
951// temp, hwnd);
952 if (!UPDOWN_InBounds(hwnd, temp))
953 {
954 if (temp < infoPtr->MinVal) temp = infoPtr->MinVal;
955 if (temp > infoPtr->MaxVal) temp = infoPtr->MaxVal;
956 }
957 wParam = infoPtr->CurVal; /* save prev value */
958 infoPtr->CurVal = temp; /* set the new value */
959 if (dwStyle & UDS_SETBUDDYINT) UPDOWN_SetBuddyInt (hwnd);
960
961 return wParam; /* return prev value */
962}
963
964static LRESULT UPDOWN_GetRange(HWND hwnd,WPARAM wParam,LPARAM lParam)
965{
966 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
967
968 if (wParam || lParam) UNKNOWN_PARAM(UDM_GETRANGE, wParam, lParam);
969
970 return MAKELONG(infoPtr->MaxVal,infoPtr->MinVal);
971}
972
973static LRESULT UPDOWN_SetRange(HWND hwnd,WPARAM wParam,LPARAM lParam)
974{
975 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
976
977 if (wParam) UNKNOWN_PARAM(UDM_SETRANGE,wParam,lParam); /* we must have: */
978 infoPtr->MaxVal = SLOWORD(lParam); /* UD_MINVAL <= Max <= UD_MAXVAL */
979 infoPtr->MinVal = SHIWORD(lParam); /* UD_MINVAL <= Min <= UD_MAXVAL */
980 /* |Max-Min| <= UD_MAXVAL */
981// TRACE(updown, "UpDown Ctrl new range(%d to %d), hwnd=%04x\n",
982// infoPtr->MinVal, infoPtr->MaxVal, hwnd);
983
984 return 0;
985}
986
987static LRESULT UPDOWN_GetRange32(HWND hwnd,WPARAM wParam,LPARAM lParam)
988{
989 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
990
991 if (wParam) *(LPINT)wParam = infoPtr->MinVal;
992 if (lParam) *(LPINT)lParam = infoPtr->MaxVal;
993
994 return 0;
995}
996
997static LRESULT UPDOWN_SetRange32(HWND hwnd,WPARAM wParam,LPARAM lParam)
998{
999 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(hwnd);
1000
1001 infoPtr->MinVal = (INT)wParam;
1002 infoPtr->MaxVal = (INT)lParam;
1003 if (infoPtr->MaxVal <= infoPtr->MinVal) infoPtr->MaxVal = infoPtr->MinVal+1;
1004// TRACE(updown, "UpDown Ctrl new range(%d to %d), hwnd=%04x\n",
1005// infoPtr->MinVal, infoPtr->MaxVal, hwnd);
1006
1007 return 0;
1008}
1009
1010/***********************************************************************
1011 * UpDownWndProc
1012 */
1013static LRESULT WINAPI UpDownWindowProc(HWND hwnd, UINT message, WPARAM wParam,
1014 LPARAM lParam)
1015{
1016 switch(message)
1017 {
1018 case WM_NCCREATE:
1019 return UPDOWN_NCCreate(hwnd,wParam,lParam);
1020
1021 case WM_CREATE:
1022 return UPDOWN_Create(hwnd,wParam,lParam);
1023
1024 case WM_DESTROY:
1025 return UPDOWN_Destroy(hwnd,wParam,lParam);
1026
1027 case WM_ENABLE:
1028 return UPDOWN_Enable(hwnd,wParam,lParam);
1029
1030 case WM_TIMER:
1031 return UPDOWN_Timer(hwnd,wParam,lParam);
1032
1033 case WM_CANCELMODE:
1034 UPDOWN_CancelMode(hwnd);
1035 break;
1036
1037 case WM_LBUTTONUP:
1038 return UPDOWN_LButtonUp(hwnd,wParam,lParam);
1039
1040 case WM_LBUTTONDOWN:
1041 case WM_MOUSEMOVE:
1042 if(UPDOWN_IsEnabled(hwnd))
1043 {
1044 POINT pt;
1045 CONV_POINT16TO32((POINT16 *)&lParam,&pt);
1046 UPDOWN_HandleMouseEvent(hwnd,message,pt);
1047 }
1048 break;
1049
1050 case WM_KEYDOWN:
1051 return UPDOWN_KeyDown(hwnd,wParam,lParam);
1052
1053 case WM_PAINT:
1054 UPDOWN_Paint(hwnd,(HDC)wParam);
1055 break;
1056
1057 case UDM_GETACCEL:
1058 return UPDOWN_GetAccel(hwnd,wParam,lParam);
1059
1060 case UDM_SETACCEL:
1061 return UPDOWN_SetAccel(hwnd,wParam,lParam);
1062
1063 case UDM_GETBASE:
1064 return UPDOWN_GetBase(hwnd,wParam,lParam);
1065
1066 case UDM_SETBASE:
1067 return UPDOWN_SetBase(hwnd,wParam,lParam);
1068
1069 case UDM_GETBUDDY:
1070 return UPDOWN_GetBuddy(hwnd,wParam,lParam);
1071
1072 case UDM_SETBUDDY:
1073 return UPDOWN_SetBuddy(hwnd,wParam,lParam);
1074
1075 case UDM_GETPOS:
1076 return UPDOWN_GetPos(hwnd,wParam,lParam);
1077
1078 case UDM_SETPOS:
1079 return UPDOWN_SetPos(hwnd,wParam,lParam);
1080
1081 case UDM_GETRANGE:
1082 return UPDOWN_GetRange(hwnd,wParam,lParam);
1083
1084 case UDM_SETRANGE:
1085 return UPDOWN_SetRange(hwnd,wParam,lParam);
1086
1087 case UDM_GETRANGE32:
1088 return UPDOWN_GetRange32(hwnd,wParam,lParam);
1089
1090 case UDM_SETRANGE32:
1091 return UPDOWN_SetRange32(hwnd,wParam,lParam);
1092
1093 default:
1094// if (message >= WM_USER)
1095// ERR (updown, "unknown msg %04x wp=%04x lp=%08lx\n",
1096// message, wParam, lParam);
1097 return DefWindowProcA(hwnd,message,wParam,lParam);
1098 }
1099
1100 return 0;
1101}
1102
1103/***********************************************************************
1104 * UPDOWN_Buddy_SubclassProc used to handle messages sent to the buddy
1105 * control.
1106 */
1107LRESULT CALLBACK
1108UPDOWN_Buddy_SubclassProc (
1109 HWND hwnd,
1110 UINT uMsg,
1111 WPARAM wParam,
1112 LPARAM lParam)
1113{
1114 LONG superClassWndProc = GetPropA(hwnd, BUDDY_SUPERCLASS_WNDPROC);
1115 //TRACE("hwnd=%04x, wndProc=%d, uMsg=%04x, wParam=%d, lParam=%d\n",
1116 // hwnd, (INT)superClassWndProc, uMsg, wParam, (UINT)lParam);
1117
1118 switch (uMsg)
1119 {
1120 case WM_KEYDOWN:
1121 {
1122 if ( ((int)wParam == VK_UP ) || ((int)wParam == VK_DOWN ) )
1123 {
1124 HWND upDownHwnd = GetPropA(hwnd, BUDDY_UPDOWN_HWND);
1125 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr(upDownHwnd);
1126
1127 if (!lstrcmpA (infoPtr->szBuddyClass, "ListBox"))
1128 {
1129 /* if the buddy is a list window, we must update curr index */
1130 INT oldVal = SendMessageA(hwnd, LB_GETCURSEL, 0, 0);
1131 SendMessageA(hwnd, LB_SETCURSEL, oldVal+1, 0);
1132 }
1133 else
1134 {
1135 UPDOWN_GetBuddyInt(upDownHwnd);
1136 UPDOWN_DoAction(upDownHwnd, 1, wParam==VK_UP);
1137 }
1138
1139 break;
1140 }
1141 /* else Fall Through */
1142 }
1143
1144 default:
1145 return CallWindowProcA( (WNDPROC)superClassWndProc, hwnd, uMsg, wParam, lParam);
1146 }
1147
1148 return 0;
1149}
1150
1151/***********************************************************************
1152 * UPDOWN_Register [Internal]
1153 *
1154 * Registers the updown window class.
1155 */
1156
1157VOID
1158UPDOWN_Register(void)
1159{
1160 WNDCLASSA wndClass;
1161
1162 if( GlobalFindAtomA( UPDOWN_CLASSA ) ) return;
1163
1164 ZeroMemory( &wndClass, sizeof( WNDCLASSA ) );
1165 wndClass.style = CS_GLOBALCLASS | CS_VREDRAW;
1166 wndClass.lpfnWndProc = (WNDPROC)UpDownWindowProc;
1167 wndClass.cbClsExtra = 0;
1168 wndClass.cbWndExtra = sizeof(UPDOWN_INFO*);
1169 wndClass.hCursor = LoadCursorA( 0, IDC_ARROWA );
1170 wndClass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
1171 wndClass.lpszClassName = UPDOWN_CLASSA;
1172
1173 RegisterClassA( &wndClass );
1174}
1175
1176
1177/***********************************************************************
1178 * UPDOWN_Unregister [Internal]
1179 *
1180 * Unregisters the updown window class.
1181 */
1182
1183VOID
1184UPDOWN_Unregister (VOID)
1185{
1186 if (GlobalFindAtomA (UPDOWN_CLASSA))
1187 UnregisterClassA (UPDOWN_CLASSA, (HINSTANCE)NULL);
1188}
1189
Note: See TracBrowser for help on using the repository browser.