1 /* 2 * Updown control 3 * 4 * Copyright 1997, 2002 Dimitrie O. Paun 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #include <assert.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <stdarg.h> 25 #include <stdio.h> 26 27 #include "windef.h" 28 #include "winbase.h" 29 #include "wingdi.h" 30 #include "winuser.h" 31 #include "winnls.h" 32 #include "commctrl.h" 33 #include "comctl32.h" 34 #include "uxtheme.h" 35 #include "vssym32.h" 36 #include "wine/heap.h" 37 #include "wine/debug.h" 38 39 WINE_DEFAULT_DEBUG_CHANNEL(updown); 40 41 typedef struct 42 { 43 HWND Self; /* Handle to this up-down control */ 44 HWND Notify; /* Handle to the parent window */ 45 DWORD dwStyle; /* The GWL_STYLE for this window */ 46 UINT AccelCount; /* Number of elements in AccelVect */ 47 UDACCEL* AccelVect; /* Vector containing AccelCount elements */ 48 INT AccelIndex; /* Current accel index, -1 if not accel'ing */ 49 INT Base; /* Base to display nr in the buddy window */ 50 INT CurVal; /* Current up-down value */ 51 INT MinVal; /* Minimum up-down value */ 52 INT MaxVal; /* Maximum up-down value */ 53 HWND Buddy; /* Handle to the buddy window */ 54 INT BuddyType; /* Remembers the buddy type BUDDY_TYPE_* */ 55 INT Flags; /* Internal Flags FLAG_* */ 56 BOOL UnicodeFormat; /* Marks the use of Unicode internally */ 57 } UPDOWN_INFO; 58 59 /* Control configuration constants */ 60 61 #define INITIAL_DELAY 500 /* initial timer until auto-inc kicks in */ 62 #define AUTOPRESS_DELAY 250 /* time to keep arrow pressed on KEY_DOWN */ 63 #define REPEAT_DELAY 50 /* delay between auto-increments */ 64 65 #define DEFAULT_WIDTH 16 /* default width of the ctrl */ 66 #define DEFAULT_XSEP 0 /* default separation between buddy and ctrl */ 67 #define DEFAULT_ADDTOP 0 /* amount to extend above the buddy window */ 68 #define DEFAULT_ADDBOT 0 /* amount to extend below the buddy window */ 69 #define DEFAULT_BUDDYBORDER 2 /* Width/height of the buddy border */ 70 #define DEFAULT_BUDDYSPACER 2 /* Spacer between the buddy and the ctrl */ 71 #define DEFAULT_BUDDYBORDER_THEMED 1 /* buddy border when theming is enabled */ 72 #define DEFAULT_BUDDYSPACER_THEMED 0 /* buddy spacer when theming is enabled */ 73 74 /* Work constants */ 75 76 #define FLAG_INCR 0x01 77 #define FLAG_DECR 0x02 78 #define FLAG_MOUSEIN 0x04 79 #define FLAG_PRESSED 0x08 80 #define FLAG_BUDDYINT 0x10 /* UDS_SETBUDDYINT was set on creation */ 81 #define FLAG_ARROW (FLAG_INCR | FLAG_DECR) 82 83 #define BUDDY_TYPE_UNKNOWN 0 84 #define BUDDY_TYPE_LISTBOX 1 85 #define BUDDY_TYPE_EDIT 2 86 87 #define TIMER_AUTOREPEAT 1 88 #define TIMER_ACCEL 2 89 #define TIMER_AUTOPRESS 3 90 91 #define UPDOWN_GetInfoPtr(hwnd) ((UPDOWN_INFO *)GetWindowLongPtrW (hwnd,0)) 92 93 /* id used for SetWindowSubclass */ 94 #define BUDDY_SUBCLASSID 1 95 96 static void UPDOWN_DoAction (UPDOWN_INFO *infoPtr, int delta, int action); 97 98 /*********************************************************************** 99 * UPDOWN_IsBuddyEdit 100 * Tests if our buddy is an edit control. 101 */ 102 static inline BOOL UPDOWN_IsBuddyEdit(const UPDOWN_INFO *infoPtr) 103 { 104 return infoPtr->BuddyType == BUDDY_TYPE_EDIT; 105 } 106 107 /*********************************************************************** 108 * UPDOWN_IsBuddyListbox 109 * Tests if our buddy is a listbox control. 110 */ 111 static inline BOOL UPDOWN_IsBuddyListbox(const UPDOWN_INFO *infoPtr) 112 { 113 return infoPtr->BuddyType == BUDDY_TYPE_LISTBOX; 114 } 115 116 /*********************************************************************** 117 * UPDOWN_InBounds 118 * Tests if a given value 'val' is between the Min&Max limits 119 */ 120 static BOOL UPDOWN_InBounds(const UPDOWN_INFO *infoPtr, int val) 121 { 122 if(infoPtr->MaxVal > infoPtr->MinVal) 123 return (infoPtr->MinVal <= val) && (val <= infoPtr->MaxVal); 124 else 125 return (infoPtr->MaxVal <= val) && (val <= infoPtr->MinVal); 126 } 127 128 /*********************************************************************** 129 * UPDOWN_OffsetVal 130 * Change the current value by delta. 131 * It returns TRUE is the value was changed successfully, or FALSE 132 * if the value was not changed, as it would go out of bounds. 133 */ 134 static BOOL UPDOWN_OffsetVal(UPDOWN_INFO *infoPtr, int delta) 135 { 136 /* check if we can do the modification first */ 137 if(!UPDOWN_InBounds (infoPtr, infoPtr->CurVal+delta)) { 138 if (infoPtr->dwStyle & UDS_WRAP) { 139 delta += (delta < 0 ? -1 : 1) * 140 (infoPtr->MaxVal < infoPtr->MinVal ? -1 : 1) * 141 (infoPtr->MinVal - infoPtr->MaxVal) + 142 (delta < 0 ? 1 : -1); 143 } else if ((infoPtr->MaxVal > infoPtr->MinVal && infoPtr->CurVal+delta > infoPtr->MaxVal) 144 || (infoPtr->MaxVal < infoPtr->MinVal && infoPtr->CurVal+delta < infoPtr->MaxVal)) { 145 delta = infoPtr->MaxVal - infoPtr->CurVal; 146 } else { 147 delta = infoPtr->MinVal - infoPtr->CurVal; 148 } 149 } 150 151 infoPtr->CurVal += delta; 152 return delta != 0; 153 } 154 155 /*********************************************************************** 156 * UPDOWN_HasBuddyBorder 157 * 158 * When we have a buddy set and that we are aligned on our buddy, we 159 * want to draw a sunken edge to make like we are part of that control. 160 */ 161 static BOOL UPDOWN_HasBuddyBorder(const UPDOWN_INFO *infoPtr) 162 { 163 return ( ((infoPtr->dwStyle & (UDS_ALIGNLEFT | UDS_ALIGNRIGHT)) != 0) && 164 UPDOWN_IsBuddyEdit(infoPtr) ); 165 } 166 167 /*********************************************************************** 168 * UPDOWN_GetArrowRect 169 * wndPtr - pointer to the up-down wnd 170 * rect - will hold the rectangle 171 * arrow - FLAG_INCR to get the "increment" rect (up or right) 172 * FLAG_DECR to get the "decrement" rect (down or left) 173 */ 174 static void UPDOWN_GetArrowRect (const UPDOWN_INFO* infoPtr, RECT *rect, unsigned int arrow) 175 { 176 HTHEME theme = GetWindowTheme (infoPtr->Self); 177 const int border = theme ? DEFAULT_BUDDYBORDER_THEMED : DEFAULT_BUDDYBORDER; 178 const int spacer = theme ? DEFAULT_BUDDYSPACER_THEMED : DEFAULT_BUDDYSPACER; 179 int size; 180 181 assert(arrow && (arrow & (FLAG_INCR | FLAG_DECR)) != (FLAG_INCR | FLAG_DECR)); 182 183 GetClientRect (infoPtr->Self, rect); 184 185 /* 186 * Make sure we calculate the rectangle to fit even if we draw the 187 * border. 188 */ 189 if (UPDOWN_HasBuddyBorder(infoPtr)) { 190 if (infoPtr->dwStyle & UDS_ALIGNLEFT) 191 rect->left += border; 192 else 193 rect->right -= border; 194 195 InflateRect(rect, 0, -border); 196 } 197 198 /* now figure out if we need a space away from the buddy */ 199 if (IsWindow(infoPtr->Buddy) ) { 200 if (infoPtr->dwStyle & UDS_ALIGNLEFT) rect->right -= spacer; 201 else if (infoPtr->dwStyle & UDS_ALIGNRIGHT) rect->left += spacer; 202 } 203 204 /* 205 * We're calculating the midpoint to figure-out where the 206 * separation between the buttons will lay. 207 */ 208 if (infoPtr->dwStyle & UDS_HORZ) { 209 size = (rect->right - rect->left) / 2; 210 if (arrow & FLAG_INCR) 211 rect->left = rect->right - size; 212 else if (arrow & FLAG_DECR) 213 rect->right = rect->left + size; 214 } else { 215 size = (rect->bottom - rect->top) / 2; 216 if (arrow & FLAG_INCR) 217 rect->bottom = rect->top + size; 218 else if (arrow & FLAG_DECR) 219 rect->top = rect->bottom - size; 220 } 221 } 222 223 /*********************************************************************** 224 * UPDOWN_GetArrowFromPoint 225 * Returns the rectagle (for the up or down arrow) that contains pt. 226 * If it returns the up rect, it returns FLAG_INCR. 227 * If it returns the down rect, it returns FLAG_DECR. 228 */ 229 static INT UPDOWN_GetArrowFromPoint (const UPDOWN_INFO *infoPtr, RECT *rect, POINT pt) 230 { 231 UPDOWN_GetArrowRect (infoPtr, rect, FLAG_INCR); 232 if(PtInRect(rect, pt)) return FLAG_INCR; 233 234 UPDOWN_GetArrowRect (infoPtr, rect, FLAG_DECR); 235 if(PtInRect(rect, pt)) return FLAG_DECR; 236 237 return 0; 238 } 239 240 241 /*********************************************************************** 242 * UPDOWN_GetThousandSep 243 * Returns the thousand sep. If an error occurs, it returns ','. 244 */ 245 static WCHAR UPDOWN_GetThousandSep(void) 246 { 247 WCHAR sep[2]; 248 249 if(GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, sep, 2) != 1) 250 sep[0] = ','; 251 252 return sep[0]; 253 } 254 255 /*********************************************************************** 256 * UPDOWN_GetBuddyInt 257 * Tries to read the pos from the buddy window and if it succeeds, 258 * it stores it in the control's CurVal 259 * returns: 260 * TRUE - if it read the integer from the buddy successfully 261 * FALSE - if an error occurred 262 */ 263 static BOOL UPDOWN_GetBuddyInt (UPDOWN_INFO *infoPtr) 264 { 265 WCHAR txt[20], sep, *src, *dst; 266 int newVal; 267 268 if (!((infoPtr->Flags & FLAG_BUDDYINT) && IsWindow(infoPtr->Buddy))) 269 return FALSE; 270 271 /*if the buddy is a list window, we must set curr index */ 272 if (UPDOWN_IsBuddyListbox(infoPtr)) { 273 newVal = SendMessageW(infoPtr->Buddy, LB_GETCARETINDEX, 0, 0); 274 if(newVal < 0) return FALSE; 275 } else { 276 /* we have a regular window, so will get the text */ 277 /* note that a zero-length string is a legitimate value for 'txt', 278 * and ought to result in a successful conversion to '0'. */ 279 if (GetWindowTextW(infoPtr->Buddy, txt, ARRAY_SIZE(txt)) < 0) 280 return FALSE; 281 282 sep = UPDOWN_GetThousandSep(); 283 284 /* now get rid of the separators */ 285 for(src = dst = txt; *src; src++) 286 if(*src != sep) *dst++ = *src; 287 *dst = 0; 288 289 /* try to convert the number and validate it */ 290 newVal = wcstol(txt, &src, infoPtr->Base); 291 if(*src || !UPDOWN_InBounds (infoPtr, newVal)) return FALSE; 292 } 293 294 TRACE("new value(%d) from buddy (old=%d)\n", newVal, infoPtr->CurVal); 295 infoPtr->CurVal = newVal; 296 return TRUE; 297 } 298 299 300 /*********************************************************************** 301 * UPDOWN_SetBuddyInt 302 * Tries to set the pos to the buddy window based on current pos 303 * returns: 304 * TRUE - if it set the caption of the buddy successfully 305 * FALSE - if an error occurred 306 */ 307 static BOOL UPDOWN_SetBuddyInt (const UPDOWN_INFO *infoPtr) 308 { 309 static const WCHAR fmt_hex[] = { '0', 'x', '%', '0', '4', 'X', 0 }; 310 static const WCHAR fmt_dec_oct[] = { '%', 'd', '\0' }; 311 const WCHAR *fmt; 312 WCHAR txt[20], txt_old[20] = { 0 }; 313 int len; 314 315 if (!((infoPtr->Flags & FLAG_BUDDYINT) && IsWindow(infoPtr->Buddy))) 316 return FALSE; 317 318 TRACE("set new value(%d) to buddy.\n", infoPtr->CurVal); 319 320 /*if the buddy is a list window, we must set curr index */ 321 if (UPDOWN_IsBuddyListbox(infoPtr)) { 322 return SendMessageW(infoPtr->Buddy, LB_SETCURSEL, infoPtr->CurVal, 0) != LB_ERR; 323 } 324 325 /* Regular window, so set caption to the number */ 326 fmt = (infoPtr->Base == 16) ? fmt_hex : fmt_dec_oct; 327 len = wsprintfW(txt, fmt, infoPtr->CurVal); 328 329 330 /* Do thousands separation if necessary */ 331 if ((infoPtr->Base == 10) && !(infoPtr->dwStyle & UDS_NOTHOUSANDS) && (len > 3)) { 332 WCHAR tmp[ARRAY_SIZE(txt)], *src = tmp, *dst = txt; 333 WCHAR sep = UPDOWN_GetThousandSep(); 334 int start = len % 3; 335 336 memcpy(tmp, txt, sizeof(txt)); 337 if (start == 0) start = 3; 338 dst += start; 339 src += start; 340 for (len=0; *src; len++) { 341 if (len % 3 == 0) *dst++ = sep; 342 *dst++ = *src++; 343 } 344 *dst = 0; 345 } 346 347 /* if nothing changed exit earlier */ 348 GetWindowTextW(infoPtr->Buddy, txt_old, ARRAY_SIZE(txt_old)); 349 if (lstrcmpiW(txt_old, txt) == 0) return FALSE; 350 351 return SetWindowTextW(infoPtr->Buddy, txt); 352 } 353 354 /*********************************************************************** 355 * UPDOWN_DrawBuddyBackground 356 * 357 * Draw buddy background for visual integration. 358 */ 359 static BOOL UPDOWN_DrawBuddyBackground (const UPDOWN_INFO *infoPtr, HDC hdc) 360 { 361 RECT br, r; 362 HTHEME buddyTheme = GetWindowTheme (infoPtr->Buddy); 363 if (!buddyTheme) return FALSE; 364 365 GetWindowRect (infoPtr->Buddy, &br); 366 MapWindowPoints (NULL, infoPtr->Self, (POINT*)&br, 2); 367 GetClientRect (infoPtr->Self, &r); 368 369 if (infoPtr->dwStyle & UDS_ALIGNLEFT) 370 br.left = r.left; 371 else if (infoPtr->dwStyle & UDS_ALIGNRIGHT) 372 br.right = r.right; 373 /* FIXME: take disabled etc. into account */ 374 DrawThemeBackground (buddyTheme, hdc, 0, 0, &br, NULL); 375 return TRUE; 376 } 377 378 /*********************************************************************** 379 * UPDOWN_Draw 380 * 381 * Draw the arrows. The background need not be erased. 382 */ 383 static LRESULT UPDOWN_Draw (const UPDOWN_INFO *infoPtr, HDC hdc) 384 { 385 BOOL uPressed, uHot, dPressed, dHot; 386 RECT rect; 387 HTHEME theme = GetWindowTheme (infoPtr->Self); 388 int uPart = 0, uState = 0, dPart = 0, dState = 0; 389 BOOL needBuddyBg = FALSE; 390 391 uPressed = (infoPtr->Flags & FLAG_PRESSED) && (infoPtr->Flags & FLAG_INCR); 392 uHot = (infoPtr->Flags & FLAG_INCR) && (infoPtr->Flags & FLAG_MOUSEIN); 393 dPressed = (infoPtr->Flags & FLAG_PRESSED) && (infoPtr->Flags & FLAG_DECR); 394 dHot = (infoPtr->Flags & FLAG_DECR) && (infoPtr->Flags & FLAG_MOUSEIN); 395 if (theme) { 396 uPart = (infoPtr->dwStyle & UDS_HORZ) ? SPNP_UPHORZ : SPNP_UP; 397 uState = (infoPtr->dwStyle & WS_DISABLED) ? DNS_DISABLED 398 : (uPressed ? DNS_PRESSED : (uHot ? DNS_HOT : DNS_NORMAL)); 399 dPart = (infoPtr->dwStyle & UDS_HORZ) ? SPNP_DOWNHORZ : SPNP_DOWN; 400 dState = (infoPtr->dwStyle & WS_DISABLED) ? DNS_DISABLED 401 : (dPressed ? DNS_PRESSED : (dHot ? DNS_HOT : DNS_NORMAL)); 402 needBuddyBg = IsWindow (infoPtr->Buddy) 403 && (IsThemeBackgroundPartiallyTransparent (theme, uPart, uState) 404 || IsThemeBackgroundPartiallyTransparent (theme, dPart, dState)); 405 } 406 407 /* Draw the common border between ourselves and our buddy */ 408 if (UPDOWN_HasBuddyBorder(infoPtr) || needBuddyBg) { 409 if (!theme || !UPDOWN_DrawBuddyBackground (infoPtr, hdc)) { 410 GetClientRect(infoPtr->Self, &rect); 411 DrawEdge(hdc, &rect, EDGE_SUNKEN, 412 BF_BOTTOM | BF_TOP | 413 (infoPtr->dwStyle & UDS_ALIGNLEFT ? BF_LEFT : BF_RIGHT)); 414 } 415 } 416 417 /* Draw the incr button */ 418 UPDOWN_GetArrowRect (infoPtr, &rect, FLAG_INCR); 419 if (theme) { 420 DrawThemeBackground(theme, hdc, uPart, uState, &rect, NULL); 421 } else { 422 DrawFrameControl(hdc, &rect, DFC_SCROLL, 423 (infoPtr->dwStyle & UDS_HORZ ? DFCS_SCROLLRIGHT : DFCS_SCROLLUP) | 424 ((infoPtr->dwStyle & UDS_HOTTRACK) && uHot ? DFCS_HOT : 0) | 425 (uPressed ? DFCS_PUSHED : 0) | 426 (infoPtr->dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) ); 427 } 428 429 /* Draw the decr button */ 430 UPDOWN_GetArrowRect(infoPtr, &rect, FLAG_DECR); 431 if (theme) { 432 DrawThemeBackground(theme, hdc, dPart, dState, &rect, NULL); 433 } else { 434 DrawFrameControl(hdc, &rect, DFC_SCROLL, 435 (infoPtr->dwStyle & UDS_HORZ ? DFCS_SCROLLLEFT : DFCS_SCROLLDOWN) | 436 ((infoPtr->dwStyle & UDS_HOTTRACK) && dHot ? DFCS_HOT : 0) | 437 (dPressed ? DFCS_PUSHED : 0) | 438 (infoPtr->dwStyle & WS_DISABLED ? DFCS_INACTIVE : 0) ); 439 } 440 441 return 0; 442 } 443 444 /*********************************************************************** 445 * UPDOWN_Paint 446 * 447 * Asynchronous drawing (must ONLY be used in WM_PAINT). 448 * Calls UPDOWN_Draw. 449 */ 450 static LRESULT UPDOWN_Paint (const UPDOWN_INFO *infoPtr, HDC hdc) 451 { 452 PAINTSTRUCT ps; 453 if (hdc) return UPDOWN_Draw (infoPtr, hdc); 454 hdc = BeginPaint (infoPtr->Self, &ps); 455 UPDOWN_Draw (infoPtr, hdc); 456 EndPaint (infoPtr->Self, &ps); 457 return 0; 458 } 459 460 /*********************************************************************** 461 * UPDOWN_KeyPressed 462 * 463 * Handle key presses (up & down) when we have to do so 464 */ 465 static LRESULT UPDOWN_KeyPressed(UPDOWN_INFO *infoPtr, int key) 466 { 467 int arrow, accel; 468 469 if (key == VK_UP) arrow = FLAG_INCR; 470 else if (key == VK_DOWN) arrow = FLAG_DECR; 471 else return 1; 472 473 UPDOWN_GetBuddyInt (infoPtr); 474 infoPtr->Flags &= ~FLAG_ARROW; 475 infoPtr->Flags |= FLAG_PRESSED | arrow; 476 InvalidateRect (infoPtr->Self, NULL, FALSE); 477 SetTimer(infoPtr->Self, TIMER_AUTOPRESS, AUTOPRESS_DELAY, 0); 478 accel = (infoPtr->AccelCount && infoPtr->AccelVect) ? infoPtr->AccelVect[0].nInc : 1; 479 UPDOWN_DoAction (infoPtr, accel, arrow); 480 return 0; 481 } 482 483 static int UPDOWN_GetPos(UPDOWN_INFO *infoPtr, BOOL *err) 484 { 485 BOOL succ = UPDOWN_GetBuddyInt(infoPtr); 486 int val = infoPtr->CurVal; 487 488 if(!UPDOWN_InBounds(infoPtr, val)) { 489 if((infoPtr->MinVal < infoPtr->MaxVal && val < infoPtr->MinVal) 490 || (infoPtr->MinVal > infoPtr->MaxVal && val > infoPtr->MinVal)) 491 val = infoPtr->MinVal; 492 else 493 val = infoPtr->MaxVal; 494 495 succ = FALSE; 496 } 497 498 if(err) *err = !succ; 499 return val; 500 } 501 502 static int UPDOWN_SetPos(UPDOWN_INFO *infoPtr, int pos) 503 { 504 int ret = infoPtr->CurVal; 505 506 if(!UPDOWN_InBounds(infoPtr, pos)) { 507 if((infoPtr->MinVal < infoPtr->MaxVal && pos < infoPtr->MinVal) 508 || (infoPtr->MinVal > infoPtr->MaxVal && pos > infoPtr->MinVal)) 509 pos = infoPtr->MinVal; 510 else 511 pos = infoPtr->MaxVal; 512 } 513 514 infoPtr->CurVal = pos; 515 UPDOWN_SetBuddyInt(infoPtr); 516 517 if(!UPDOWN_InBounds(infoPtr, ret)) { 518 if((infoPtr->MinVal < infoPtr->MaxVal && ret < infoPtr->MinVal) 519 || (infoPtr->MinVal > infoPtr->MaxVal && ret > infoPtr->MinVal)) 520 ret = infoPtr->MinVal; 521 else 522 ret = infoPtr->MaxVal; 523 } 524 return ret; 525 } 526 527 528 /*********************************************************************** 529 * UPDOWN_SetRange 530 * 531 * Handle UDM_SETRANGE, UDM_SETRANGE32 532 * 533 * FIXME: handle Max == Min properly: 534 * - arrows should be disabled (without WS_DISABLED set), 535 * visually they can't be pressed and don't respond; 536 * - all input messages should still pass in. 537 */ 538 static LRESULT UPDOWN_SetRange(UPDOWN_INFO *infoPtr, INT Max, INT Min) 539 { 540 infoPtr->MaxVal = Max; 541 infoPtr->MinVal = Min; 542 543 TRACE("UpDown Ctrl new range(%d to %d), hwnd=%p\n", 544 infoPtr->MinVal, infoPtr->MaxVal, infoPtr->Self); 545 546 return 0; 547 } 548 549 /*********************************************************************** 550 * UPDOWN_MouseWheel 551 * 552 * Handle mouse wheel scrolling 553 */ 554 static LRESULT UPDOWN_MouseWheel(UPDOWN_INFO *infoPtr, WPARAM wParam) 555 { 556 int iWheelDelta = GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA; 557 558 if (wParam & (MK_SHIFT | MK_CONTROL)) 559 return 0; 560 561 if (iWheelDelta != 0) 562 { 563 UPDOWN_GetBuddyInt(infoPtr); 564 UPDOWN_DoAction(infoPtr, abs(iWheelDelta), iWheelDelta > 0 ? FLAG_INCR : FLAG_DECR); 565 } 566 567 return 1; 568 } 569 570 571 /*********************************************************************** 572 * UPDOWN_Buddy_SubclassProc used to handle messages sent to the buddy 573 * control. 574 */ 575 static LRESULT CALLBACK 576 UPDOWN_Buddy_SubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, 577 UINT_PTR uId, DWORD_PTR ref_data) 578 { 579 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr((HWND)ref_data); 580 581 TRACE("hwnd=%p, uMsg=%04x, wParam=%08lx, lParam=%08lx\n", 582 hwnd, uMsg, wParam, lParam); 583 584 switch(uMsg) 585 { 586 case WM_KEYDOWN: 587 if (infoPtr) 588 { 589 UPDOWN_KeyPressed(infoPtr, (int)wParam); 590 if (wParam == VK_UP || wParam == VK_DOWN) 591 return 0; 592 } 593 break; 594 595 case WM_MOUSEWHEEL: 596 if (infoPtr) 597 UPDOWN_MouseWheel(infoPtr, (int)wParam); 598 break; 599 600 case WM_NCDESTROY: 601 RemoveWindowSubclass(hwnd, UPDOWN_Buddy_SubclassProc, BUDDY_SUBCLASSID); 602 break; 603 default: 604 break; 605 } 606 607 return DefSubclassProc(hwnd, uMsg, wParam, lParam); 608 } 609 610 static void UPDOWN_ResetSubclass (UPDOWN_INFO *infoPtr) 611 { 612 SetWindowSubclass(infoPtr->Buddy, UPDOWN_Buddy_SubclassProc, BUDDY_SUBCLASSID, 0); 613 } 614 615 /*********************************************************************** 616 * UPDOWN_SetBuddy 617 * 618 * Sets bud as a new Buddy. 619 * Then, it should subclass the buddy 620 * If window has the UDS_ARROWKEYS, it subclasses the buddy window to 621 * process the UP/DOWN arrow keys. 622 * If window has the UDS_ALIGNLEFT or UDS_ALIGNRIGHT style 623 * the size/pos of the buddy and the control are adjusted accordingly. 624 */ 625 static HWND UPDOWN_SetBuddy (UPDOWN_INFO* infoPtr, HWND bud) 626 { 627 RECT budRect; /* new coord for the buddy */ 628 int x, width; /* new x position and width for the up-down */ 629 WCHAR buddyClass[40]; 630 HWND old_buddy; 631 632 TRACE("(hwnd=%p, bud=%p)\n", infoPtr->Self, bud); 633 634 old_buddy = infoPtr->Buddy; 635 636 UPDOWN_ResetSubclass (infoPtr); 637 638 if (!IsWindow(bud)) bud = NULL; 639 640 /* Store buddy window handle */ 641 infoPtr->Buddy = bud; 642 643 if(bud) { 644 /* Store buddy window class type */ 645 infoPtr->BuddyType = BUDDY_TYPE_UNKNOWN; 646 if (GetClassNameW(bud, buddyClass, ARRAY_SIZE(buddyClass))) { 647 if (lstrcmpiW(buddyClass, WC_EDITW) == 0) 648 infoPtr->BuddyType = BUDDY_TYPE_EDIT; 649 else if (lstrcmpiW(buddyClass, WC_LISTBOXW) == 0) 650 infoPtr->BuddyType = BUDDY_TYPE_LISTBOX; 651 } 652 653 if (infoPtr->dwStyle & UDS_ARROWKEYS) 654 SetWindowSubclass(bud, UPDOWN_Buddy_SubclassProc, BUDDY_SUBCLASSID, 655 (DWORD_PTR)infoPtr->Self); 656 657 /* Get the rect of the buddy relative to its parent */ 658 GetWindowRect(infoPtr->Buddy, &budRect); 659 MapWindowPoints(HWND_DESKTOP, GetParent(infoPtr->Buddy), (POINT *)(&budRect.left), 2); 660 661 /* now do the positioning */ 662 if (infoPtr->dwStyle & UDS_ALIGNLEFT) { 663 x = budRect.left; 664 budRect.left += DEFAULT_WIDTH + DEFAULT_XSEP; 665 } else if (infoPtr->dwStyle & UDS_ALIGNRIGHT) { 666 budRect.right -= DEFAULT_WIDTH + DEFAULT_XSEP; 667 x = budRect.right+DEFAULT_XSEP; 668 } else { 669 /* nothing to do */ 670 return old_buddy; 671 } 672 673 /* first adjust the buddy to accommodate the up/down */ 674 SetWindowPos(infoPtr->Buddy, 0, budRect.left, budRect.top, 675 budRect.right - budRect.left, budRect.bottom - budRect.top, 676 SWP_NOACTIVATE|SWP_NOZORDER); 677 678 /* now position the up/down */ 679 /* Since the UDS_ALIGN* flags were used, */ 680 /* we will pick the position and size of the window. */ 681 width = DEFAULT_WIDTH; 682 683 /* 684 * If the updown has a buddy border, it has to overlap with the buddy 685 * to look as if it is integrated with the buddy control. 686 * We nudge the control or change its size to overlap. 687 */ 688 if (UPDOWN_HasBuddyBorder(infoPtr)) { 689 if(infoPtr->dwStyle & UDS_ALIGNLEFT) 690 width += DEFAULT_BUDDYBORDER; 691 else 692 x -= DEFAULT_BUDDYBORDER; 693 } 694 695 SetWindowPos(infoPtr->Self, 0, x, 696 budRect.top - DEFAULT_ADDTOP, width, 697 budRect.bottom - budRect.top + DEFAULT_ADDTOP + DEFAULT_ADDBOT, 698 SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOZORDER); 699 } else if (!(infoPtr->dwStyle & UDS_HORZ) && old_buddy != NULL) { 700 RECT rect; 701 GetWindowRect(infoPtr->Self, &rect); 702 MapWindowPoints(HWND_DESKTOP, GetParent(infoPtr->Self), (POINT *)&rect, 2); 703 SetWindowPos(infoPtr->Self, 0, rect.left, rect.top, DEFAULT_WIDTH, rect.bottom - rect.top, 704 SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOZORDER); 705 } 706 707 return old_buddy; 708 } 709 710 /*********************************************************************** 711 * UPDOWN_DoAction 712 * 713 * This function increments/decrements the CurVal by the 714 * 'delta' amount according to the 'action' flag which can be a 715 * combination of FLAG_INCR and FLAG_DECR 716 * It notifies the parent as required. 717 * It handles wrapping and non-wrapping correctly. 718 * It is assumed that delta>0 719 */ 720 static void UPDOWN_DoAction (UPDOWN_INFO *infoPtr, int delta, int action) 721 { 722 NM_UPDOWN ni; 723 724 TRACE("%d by %d\n", action, delta); 725 726 /* check if we can do the modification first */ 727 delta *= (action & FLAG_INCR ? 1 : -1) * (infoPtr->MaxVal < infoPtr->MinVal ? -1 : 1); 728 if ( (action & FLAG_INCR) && (action & FLAG_DECR) ) delta = 0; 729 730 TRACE("current %d, delta: %d\n", infoPtr->CurVal, delta); 731 732 /* We must notify parent now to obtain permission */ 733 ni.iPos = infoPtr->CurVal; 734 ni.iDelta = delta; 735 ni.hdr.hwndFrom = infoPtr->Self; 736 ni.hdr.idFrom = GetWindowLongPtrW (infoPtr->Self, GWLP_ID); 737 ni.hdr.code = UDN_DELTAPOS; 738 if (!SendMessageW(infoPtr->Notify, WM_NOTIFY, ni.hdr.idFrom, (LPARAM)&ni)) { 739 /* Parent said: OK to adjust */ 740 741 /* Now adjust value with (maybe new) delta */ 742 if (UPDOWN_OffsetVal (infoPtr, ni.iDelta)) { 743 TRACE("new %d, delta: %d\n", infoPtr->CurVal, ni.iDelta); 744 745 /* Now take care about our buddy */ 746 UPDOWN_SetBuddyInt (infoPtr); 747 } 748 } 749 750 /* Also, notify it. This message is sent in any case. */ 751 SendMessageW( infoPtr->Notify, (infoPtr->dwStyle & UDS_HORZ) ? WM_HSCROLL : WM_VSCROLL, 752 MAKELONG(SB_THUMBPOSITION, infoPtr->CurVal), (LPARAM)infoPtr->Self); 753 } 754 755 /*********************************************************************** 756 * UPDOWN_IsEnabled 757 * 758 * Returns TRUE if it is enabled as well as its buddy (if any) 759 * FALSE otherwise 760 */ 761 static BOOL UPDOWN_IsEnabled (const UPDOWN_INFO *infoPtr) 762 { 763 if (!IsWindowEnabled(infoPtr->Self)) 764 return FALSE; 765 if(infoPtr->Buddy) 766 return IsWindowEnabled(infoPtr->Buddy); 767 return TRUE; 768 } 769 770 /*********************************************************************** 771 * UPDOWN_CancelMode 772 * 773 * Deletes any timers, releases the mouse and does redraw if necessary. 774 * If the control is not in "capture" mode, it does nothing. 775 * If the control was not in cancel mode, it returns FALSE. 776 * If the control was in cancel mode, it returns TRUE. 777 */ 778 static BOOL UPDOWN_CancelMode (UPDOWN_INFO *infoPtr) 779 { 780 if (!(infoPtr->Flags & FLAG_PRESSED)) return FALSE; 781 782 KillTimer (infoPtr->Self, TIMER_AUTOREPEAT); 783 KillTimer (infoPtr->Self, TIMER_ACCEL); 784 KillTimer (infoPtr->Self, TIMER_AUTOPRESS); 785 786 if (GetCapture() == infoPtr->Self) 787 ReleaseCapture(); 788 789 infoPtr->Flags &= ~FLAG_PRESSED; 790 InvalidateRect (infoPtr->Self, NULL, FALSE); 791 792 return TRUE; 793 } 794 795 /*********************************************************************** 796 * UPDOWN_HandleMouseEvent 797 * 798 * Handle a mouse event for the updown. 799 * 'pt' is the location of the mouse event in client or 800 * windows coordinates. 801 */ 802 static void UPDOWN_HandleMouseEvent (UPDOWN_INFO *infoPtr, UINT msg, INT x, INT y) 803 { 804 POINT pt = { x, y }; 805 RECT rect; 806 int temp, arrow; 807 TRACKMOUSEEVENT tme; 808 809 TRACE("msg %04x point %s\n", msg, wine_dbgstr_point(&pt)); 810 811 switch(msg) 812 { 813 case WM_LBUTTONDOWN: /* Initialise mouse tracking */ 814 815 /* If the buddy is an edit, will set focus to it */ 816 if (UPDOWN_IsBuddyEdit(infoPtr)) SetFocus(infoPtr->Buddy); 817 818 /* Now see which one is the 'active' arrow */ 819 arrow = UPDOWN_GetArrowFromPoint (infoPtr, &rect, pt); 820 821 /* Update the flags if we are in/out */ 822 infoPtr->Flags &= ~(FLAG_MOUSEIN | FLAG_ARROW); 823 if (arrow) 824 infoPtr->Flags |= FLAG_MOUSEIN | arrow; 825 else 826 if (infoPtr->AccelIndex != -1) infoPtr->AccelIndex = 0; 827 828 if (infoPtr->Flags & FLAG_ARROW) { 829 830 /* Update the CurVal if necessary */ 831 UPDOWN_GetBuddyInt (infoPtr); 832 833 /* Set up the correct flags */ 834 infoPtr->Flags |= FLAG_PRESSED; 835 836 /* repaint the control */ 837 InvalidateRect (infoPtr->Self, NULL, FALSE); 838 839 /* process the click */ 840 temp = (infoPtr->AccelCount && infoPtr->AccelVect) ? infoPtr->AccelVect[0].nInc : 1; 841 UPDOWN_DoAction (infoPtr, temp, infoPtr->Flags & FLAG_ARROW); 842 843 /* now capture all mouse messages */ 844 SetCapture (infoPtr->Self); 845 846 /* and startup the first timer */ 847 SetTimer(infoPtr->Self, TIMER_AUTOREPEAT, INITIAL_DELAY, 0); 848 } 849 break; 850 851 case WM_MOUSEMOVE: 852 /* save the flags to see if any got modified */ 853 temp = infoPtr->Flags; 854 855 /* Now see which one is the 'active' arrow */ 856 arrow = UPDOWN_GetArrowFromPoint (infoPtr, &rect, pt); 857 858 /* Update the flags if we are in/out */ 859 infoPtr->Flags &= ~(FLAG_MOUSEIN | FLAG_ARROW); 860 if(arrow) { 861 infoPtr->Flags |= FLAG_MOUSEIN | arrow; 862 } else { 863 if(infoPtr->AccelIndex != -1) infoPtr->AccelIndex = 0; 864 } 865 866 /* If state changed, redraw the control */ 867 if(temp != infoPtr->Flags) 868 InvalidateRect (infoPtr->Self, NULL, FALSE); 869 870 /* Set up tracking so the mousein flags can be reset when the 871 * mouse leaves the control */ 872 tme.cbSize = sizeof( tme ); 873 tme.dwFlags = TME_LEAVE; 874 tme.hwndTrack = infoPtr->Self; 875 TrackMouseEvent (&tme); 876 877 break; 878 case WM_MOUSELEAVE: 879 infoPtr->Flags &= ~(FLAG_MOUSEIN | FLAG_ARROW); 880 InvalidateRect (infoPtr->Self, NULL, FALSE); 881 break; 882 883 default: 884 ERR("Impossible case (msg=%x)!\n", msg); 885 } 886 887 } 888 889 /*********************************************************************** 890 * UpDownWndProc 891 */ 892 static LRESULT WINAPI UpDownWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 893 { 894 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd); 895 static const WCHAR themeClass[] = {'S','p','i','n',0}; 896 HTHEME theme; 897 898 TRACE("hwnd=%p msg=%04x wparam=%08lx lparam=%08lx\n", hwnd, message, wParam, lParam); 899 900 if (!infoPtr && (message != WM_CREATE)) 901 return DefWindowProcW (hwnd, message, wParam, lParam); 902 903 switch(message) 904 { 905 case WM_CREATE: 906 { 907 CREATESTRUCTW *pcs = (CREATESTRUCTW*)lParam; 908 909 infoPtr = heap_alloc_zero(sizeof(*infoPtr)); 910 SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr); 911 912 /* initialize the info struct */ 913 infoPtr->Self = hwnd; 914 infoPtr->Notify = pcs->hwndParent; 915 infoPtr->dwStyle = pcs->style; 916 infoPtr->AccelCount = 0; 917 infoPtr->AccelVect = 0; 918 infoPtr->AccelIndex = -1; 919 infoPtr->CurVal = 0; 920 infoPtr->MinVal = 100; 921 infoPtr->MaxVal = 0; 922 infoPtr->Base = 10; /* Default to base 10 */ 923 infoPtr->Buddy = 0; /* No buddy window yet */ 924 infoPtr->Flags = (infoPtr->dwStyle & UDS_SETBUDDYINT) ? FLAG_BUDDYINT : 0; 925 926 SetWindowLongW (hwnd, GWL_STYLE, infoPtr->dwStyle & ~WS_BORDER); 927 if (!(infoPtr->dwStyle & UDS_HORZ)) 928 SetWindowPos (hwnd, NULL, 0, 0, DEFAULT_WIDTH, pcs->cy, 929 SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE); 930 931 /* Do we pick the buddy win ourselves? */ 932 if (infoPtr->dwStyle & UDS_AUTOBUDDY) 933 UPDOWN_SetBuddy (infoPtr, GetWindow (hwnd, GW_HWNDPREV)); 934 935 OpenThemeData (hwnd, themeClass); 936 937 TRACE("UpDown Ctrl creation, hwnd=%p\n", hwnd); 938 } 939 break; 940 941 case WM_DESTROY: 942 heap_free (infoPtr->AccelVect); 943 UPDOWN_ResetSubclass (infoPtr); 944 heap_free (infoPtr); 945 SetWindowLongPtrW (hwnd, 0, 0); 946 theme = GetWindowTheme (hwnd); 947 CloseThemeData (theme); 948 TRACE("UpDown Ctrl destruction, hwnd=%p\n", hwnd); 949 break; 950 951 case WM_ENABLE: 952 if (wParam) { 953 infoPtr->dwStyle &= ~WS_DISABLED; 954 } else { 955 infoPtr->dwStyle |= WS_DISABLED; 956 UPDOWN_CancelMode (infoPtr); 957 } 958 InvalidateRect (infoPtr->Self, NULL, FALSE); 959 break; 960 961 case WM_STYLECHANGED: 962 if (wParam == GWL_STYLE) { 963 infoPtr->dwStyle = ((LPSTYLESTRUCT)lParam)->styleNew; 964 InvalidateRect (infoPtr->Self, NULL, FALSE); 965 } 966 break; 967 968 case WM_THEMECHANGED: 969 theme = GetWindowTheme (hwnd); 970 CloseThemeData (theme); 971 OpenThemeData (hwnd, themeClass); 972 InvalidateRect (hwnd, NULL, FALSE); 973 break; 974 975 case WM_TIMER: 976 /* is this the auto-press timer? */ 977 if(wParam == TIMER_AUTOPRESS) { 978 KillTimer(hwnd, TIMER_AUTOPRESS); 979 infoPtr->Flags &= ~(FLAG_PRESSED | FLAG_ARROW); 980 InvalidateRect(infoPtr->Self, NULL, FALSE); 981 } 982 983 /* if initial timer, kill it and start the repeat timer */ 984 if(wParam == TIMER_AUTOREPEAT) { 985 INT delay; 986 987 KillTimer(hwnd, TIMER_AUTOREPEAT); 988 /* if no accel info given, used default timer */ 989 if(infoPtr->AccelCount==0 || infoPtr->AccelVect==0) { 990 infoPtr->AccelIndex = -1; 991 delay = REPEAT_DELAY; 992 } else { 993 infoPtr->AccelIndex = 0; /* otherwise, use it */ 994 delay = infoPtr->AccelVect[infoPtr->AccelIndex].nSec * 1000 + 1; 995 } 996 SetTimer(hwnd, TIMER_ACCEL, delay, 0); 997 } 998 999 /* now, if the mouse is above us, do the thing...*/ 1000 if(infoPtr->Flags & FLAG_MOUSEIN) { 1001 int temp; 1002 1003 temp = infoPtr->AccelIndex == -1 ? 1 : infoPtr->AccelVect[infoPtr->AccelIndex].nInc; 1004 UPDOWN_DoAction(infoPtr, temp, infoPtr->Flags & FLAG_ARROW); 1005 1006 if(infoPtr->AccelIndex != -1 && infoPtr->AccelIndex < infoPtr->AccelCount-1) { 1007 KillTimer(hwnd, TIMER_ACCEL); 1008 infoPtr->AccelIndex++; /* move to the next accel info */ 1009 temp = infoPtr->AccelVect[infoPtr->AccelIndex].nSec * 1000 + 1; 1010 /* make sure we have at least 1ms intervals */ 1011 SetTimer(hwnd, TIMER_ACCEL, temp, 0); 1012 } 1013 } 1014 break; 1015 1016 case WM_CANCELMODE: 1017 return UPDOWN_CancelMode (infoPtr); 1018 1019 case WM_LBUTTONUP: 1020 if (GetCapture() != infoPtr->Self) break; 1021 1022 if ( (infoPtr->Flags & FLAG_MOUSEIN) && 1023 (infoPtr->Flags & FLAG_ARROW) ) { 1024 1025 SendMessageW( infoPtr->Notify, 1026 (infoPtr->dwStyle & UDS_HORZ) ? WM_HSCROLL : WM_VSCROLL, 1027 MAKELONG(SB_ENDSCROLL, infoPtr->CurVal), 1028 (LPARAM)hwnd); 1029 if (UPDOWN_IsBuddyEdit(infoPtr)) 1030 SendMessageW(infoPtr->Buddy, EM_SETSEL, 0, MAKELONG(0, -1)); 1031 } 1032 UPDOWN_CancelMode(infoPtr); 1033 break; 1034 1035 case WM_LBUTTONDOWN: 1036 case WM_MOUSEMOVE: 1037 case WM_MOUSELEAVE: 1038 if(UPDOWN_IsEnabled(infoPtr)) 1039 UPDOWN_HandleMouseEvent (infoPtr, message, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 1040 break; 1041 1042 case WM_MOUSEWHEEL: 1043 UPDOWN_MouseWheel(infoPtr, wParam); 1044 break; 1045 1046 case WM_KEYDOWN: 1047 if((infoPtr->dwStyle & UDS_ARROWKEYS) && UPDOWN_IsEnabled(infoPtr)) 1048 return UPDOWN_KeyPressed(infoPtr, (int)wParam); 1049 break; 1050 1051 case WM_PRINTCLIENT: 1052 case WM_PAINT: 1053 return UPDOWN_Paint (infoPtr, (HDC)wParam); 1054 1055 case UDM_GETACCEL: 1056 if (wParam==0 && lParam==0) return infoPtr->AccelCount; 1057 if (wParam && lParam) { 1058 int temp = min(infoPtr->AccelCount, wParam); 1059 memcpy((void *)lParam, infoPtr->AccelVect, temp*sizeof(UDACCEL)); 1060 return temp; 1061 } 1062 return 0; 1063 1064 case UDM_SETACCEL: 1065 { 1066 TRACE("UDM_SETACCEL\n"); 1067 1068 if(infoPtr->AccelVect) { 1069 heap_free (infoPtr->AccelVect); 1070 infoPtr->AccelCount = 0; 1071 infoPtr->AccelVect = 0; 1072 } 1073 if(wParam==0) return TRUE; 1074 infoPtr->AccelVect = heap_alloc(wParam*sizeof(UDACCEL)); 1075 if(!infoPtr->AccelVect) return FALSE; 1076 memcpy(infoPtr->AccelVect, (void*)lParam, wParam*sizeof(UDACCEL)); 1077 infoPtr->AccelCount = wParam; 1078 1079 if (TRACE_ON(updown)) 1080 { 1081 UINT i; 1082 1083 for (i = 0; i < wParam; i++) 1084 TRACE("%u: nSec %u nInc %u\n", i, 1085 infoPtr->AccelVect[i].nSec, infoPtr->AccelVect[i].nInc); 1086 } 1087 1088 return TRUE; 1089 } 1090 case UDM_GETBASE: 1091 return infoPtr->Base; 1092 1093 case UDM_SETBASE: 1094 TRACE("UpDown Ctrl new base(%ld), hwnd=%p\n", wParam, hwnd); 1095 if (wParam==10 || wParam==16) { 1096 WPARAM old_base = infoPtr->Base; 1097 infoPtr->Base = wParam; 1098 1099 if (old_base != infoPtr->Base) 1100 UPDOWN_SetBuddyInt(infoPtr); 1101 1102 return old_base; 1103 } 1104 break; 1105 1106 case UDM_GETBUDDY: 1107 return (LRESULT)infoPtr->Buddy; 1108 1109 case UDM_SETBUDDY: 1110 return (LRESULT)UPDOWN_SetBuddy (infoPtr, (HWND)wParam); 1111 1112 case UDM_GETPOS: 1113 { 1114 BOOL err; 1115 int pos; 1116 1117 pos = UPDOWN_GetPos(infoPtr, &err); 1118 return MAKELONG(pos, err); 1119 } 1120 case UDM_SETPOS: 1121 { 1122 return UPDOWN_SetPos(infoPtr, (short)LOWORD(lParam)); 1123 } 1124 case UDM_GETRANGE: 1125 return MAKELONG(infoPtr->MaxVal, infoPtr->MinVal); 1126 1127 case UDM_SETRANGE: 1128 /* we must have: 1129 UD_MINVAL <= Max <= UD_MAXVAL 1130 UD_MINVAL <= Min <= UD_MAXVAL 1131 |Max-Min| <= UD_MAXVAL */ 1132 UPDOWN_SetRange(infoPtr, (short)lParam, (short)HIWORD(lParam)); 1133 break; 1134 1135 case UDM_GETRANGE32: 1136 if (wParam) *(LPINT)wParam = infoPtr->MinVal; 1137 if (lParam) *(LPINT)lParam = infoPtr->MaxVal; 1138 break; 1139 1140 case UDM_SETRANGE32: 1141 UPDOWN_SetRange(infoPtr, (INT)lParam, (INT)wParam); 1142 break; 1143 1144 case UDM_GETPOS32: 1145 { 1146 return UPDOWN_GetPos(infoPtr, (BOOL*)lParam); 1147 } 1148 case UDM_SETPOS32: 1149 { 1150 return UPDOWN_SetPos(infoPtr, (int)lParam); 1151 } 1152 case UDM_GETUNICODEFORMAT: 1153 /* we lie a bit here, we're always using Unicode internally */ 1154 return infoPtr->UnicodeFormat; 1155 1156 case UDM_SETUNICODEFORMAT: 1157 { 1158 /* do we really need to honour this flag? */ 1159 int temp = infoPtr->UnicodeFormat; 1160 infoPtr->UnicodeFormat = (BOOL)wParam; 1161 return temp; 1162 } 1163 default: 1164 if ((message >= WM_USER) && (message < WM_APP) && !COMCTL32_IsReflectedMessage(message)) 1165 ERR("unknown msg %04x wp=%04lx lp=%08lx\n", message, wParam, lParam); 1166 return DefWindowProcW (hwnd, message, wParam, lParam); 1167 } 1168 1169 return 0; 1170 } 1171 1172 /*********************************************************************** 1173 * UPDOWN_Register [Internal] 1174 * 1175 * Registers the updown window class. 1176 */ 1177 void UPDOWN_Register(void) 1178 { 1179 WNDCLASSW wndClass; 1180 1181 ZeroMemory( &wndClass, sizeof( WNDCLASSW ) ); 1182 wndClass.style = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW; 1183 wndClass.lpfnWndProc = UpDownWindowProc; 1184 wndClass.cbClsExtra = 0; 1185 wndClass.cbWndExtra = sizeof(UPDOWN_INFO*); 1186 wndClass.hCursor = LoadCursorW( 0, (LPWSTR)IDC_ARROW ); 1187 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); 1188 wndClass.lpszClassName = UPDOWN_CLASSW; 1189 1190 RegisterClassW( &wndClass ); 1191 } 1192 1193 1194 /*********************************************************************** 1195 * UPDOWN_Unregister [Internal] 1196 * 1197 * Unregisters the updown window class. 1198 */ 1199 void UPDOWN_Unregister (void) 1200 { 1201 UnregisterClassW (UPDOWN_CLASSW, NULL); 1202 } 1203