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

Last change on this file since 2012 was 1431, checked in by sandervl, 26 years ago

RegisterClass even if atom exists

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