source: trunk/src/comctl32/updown.cpp@ 3831

Last change on this file since 3831 was 3154, checked in by cbratschi, 25 years ago

Corel 20000317 merge, ccbase finished, bug fixes

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