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