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 NMHDR hdr; 786 hdr.hwndFrom = infoPtr->Self; 787 hdr.idFrom = GetWindowLongPtrW (infoPtr->Self, GWLP_ID); 788 hdr.code = NM_RELEASEDCAPTURE; 789 SendMessageW(infoPtr->Notify, WM_NOTIFY, hdr.idFrom, (LPARAM)&hdr); 790 ReleaseCapture(); 791 } 792 793 infoPtr->Flags &= ~FLAG_PRESSED; 794 InvalidateRect (infoPtr->Self, NULL, FALSE); 795 796 return TRUE; 797 } 798 799 /*********************************************************************** 800 * UPDOWN_HandleMouseEvent 801 * 802 * Handle a mouse event for the updown. 803 * 'pt' is the location of the mouse event in client or 804 * windows coordinates. 805 */ 806 static void UPDOWN_HandleMouseEvent (UPDOWN_INFO *infoPtr, UINT msg, INT x, INT y) 807 { 808 POINT pt = { x, y }; 809 RECT rect; 810 int temp, arrow; 811 TRACKMOUSEEVENT tme; 812 813 TRACE("msg %04x point %s\n", msg, wine_dbgstr_point(&pt)); 814 815 switch(msg) 816 { 817 case WM_LBUTTONDOWN: /* Initialise mouse tracking */ 818 819 /* If the buddy is an edit, will set focus to it */ 820 if (UPDOWN_IsBuddyEdit(infoPtr)) SetFocus(infoPtr->Buddy); 821 822 /* Now see which one is the 'active' arrow */ 823 arrow = UPDOWN_GetArrowFromPoint (infoPtr, &rect, pt); 824 825 /* Update the flags if we are in/out */ 826 infoPtr->Flags &= ~(FLAG_MOUSEIN | FLAG_ARROW); 827 if (arrow) 828 infoPtr->Flags |= FLAG_MOUSEIN | arrow; 829 else 830 if (infoPtr->AccelIndex != -1) infoPtr->AccelIndex = 0; 831 832 if (infoPtr->Flags & FLAG_ARROW) { 833 834 /* Update the CurVal if necessary */ 835 UPDOWN_GetBuddyInt (infoPtr); 836 837 /* Set up the correct flags */ 838 infoPtr->Flags |= FLAG_PRESSED; 839 840 /* repaint the control */ 841 InvalidateRect (infoPtr->Self, NULL, FALSE); 842 843 /* process the click */ 844 temp = (infoPtr->AccelCount && infoPtr->AccelVect) ? infoPtr->AccelVect[0].nInc : 1; 845 UPDOWN_DoAction (infoPtr, temp, infoPtr->Flags & FLAG_ARROW); 846 847 /* now capture all mouse messages */ 848 SetCapture (infoPtr->Self); 849 850 /* and startup the first timer */ 851 SetTimer(infoPtr->Self, TIMER_AUTOREPEAT, INITIAL_DELAY, 0); 852 } 853 break; 854 855 case WM_MOUSEMOVE: 856 /* save the flags to see if any got modified */ 857 temp = infoPtr->Flags; 858 859 /* Now see which one is the 'active' arrow */ 860 arrow = UPDOWN_GetArrowFromPoint (infoPtr, &rect, pt); 861 862 /* Update the flags if we are in/out */ 863 infoPtr->Flags &= ~(FLAG_MOUSEIN | FLAG_ARROW); 864 if(arrow) { 865 infoPtr->Flags |= FLAG_MOUSEIN | arrow; 866 } else { 867 if(infoPtr->AccelIndex != -1) infoPtr->AccelIndex = 0; 868 } 869 870 /* If state changed, redraw the control */ 871 if(temp != infoPtr->Flags) 872 InvalidateRect (infoPtr->Self, NULL, FALSE); 873 874 /* Set up tracking so the mousein flags can be reset when the 875 * mouse leaves the control */ 876 tme.cbSize = sizeof( tme ); 877 tme.dwFlags = TME_LEAVE; 878 tme.hwndTrack = infoPtr->Self; 879 TrackMouseEvent (&tme); 880 881 break; 882 case WM_MOUSELEAVE: 883 infoPtr->Flags &= ~(FLAG_MOUSEIN | FLAG_ARROW); 884 InvalidateRect (infoPtr->Self, NULL, FALSE); 885 break; 886 887 default: 888 ERR("Impossible case (msg=%x)!\n", msg); 889 } 890 891 } 892 893 /*********************************************************************** 894 * UpDownWndProc 895 */ 896 static LRESULT WINAPI UpDownWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 897 { 898 UPDOWN_INFO *infoPtr = UPDOWN_GetInfoPtr (hwnd); 899 static const WCHAR themeClass[] = {'S','p','i','n',0}; 900 HTHEME theme; 901 902 TRACE("hwnd=%p msg=%04x wparam=%08lx lparam=%08lx\n", hwnd, message, wParam, lParam); 903 904 if (!infoPtr && (message != WM_CREATE)) 905 return DefWindowProcW (hwnd, message, wParam, lParam); 906 907 switch(message) 908 { 909 case WM_CREATE: 910 { 911 CREATESTRUCTW *pcs = (CREATESTRUCTW*)lParam; 912 913 infoPtr = heap_alloc_zero(sizeof(*infoPtr)); 914 SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr); 915 916 /* initialize the info struct */ 917 infoPtr->Self = hwnd; 918 infoPtr->Notify = pcs->hwndParent; 919 infoPtr->dwStyle = pcs->style; 920 infoPtr->AccelCount = 0; 921 infoPtr->AccelVect = 0; 922 infoPtr->AccelIndex = -1; 923 infoPtr->CurVal = 0; 924 infoPtr->MinVal = 100; 925 infoPtr->MaxVal = 0; 926 infoPtr->Base = 10; /* Default to base 10 */ 927 infoPtr->Buddy = 0; /* No buddy window yet */ 928 infoPtr->Flags = (infoPtr->dwStyle & UDS_SETBUDDYINT) ? FLAG_BUDDYINT : 0; 929 930 SetWindowLongW (hwnd, GWL_STYLE, infoPtr->dwStyle & ~WS_BORDER); 931 if (!(infoPtr->dwStyle & UDS_HORZ)) 932 SetWindowPos (hwnd, NULL, 0, 0, DEFAULT_WIDTH, pcs->cy, 933 SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE); 934 935 /* Do we pick the buddy win ourselves? */ 936 if (infoPtr->dwStyle & UDS_AUTOBUDDY) 937 UPDOWN_SetBuddy (infoPtr, GetWindow (hwnd, GW_HWNDPREV)); 938 939 OpenThemeData (hwnd, themeClass); 940 941 TRACE("UpDown Ctrl creation, hwnd=%p\n", hwnd); 942 } 943 break; 944 945 case WM_DESTROY: 946 heap_free (infoPtr->AccelVect); 947 UPDOWN_ResetSubclass (infoPtr); 948 heap_free (infoPtr); 949 SetWindowLongPtrW (hwnd, 0, 0); 950 theme = GetWindowTheme (hwnd); 951 CloseThemeData (theme); 952 TRACE("UpDown Ctrl destruction, hwnd=%p\n", hwnd); 953 break; 954 955 case WM_ENABLE: 956 if (wParam) { 957 infoPtr->dwStyle &= ~WS_DISABLED; 958 } else { 959 infoPtr->dwStyle |= WS_DISABLED; 960 UPDOWN_CancelMode (infoPtr); 961 } 962 InvalidateRect (infoPtr->Self, NULL, FALSE); 963 break; 964 965 case WM_STYLECHANGED: 966 if (wParam == GWL_STYLE) { 967 infoPtr->dwStyle = ((LPSTYLESTRUCT)lParam)->styleNew; 968 InvalidateRect (infoPtr->Self, NULL, FALSE); 969 } 970 break; 971 972 case WM_THEMECHANGED: 973 theme = GetWindowTheme (hwnd); 974 CloseThemeData (theme); 975 OpenThemeData (hwnd, themeClass); 976 InvalidateRect (hwnd, NULL, FALSE); 977 break; 978 979 case WM_TIMER: 980 /* is this the auto-press timer? */ 981 if(wParam == TIMER_AUTOPRESS) { 982 KillTimer(hwnd, TIMER_AUTOPRESS); 983 infoPtr->Flags &= ~(FLAG_PRESSED | FLAG_ARROW); 984 InvalidateRect(infoPtr->Self, NULL, FALSE); 985 } 986 987 /* if initial timer, kill it and start the repeat timer */ 988 if(wParam == TIMER_AUTOREPEAT) { 989 INT delay; 990 991 KillTimer(hwnd, TIMER_AUTOREPEAT); 992 /* if no accel info given, used default timer */ 993 if(infoPtr->AccelCount==0 || infoPtr->AccelVect==0) { 994 infoPtr->AccelIndex = -1; 995 delay = REPEAT_DELAY; 996 } else { 997 infoPtr->AccelIndex = 0; /* otherwise, use it */ 998 delay = infoPtr->AccelVect[infoPtr->AccelIndex].nSec * 1000 + 1; 999 } 1000 SetTimer(hwnd, TIMER_ACCEL, delay, 0); 1001 } 1002 1003 /* now, if the mouse is above us, do the thing...*/ 1004 if(infoPtr->Flags & FLAG_MOUSEIN) { 1005 int temp; 1006 1007 temp = infoPtr->AccelIndex == -1 ? 1 : infoPtr->AccelVect[infoPtr->AccelIndex].nInc; 1008 UPDOWN_DoAction(infoPtr, temp, infoPtr->Flags & FLAG_ARROW); 1009 1010 if(infoPtr->AccelIndex != -1 && infoPtr->AccelIndex < infoPtr->AccelCount-1) { 1011 KillTimer(hwnd, TIMER_ACCEL); 1012 infoPtr->AccelIndex++; /* move to the next accel info */ 1013 temp = infoPtr->AccelVect[infoPtr->AccelIndex].nSec * 1000 + 1; 1014 /* make sure we have at least 1ms intervals */ 1015 SetTimer(hwnd, TIMER_ACCEL, temp, 0); 1016 } 1017 } 1018 break; 1019 1020 case WM_CANCELMODE: 1021 return UPDOWN_CancelMode (infoPtr); 1022 1023 case WM_LBUTTONUP: 1024 if (GetCapture() != infoPtr->Self) break; 1025 1026 if ( (infoPtr->Flags & FLAG_MOUSEIN) && 1027 (infoPtr->Flags & FLAG_ARROW) ) { 1028 1029 SendMessageW( infoPtr->Notify, 1030 (infoPtr->dwStyle & UDS_HORZ) ? WM_HSCROLL : WM_VSCROLL, 1031 MAKELONG(SB_ENDSCROLL, infoPtr->CurVal), 1032 (LPARAM)hwnd); 1033 if (UPDOWN_IsBuddyEdit(infoPtr)) 1034 SendMessageW(infoPtr->Buddy, EM_SETSEL, 0, MAKELONG(0, -1)); 1035 } 1036 UPDOWN_CancelMode(infoPtr); 1037 break; 1038 1039 case WM_LBUTTONDOWN: 1040 case WM_MOUSEMOVE: 1041 case WM_MOUSELEAVE: 1042 if(UPDOWN_IsEnabled(infoPtr)) 1043 UPDOWN_HandleMouseEvent (infoPtr, message, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 1044 break; 1045 1046 case WM_MOUSEWHEEL: 1047 UPDOWN_MouseWheel(infoPtr, wParam); 1048 break; 1049 1050 case WM_KEYDOWN: 1051 if((infoPtr->dwStyle & UDS_ARROWKEYS) && UPDOWN_IsEnabled(infoPtr)) 1052 return UPDOWN_KeyPressed(infoPtr, (int)wParam); 1053 break; 1054 1055 case WM_PRINTCLIENT: 1056 case WM_PAINT: 1057 return UPDOWN_Paint (infoPtr, (HDC)wParam); 1058 1059 case UDM_GETACCEL: 1060 if (wParam==0 && lParam==0) return infoPtr->AccelCount; 1061 if (wParam && lParam) { 1062 int temp = min(infoPtr->AccelCount, wParam); 1063 memcpy((void *)lParam, infoPtr->AccelVect, temp*sizeof(UDACCEL)); 1064 return temp; 1065 } 1066 return 0; 1067 1068 case UDM_SETACCEL: 1069 { 1070 TRACE("UDM_SETACCEL\n"); 1071 1072 if(infoPtr->AccelVect) { 1073 heap_free (infoPtr->AccelVect); 1074 infoPtr->AccelCount = 0; 1075 infoPtr->AccelVect = 0; 1076 } 1077 if(wParam==0) return TRUE; 1078 infoPtr->AccelVect = heap_alloc(wParam*sizeof(UDACCEL)); 1079 if(!infoPtr->AccelVect) return FALSE; 1080 memcpy(infoPtr->AccelVect, (void*)lParam, wParam*sizeof(UDACCEL)); 1081 infoPtr->AccelCount = wParam; 1082 1083 if (TRACE_ON(updown)) 1084 { 1085 UINT i; 1086 1087 for (i = 0; i < wParam; i++) 1088 TRACE("%u: nSec %u nInc %u\n", i, 1089 infoPtr->AccelVect[i].nSec, infoPtr->AccelVect[i].nInc); 1090 } 1091 1092 return TRUE; 1093 } 1094 case UDM_GETBASE: 1095 return infoPtr->Base; 1096 1097 case UDM_SETBASE: 1098 TRACE("UpDown Ctrl new base(%ld), hwnd=%p\n", wParam, hwnd); 1099 if (wParam==10 || wParam==16) { 1100 WPARAM old_base = infoPtr->Base; 1101 infoPtr->Base = wParam; 1102 1103 if (old_base != infoPtr->Base) 1104 UPDOWN_SetBuddyInt(infoPtr); 1105 1106 return old_base; 1107 } 1108 break; 1109 1110 case UDM_GETBUDDY: 1111 return (LRESULT)infoPtr->Buddy; 1112 1113 case UDM_SETBUDDY: 1114 return (LRESULT)UPDOWN_SetBuddy (infoPtr, (HWND)wParam); 1115 1116 case UDM_GETPOS: 1117 { 1118 BOOL err; 1119 int pos; 1120 1121 pos = UPDOWN_GetPos(infoPtr, &err); 1122 return MAKELONG(pos, err); 1123 } 1124 case UDM_SETPOS: 1125 { 1126 return UPDOWN_SetPos(infoPtr, (short)LOWORD(lParam)); 1127 } 1128 case UDM_GETRANGE: 1129 return MAKELONG(infoPtr->MaxVal, infoPtr->MinVal); 1130 1131 case UDM_SETRANGE: 1132 /* we must have: 1133 UD_MINVAL <= Max <= UD_MAXVAL 1134 UD_MINVAL <= Min <= UD_MAXVAL 1135 |Max-Min| <= UD_MAXVAL */ 1136 UPDOWN_SetRange(infoPtr, (short)lParam, (short)HIWORD(lParam)); 1137 break; 1138 1139 case UDM_GETRANGE32: 1140 if (wParam) *(LPINT)wParam = infoPtr->MinVal; 1141 if (lParam) *(LPINT)lParam = infoPtr->MaxVal; 1142 break; 1143 1144 case UDM_SETRANGE32: 1145 UPDOWN_SetRange(infoPtr, (INT)lParam, (INT)wParam); 1146 break; 1147 1148 case UDM_GETPOS32: 1149 { 1150 return UPDOWN_GetPos(infoPtr, (BOOL*)lParam); 1151 } 1152 case UDM_SETPOS32: 1153 { 1154 return UPDOWN_SetPos(infoPtr, (int)lParam); 1155 } 1156 case UDM_GETUNICODEFORMAT: 1157 /* we lie a bit here, we're always using Unicode internally */ 1158 return infoPtr->UnicodeFormat; 1159 1160 case UDM_SETUNICODEFORMAT: 1161 { 1162 /* do we really need to honour this flag? */ 1163 int temp = infoPtr->UnicodeFormat; 1164 infoPtr->UnicodeFormat = (BOOL)wParam; 1165 return temp; 1166 } 1167 default: 1168 if ((message >= WM_USER) && (message < WM_APP) && !COMCTL32_IsReflectedMessage(message)) 1169 ERR("unknown msg %04x wp=%04lx lp=%08lx\n", message, wParam, lParam); 1170 return DefWindowProcW (hwnd, message, wParam, lParam); 1171 } 1172 1173 return 0; 1174 } 1175 1176 /*********************************************************************** 1177 * UPDOWN_Register [Internal] 1178 * 1179 * Registers the updown window class. 1180 */ 1181 void UPDOWN_Register(void) 1182 { 1183 WNDCLASSW wndClass; 1184 1185 ZeroMemory( &wndClass, sizeof( WNDCLASSW ) ); 1186 wndClass.style = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW; 1187 wndClass.lpfnWndProc = UpDownWindowProc; 1188 wndClass.cbClsExtra = 0; 1189 wndClass.cbWndExtra = sizeof(UPDOWN_INFO*); 1190 wndClass.hCursor = LoadCursorW( 0, (LPWSTR)IDC_ARROW ); 1191 wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); 1192 wndClass.lpszClassName = UPDOWN_CLASSW; 1193 1194 RegisterClassW( &wndClass ); 1195 } 1196 1197 1198 /*********************************************************************** 1199 * UPDOWN_Unregister [Internal] 1200 * 1201 * Unregisters the updown window class. 1202 */ 1203 void UPDOWN_Unregister (void) 1204 { 1205 UnregisterClassW (UPDOWN_CLASSW, NULL); 1206 } 1207