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

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

C -> C++, WINE animate, treeview WM_VSCROLL fixed

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