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

Last change on this file since 6823 was 5416, checked in by sandervl, 24 years ago

Resync with Wine + previous merge fixes

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