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