1 /* 2 * ReactOS User32 Library 3 * - ScrollBar control 4 * 5 * Copyright 2001 Casper S. Hornstrup 6 * Copyright 2003 Thomas Weidenmueller 7 * Copyright 2003 Filip Navara 8 * 9 * Based on Wine code. 10 * 11 * Copyright 1993 Martin Ayotte 12 * Copyright 1994, 1996 Alexandre Julliard 13 * 14 * This library is free software; you can redistribute it and/or 15 * modify it under the terms of the GNU Lesser General Public 16 * License as published by the Free Software Foundation; either 17 * version 2.1 of the License, or (at your option) any later version. 18 * 19 * This library is distributed in the hope that it will be useful, 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 * Lesser General Public License for more details. 23 * 24 * You should have received a copy of the GNU Lesser General Public 25 * License along with this library; if not, write to the Free Software 26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 27 */ 28 29 /* INCLUDES *******************************************************************/ 30 31 #include <user32.h> 32 33 #include <wine/debug.h> 34 WINE_DEFAULT_DEBUG_CHANNEL(scrollbar); 35 36 /* GLOBAL VARIABLES ***********************************************************/ 37 38 /* Definitions for scrollbar hit testing [See SCROLLBARINFO in MSDN] */ 39 #define SCROLL_NOWHERE 0x00 /* Outside the scroll bar */ 40 #define SCROLL_TOP_ARROW 0x01 /* Top or left arrow */ 41 #define SCROLL_TOP_RECT 0x02 /* Rectangle between the top arrow and the thumb */ 42 #define SCROLL_THUMB 0x03 /* Thumb rectangle */ 43 #define SCROLL_BOTTOM_RECT 0x04 /* Rectangle between the thumb and the bottom arrow */ 44 #define SCROLL_BOTTOM_ARROW 0x05 /* Bottom or right arrow */ 45 46 #define SCROLL_FIRST_DELAY 200 /* Delay (in ms) before first repetition when 47 holding the button down */ 48 #define SCROLL_REPEAT_DELAY 50 /* Delay (in ms) between scroll repetitions */ 49 50 #define SCROLL_TIMER 0 /* Scroll timer id */ 51 52 /* Minimum size of the rectangle between the arrows */ 53 #define SCROLL_MIN_RECT 4 54 55 /* Minimum size of the thumb in pixels */ 56 #define SCROLL_MIN_THUMB 6 57 58 /* Overlap between arrows and thumb */ 59 #define SCROLL_ARROW_THUMB_OVERLAP 0 60 61 /* Thumb-tracking info */ 62 static HWND ScrollTrackingWin = 0; 63 static INT ScrollTrackingBar = 0; 64 static INT ScrollTrackingPos = 0; 65 static INT ScrollTrackingVal = 0; 66 /* Hit test code of the last button-down event */ 67 static DWORD ScrollTrackHitTest = SCROLL_NOWHERE; 68 static BOOL ScrollTrackVertical; 69 70 /* Is the moving thumb being displayed? */ 71 static BOOL ScrollMovingThumb = FALSE; 72 73 HBRUSH DefWndControlColor(HDC hDC, UINT ctlType); 74 75 UINT_PTR WINAPI SetSystemTimer(HWND,UINT_PTR,UINT,TIMERPROC); 76 BOOL WINAPI KillSystemTimer(HWND,UINT_PTR); 77 78 /********************************************************************* 79 * scrollbar class descriptor 80 */ 81 const struct builtin_class_descr SCROLL_builtin_class = 82 { 83 L"ScrollBar", /* name */ 84 CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC, /* style */ 85 ScrollBarWndProcA, /* procA */ 86 ScrollBarWndProcW, /* procW */ 87 sizeof(SBWND)-sizeof(WND), /* extra */ 88 IDC_ARROW, /* cursor */ 89 0 /* brush */ 90 }; 91 92 /* PRIVATE FUNCTIONS **********************************************************/ 93 94 static PSBDATA 95 IntGetSBData(PWND pwnd, INT Bar) 96 { 97 PSBWND pSBWnd; 98 PSBINFO pSBInfo; 99 100 pSBInfo = DesktopPtrToUser(pwnd->pSBInfo); 101 switch (Bar) 102 { 103 case SB_HORZ: 104 return &pSBInfo->Horz; 105 case SB_VERT: 106 return &pSBInfo->Vert; 107 case SB_CTL: 108 if ( pwnd->cbwndExtra != (sizeof(SBWND)-sizeof(WND)) ) 109 { 110 ERR("IntGetSBData Wrong Extra bytes for CTL Scrollbar!\n"); 111 return 0; 112 } 113 pSBWnd = (PSBWND)pwnd; 114 return (PSBDATA)&pSBWnd->SBCalc; 115 default: 116 ERR("IntGetSBData Bad Bar!\n"); 117 } 118 return NULL; 119 } 120 121 static void 122 IntDrawScrollInterior(HWND hWnd, HDC hDC, INT nBar, BOOL Vertical, 123 PSCROLLBARINFO ScrollBarInfo) 124 { 125 INT ThumbSize = ScrollBarInfo->xyThumbBottom - ScrollBarInfo->xyThumbTop; 126 INT ThumbTop = ScrollBarInfo->xyThumbTop; 127 RECT Rect; 128 HBRUSH hSaveBrush, hBrush; 129 BOOL TopSelected = FALSE, BottomSelected = FALSE; 130 131 if (ScrollBarInfo->rgstate[SCROLL_TOP_RECT] & STATE_SYSTEM_PRESSED) 132 TopSelected = TRUE; 133 if (ScrollBarInfo->rgstate[SCROLL_BOTTOM_RECT] & STATE_SYSTEM_PRESSED) 134 BottomSelected = TRUE; 135 136 /* 137 * Only scrollbar controls send WM_CTLCOLORSCROLLBAR. 138 * The window-owned scrollbars need to call DefWndControlColor 139 * to correctly setup default scrollbar colors 140 */ 141 if (nBar == SB_CTL) 142 { 143 hBrush = GetControlBrush( hWnd, hDC, WM_CTLCOLORSCROLLBAR); 144 if (!hBrush) 145 hBrush = GetSysColorBrush(COLOR_SCROLLBAR); 146 } 147 else 148 { 149 hBrush = DefWndControlColor(hDC, CTLCOLOR_SCROLLBAR); 150 } 151 152 hSaveBrush = SelectObject(hDC, hBrush); 153 154 /* Calculate the scroll rectangle */ 155 if (Vertical) 156 { 157 Rect.top = ScrollBarInfo->rcScrollBar.top + ScrollBarInfo->dxyLineButton; 158 Rect.bottom = ScrollBarInfo->rcScrollBar.bottom - ScrollBarInfo->dxyLineButton; 159 Rect.left = ScrollBarInfo->rcScrollBar.left; 160 Rect.right = ScrollBarInfo->rcScrollBar.right; 161 } 162 else 163 { 164 Rect.top = ScrollBarInfo->rcScrollBar.top; 165 Rect.bottom = ScrollBarInfo->rcScrollBar.bottom; 166 Rect.left = ScrollBarInfo->rcScrollBar.left + ScrollBarInfo->dxyLineButton; 167 Rect.right = ScrollBarInfo->rcScrollBar.right - ScrollBarInfo->dxyLineButton; 168 } 169 170 /* Draw the scroll rectangles and thumb */ 171 if (!ScrollBarInfo->xyThumbBottom) 172 { 173 PatBlt(hDC, Rect.left, Rect.top, Rect.right - Rect.left, 174 Rect.bottom - Rect.top, PATCOPY); 175 176 /* Cleanup and return */ 177 SelectObject(hDC, hSaveBrush); 178 return; 179 } 180 181 ThumbTop -= ScrollBarInfo->dxyLineButton; 182 183 if (ScrollBarInfo->dxyLineButton) 184 { 185 if (Vertical) 186 { 187 if (ThumbSize) 188 { 189 PatBlt(hDC, Rect.left, Rect.top, Rect.right - Rect.left, 190 ThumbTop, TopSelected ? BLACKNESS : PATCOPY); 191 Rect.top += ThumbTop; 192 PatBlt(hDC, Rect.left, Rect.top + ThumbSize, Rect.right - Rect.left, 193 Rect.bottom - Rect.top - ThumbSize, BottomSelected ? BLACKNESS : PATCOPY); 194 Rect.bottom = Rect.top + ThumbSize; 195 } 196 else 197 { 198 if (ThumbTop) 199 { 200 PatBlt(hDC, Rect.left, ScrollBarInfo->dxyLineButton, 201 Rect.right - Rect.left, Rect.bottom - Rect.top, PATCOPY); 202 } 203 } 204 } 205 else 206 { 207 if (ThumbSize) 208 { 209 PatBlt(hDC, Rect.left, Rect.top, ThumbTop, 210 Rect.bottom - Rect.top, TopSelected ? BLACKNESS : PATCOPY); 211 Rect.left += ThumbTop; 212 PatBlt(hDC, Rect.left + ThumbSize, Rect.top, 213 Rect.right - Rect.left - ThumbSize, Rect.bottom - Rect.top, 214 BottomSelected ? BLACKNESS : PATCOPY); 215 Rect.right = Rect.left + ThumbSize; 216 } 217 else 218 { 219 if (ThumbTop) 220 { 221 PatBlt(hDC, ScrollBarInfo->dxyLineButton, Rect.top, 222 Rect.right - Rect.left, Rect.bottom - Rect.top, PATCOPY); 223 } 224 } 225 } 226 } 227 228 /* Draw the thumb */ 229 if (ThumbSize) 230 DrawEdge(hDC, &Rect, EDGE_RAISED, BF_RECT | BF_MIDDLE); 231 232 /* Cleanup */ 233 SelectObject(hDC, hSaveBrush); 234 } 235 236 static VOID FASTCALL 237 IntDrawScrollArrows(HDC hDC, PSCROLLBARINFO ScrollBarInfo, BOOL Vertical) 238 { 239 RECT RectLT, RectRB; 240 INT ScrollDirFlagLT, ScrollDirFlagRB; 241 242 RectLT = RectRB = ScrollBarInfo->rcScrollBar; 243 if (Vertical) 244 { 245 ScrollDirFlagLT = DFCS_SCROLLUP; 246 ScrollDirFlagRB = DFCS_SCROLLDOWN; 247 RectLT.bottom = RectLT.top + ScrollBarInfo->dxyLineButton; 248 RectRB.top = RectRB.bottom - ScrollBarInfo->dxyLineButton; 249 } 250 else 251 { 252 ScrollDirFlagLT = DFCS_SCROLLLEFT; 253 ScrollDirFlagRB = DFCS_SCROLLRIGHT; 254 RectLT.right = RectLT.left + ScrollBarInfo->dxyLineButton; 255 RectRB.left = RectRB.right - ScrollBarInfo->dxyLineButton; 256 } 257 258 if (ScrollBarInfo->rgstate[SCROLL_TOP_ARROW] & STATE_SYSTEM_PRESSED) 259 { 260 ScrollDirFlagLT |= DFCS_PUSHED | DFCS_FLAT; 261 } 262 if (ScrollBarInfo->rgstate[SCROLL_TOP_ARROW] & STATE_SYSTEM_UNAVAILABLE) 263 { 264 ScrollDirFlagLT |= DFCS_INACTIVE; 265 } 266 if (ScrollBarInfo->rgstate[SCROLL_BOTTOM_ARROW] & STATE_SYSTEM_PRESSED) 267 { 268 ScrollDirFlagRB |= DFCS_PUSHED | DFCS_FLAT; 269 } 270 if (ScrollBarInfo->rgstate[SCROLL_BOTTOM_ARROW] & STATE_SYSTEM_UNAVAILABLE) 271 { 272 ScrollDirFlagRB |= DFCS_INACTIVE; 273 } 274 275 DrawFrameControl(hDC, &RectLT, DFC_SCROLL, ScrollDirFlagLT); 276 DrawFrameControl(hDC, &RectRB, DFC_SCROLL, ScrollDirFlagRB); 277 } 278 279 static VOID FASTCALL 280 IntScrollDrawMovingThumb(HDC Dc, PSCROLLBARINFO ScrollBarInfo, BOOL Vertical) 281 { 282 INT Pos = ScrollTrackingPos; 283 INT MaxSize; 284 INT OldTop; 285 286 if (Vertical) 287 MaxSize = ScrollBarInfo->rcScrollBar.bottom - ScrollBarInfo->rcScrollBar.top; 288 else 289 MaxSize = ScrollBarInfo->rcScrollBar.right - ScrollBarInfo->rcScrollBar.left; 290 291 MaxSize -= ScrollBarInfo->dxyLineButton + ScrollBarInfo->xyThumbBottom - ScrollBarInfo->xyThumbTop; 292 293 if (Pos < ScrollBarInfo->dxyLineButton) 294 Pos = ScrollBarInfo->dxyLineButton; 295 else if (MaxSize < Pos) 296 Pos = MaxSize; 297 298 OldTop = ScrollBarInfo->xyThumbTop; 299 ScrollBarInfo->xyThumbBottom = Pos + ScrollBarInfo->xyThumbBottom - ScrollBarInfo->xyThumbTop; 300 ScrollBarInfo->xyThumbTop = Pos; 301 IntDrawScrollInterior(ScrollTrackingWin, Dc, ScrollTrackingBar, Vertical, ScrollBarInfo); 302 ScrollBarInfo->xyThumbBottom = OldTop + ScrollBarInfo->xyThumbBottom - ScrollBarInfo->xyThumbTop; 303 ScrollBarInfo->xyThumbTop = OldTop; 304 305 ScrollMovingThumb = !ScrollMovingThumb; 306 } 307 308 static LONG FASTCALL 309 IntScrollGetObjectId(INT SBType) 310 { 311 if (SBType == SB_VERT) 312 return OBJID_VSCROLL; 313 if (SBType == SB_HORZ) 314 return OBJID_HSCROLL; 315 return OBJID_CLIENT; 316 } 317 318 static BOOL FASTCALL 319 IntGetScrollBarInfo(HWND Wnd, INT Bar, PSCROLLBARINFO ScrollBarInfo) 320 { 321 ScrollBarInfo->cbSize = sizeof(SCROLLBARINFO); 322 323 return NtUserGetScrollBarInfo(Wnd, IntScrollGetObjectId(Bar), ScrollBarInfo); 324 } 325 326 void 327 IntDrawScrollBar(HWND Wnd, HDC DC, INT Bar) 328 { 329 //PSBWND pSBWnd; 330 //INT ThumbSize; 331 SCROLLBARINFO Info; 332 BOOL Vertical; 333 334 /* 335 * Get scroll bar info. 336 */ 337 switch (Bar) 338 { 339 case SB_HORZ: 340 Vertical = FALSE; 341 break; 342 343 case SB_VERT: 344 Vertical = TRUE; 345 break; 346 347 case SB_CTL: 348 Vertical = (GetWindowLongPtrW(Wnd, GWL_STYLE) & SBS_VERT) != 0; 349 break; 350 351 default: 352 return; 353 } 354 if (!IntGetScrollBarInfo(Wnd, Bar, &Info)) 355 { 356 return; 357 } 358 359 if (IsRectEmpty(&Info.rcScrollBar)) 360 { 361 return; 362 } 363 364 //ThumbSize = pSBWnd->pSBCalc->pxThumbBottom - pSBWnd->pSBCalc->pxThumbTop; 365 366 /* 367 * Draw the arrows. 368 */ 369 if (Info.dxyLineButton) 370 { 371 IntDrawScrollArrows(DC, &Info, Vertical); 372 } 373 374 /* 375 * Draw the interior. 376 */ 377 IntDrawScrollInterior(Wnd, DC, Bar, Vertical, &Info); 378 379 /* 380 * If scroll bar has focus, reposition the caret. 381 */ 382 if (Wnd == GetFocus() && SB_CTL == Bar) 383 { 384 if (Vertical) 385 { 386 SetCaretPos(Info.rcScrollBar.top + 1, Info.dxyLineButton + 1); 387 } 388 else 389 { 390 SetCaretPos(Info.dxyLineButton + 1, Info.rcScrollBar.top + 1); 391 } 392 } 393 } 394 395 static BOOL FASTCALL 396 IntScrollPtInRectEx(LPRECT Rect, POINT Pt, BOOL Vertical) 397 { 398 RECT TempRect = *Rect; 399 int scrollbarWidth; 400 401 /* Pad hit rect to allow mouse to be dragged outside of scrollbar and 402 * still be considered in the scrollbar. */ 403 if (Vertical) 404 { 405 scrollbarWidth = Rect->right - Rect->left; 406 TempRect.left -= scrollbarWidth*8; 407 TempRect.right += scrollbarWidth*8; 408 TempRect.top -= scrollbarWidth*2; 409 TempRect.bottom += scrollbarWidth*2; 410 } 411 else 412 { 413 scrollbarWidth = Rect->bottom - Rect->top; 414 TempRect.left -= scrollbarWidth*2; 415 TempRect.right += scrollbarWidth*2; 416 TempRect.top -= scrollbarWidth*8; 417 TempRect.bottom += scrollbarWidth*8; 418 } 419 420 return PtInRect(&TempRect, Pt); 421 } 422 423 static DWORD FASTCALL 424 IntScrollHitTest(PSCROLLBARINFO ScrollBarInfo, BOOL Vertical, POINT Pt, BOOL Dragging) 425 { 426 INT ArrowSize, ThumbSize, ThumbPos; 427 428 if ((Dragging && ! IntScrollPtInRectEx(&ScrollBarInfo->rcScrollBar, Pt, Vertical)) || 429 ! PtInRect(&ScrollBarInfo->rcScrollBar, Pt)) return SCROLL_NOWHERE; 430 431 ThumbPos = ScrollBarInfo->xyThumbTop; 432 ThumbSize = ScrollBarInfo->xyThumbBottom - ThumbPos; 433 ArrowSize = ScrollBarInfo->dxyLineButton; 434 435 if (Vertical) 436 { 437 if (Pt.y < ScrollBarInfo->rcScrollBar.top + ArrowSize) return SCROLL_TOP_ARROW; 438 if (Pt.y >= ScrollBarInfo->rcScrollBar.bottom - ArrowSize) return SCROLL_BOTTOM_ARROW; 439 if (!ThumbPos) return SCROLL_TOP_RECT; 440 Pt.y -= ScrollBarInfo->rcScrollBar.top; 441 if (Pt.y < ThumbPos) return SCROLL_TOP_RECT; 442 if (Pt.y >= ThumbPos + ThumbSize) return SCROLL_BOTTOM_RECT; 443 } 444 else 445 { 446 if (Pt.x < ScrollBarInfo->rcScrollBar.left + ArrowSize) return SCROLL_TOP_ARROW; 447 if (Pt.x >= ScrollBarInfo->rcScrollBar.right - ArrowSize) return SCROLL_BOTTOM_ARROW; 448 if (!ThumbPos) return SCROLL_TOP_RECT; 449 Pt.x -= ScrollBarInfo->rcScrollBar.left; 450 if (Pt.x < ThumbPos) return SCROLL_TOP_RECT; 451 if (Pt.x >= ThumbPos + ThumbSize) return SCROLL_BOTTOM_RECT; 452 } 453 454 return SCROLL_THUMB; 455 } 456 457 458 /*********************************************************************** 459 * IntScrollGetScrollBarRect 460 * 461 * Compute the scroll bar rectangle, in drawing coordinates (i.e. client 462 * coords for SB_CTL, window coords for SB_VERT and SB_HORZ). 463 * 'arrowSize' returns the width or height of an arrow (depending on 464 * the orientation of the scrollbar), 'thumbSize' returns the size of 465 * the thumb, and 'thumbPos' returns the position of the thumb 466 * relative to the left or to the top. 467 * Return TRUE if the scrollbar is vertical, FALSE if horizontal. 468 */ 469 static BOOL FASTCALL 470 IntScrollGetScrollBarRect(HWND Wnd, INT Bar, RECT *Rect, 471 INT *ArrowSize, INT *ThumbSize, 472 INT *ThumbPos) 473 { 474 INT Pixels; 475 BOOL Vertical; 476 PWND pWnd; 477 PSBINFO pSBInfo; 478 PSBDATA pSBData; 479 PSBWND pSBWnd; 480 481 pWnd = ValidateHwnd( Wnd ); 482 if (!pWnd) return FALSE; 483 pSBInfo = DesktopPtrToUser(pWnd->pSBInfo); 484 485 *Rect = pWnd->rcClient; 486 OffsetRect( Rect, -pWnd->rcWindow.left, -pWnd->rcWindow.top ); 487 if (pWnd->ExStyle & WS_EX_LAYOUTRTL) 488 mirror_rect( &pWnd->rcWindow, Rect ); 489 490 switch (Bar) 491 { 492 case SB_HORZ: 493 // WIN_GetRectangles( Wnd, COORDS_WINDOW, NULL, Rect ); 494 Rect->top = Rect->bottom; 495 Rect->bottom += GetSystemMetrics(SM_CYHSCROLL); 496 if (pWnd->style & WS_BORDER) 497 { 498 Rect->left--; 499 Rect->right++; 500 } 501 else if (pWnd->style & WS_VSCROLL) 502 { 503 Rect->right++; 504 } 505 Vertical = FALSE; 506 pSBData = &pSBInfo->Horz; 507 break; 508 509 case SB_VERT: 510 // WIN_GetRectangles( Wnd, COORDS_WINDOW, NULL, Rect ); 511 if (pWnd->ExStyle & WS_EX_LEFTSCROLLBAR) 512 { 513 Rect->right = Rect->left; 514 Rect->left -= GetSystemMetrics(SM_CXVSCROLL); 515 } 516 else 517 { 518 Rect->left = Rect->right; 519 Rect->right += GetSystemMetrics(SM_CXVSCROLL); 520 } 521 if (pWnd->style & WS_BORDER) 522 { 523 Rect->top--; 524 Rect->bottom++; 525 } 526 else if (pWnd->style & WS_HSCROLL) 527 { 528 Rect->bottom++; 529 } 530 Vertical = TRUE; 531 pSBData = &pSBInfo->Vert; 532 break; 533 534 case SB_CTL: 535 GetClientRect( Wnd, Rect ); 536 Vertical = (pWnd->style & SBS_VERT); 537 pSBWnd = (PSBWND)pWnd; 538 pSBData = (PSBDATA)&pSBWnd->SBCalc; 539 break; 540 541 default: 542 return FALSE; 543 } 544 545 if (Vertical) Pixels = Rect->bottom - Rect->top; 546 else Pixels = Rect->right - Rect->left; 547 548 if (Pixels <= 2 * GetSystemMetrics(SM_CXVSCROLL) + SCROLL_MIN_RECT) 549 { 550 if (SCROLL_MIN_RECT < Pixels) 551 *ArrowSize = (Pixels - SCROLL_MIN_RECT) / 2; 552 else 553 *ArrowSize = 0; 554 *ThumbPos = *ThumbSize = 0; 555 } 556 else 557 { 558 *ArrowSize = GetSystemMetrics(SM_CXVSCROLL); 559 Pixels -= (2 * (GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP)); 560 if (pSBData->page) 561 { 562 *ThumbSize = MulDiv(Pixels, pSBData->page, (pSBData->posMax - pSBData->posMin + 1)); 563 if (*ThumbSize < SCROLL_MIN_THUMB) *ThumbSize = SCROLL_MIN_THUMB; 564 } 565 else *ThumbSize = GetSystemMetrics(SM_CXVSCROLL); 566 567 if (((Pixels -= *ThumbSize ) < 0) || 568 (( pSBInfo->WSBflags & ESB_DISABLE_BOTH) == ESB_DISABLE_BOTH)) 569 { 570 /* Rectangle too small or scrollbar disabled -> no thumb */ 571 *ThumbPos = *ThumbSize = 0; 572 } 573 else 574 { 575 INT Max = pSBData->posMax - max(pSBData->page - 1, 0); 576 if (pSBData->posMin >= Max) 577 *ThumbPos = *ArrowSize - SCROLL_ARROW_THUMB_OVERLAP; 578 else 579 *ThumbPos = *ArrowSize - SCROLL_ARROW_THUMB_OVERLAP 580 + MulDiv(Pixels, (pSBData->pos - pSBData->posMin),(Max - pSBData->posMin)); 581 } 582 } 583 return Vertical; 584 } 585 586 /*********************************************************************** 587 * IntScrollGetThumbVal 588 * 589 * Compute the current scroll position based on the thumb position in pixels 590 * from the top of the scroll-bar. 591 */ 592 static UINT FASTCALL 593 IntScrollGetThumbVal(HWND Wnd, INT SBType, PSCROLLBARINFO ScrollBarInfo, 594 BOOL Vertical, INT Pos) 595 { 596 PWND pWnd; 597 PSBDATA pSBData; 598 INT Pixels = Vertical ? ScrollBarInfo->rcScrollBar.bottom 599 - ScrollBarInfo->rcScrollBar.top 600 : ScrollBarInfo->rcScrollBar.right 601 - ScrollBarInfo->rcScrollBar.left; 602 603 pWnd = ValidateHwnd( Wnd ); 604 if (!pWnd) return FALSE; 605 606 pSBData = IntGetSBData(pWnd, SBType); 607 608 if ((Pixels -= 2 * ScrollBarInfo->dxyLineButton) <= 0) 609 { 610 return pSBData->posMin; 611 } 612 613 if ((Pixels -= (ScrollBarInfo->xyThumbBottom - ScrollBarInfo->xyThumbTop)) <= 0) 614 { 615 return pSBData->posMin; 616 } 617 618 Pos = Pos - ScrollBarInfo->dxyLineButton; 619 if (Pos < 0) 620 { 621 Pos = 0; 622 } 623 if (Pos > Pixels) Pos = Pixels; 624 625 if (!pSBData->page) 626 Pos *= pSBData->posMax - pSBData->posMin; 627 else 628 Pos *= pSBData->posMax - pSBData->posMin - pSBData->page + 1; 629 630 return pSBData->posMin + ((Pos + Pixels / 2) / Pixels); 631 } 632 633 /*********************************************************************** 634 * IntScrollClipPos 635 */ 636 static POINT IntScrollClipPos(PRECT lpRect, POINT pt) 637 { 638 if( pt.x < lpRect->left ) 639 pt.x = lpRect->left; 640 else 641 if( pt.x > lpRect->right ) 642 pt.x = lpRect->right; 643 644 if( pt.y < lpRect->top ) 645 pt.y = lpRect->top; 646 else 647 if( pt.y > lpRect->bottom ) 648 pt.y = lpRect->bottom; 649 650 return pt; 651 } 652 653 /*********************************************************************** 654 * IntScrollDrawSizeGrip 655 * 656 * Draw the size grip. 657 */ 658 static void FASTCALL 659 IntScrollDrawSizeGrip(HWND Wnd, HDC Dc) 660 { 661 RECT Rect; 662 663 GetClientRect(Wnd, &Rect); 664 FillRect(Dc, &Rect, GetSysColorBrush(COLOR_SCROLLBAR)); 665 Rect.left = max(Rect.left, Rect.right - GetSystemMetrics(SM_CXVSCROLL) - 1); 666 Rect.top = max(Rect.top, Rect.bottom - GetSystemMetrics(SM_CYHSCROLL) - 1); 667 DrawFrameControl(Dc, &Rect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); 668 } 669 670 /*********************************************************************** 671 * SCROLL_RefreshScrollBar 672 * 673 * Repaint the scroll bar interior after a SetScrollRange() or 674 * SetScrollPos() call. 675 */ 676 static void SCROLL_RefreshScrollBar( HWND hwnd, INT nBar, 677 BOOL arrows, BOOL interior ) 678 { 679 HDC hdc = GetDCEx( hwnd, 0, 680 DCX_CACHE | ((nBar == SB_CTL) ? 0 : DCX_WINDOW) ); 681 if (!hdc) return; 682 683 IntDrawScrollBar( hwnd, hdc, nBar);//, arrows, interior ); 684 ReleaseDC( hwnd, hdc ); 685 } 686 687 688 /*********************************************************************** 689 * IntScrollHandleKbdEvent 690 * 691 * Handle a keyboard event (only for SB_CTL scrollbars with focus). 692 */ 693 static void FASTCALL 694 IntScrollHandleKbdEvent( 695 HWND Wnd /* [in] Handle of window with scrollbar(s) */, 696 WPARAM wParam /* [in] Variable input including enable state */, 697 LPARAM lParam /* [in] Variable input including input point */) 698 { 699 TRACE("Wnd=%p wParam=%ld lParam=%ld\n", Wnd, wParam, lParam); 700 701 /* hide caret on first KEYDOWN to prevent flicker */ 702 if (0 == (lParam & PFD_DOUBLEBUFFER_DONTCARE)) 703 { 704 HideCaret(Wnd); 705 } 706 707 switch(wParam) 708 { 709 case VK_PRIOR: 710 wParam = SB_PAGEUP; 711 break; 712 713 case VK_NEXT: 714 wParam = SB_PAGEDOWN; 715 break; 716 717 case VK_HOME: 718 wParam = SB_TOP; 719 break; 720 721 case VK_END: 722 wParam = SB_BOTTOM; 723 break; 724 725 case VK_UP: 726 wParam = SB_LINEUP; 727 break; 728 729 case VK_DOWN: 730 wParam = SB_LINEDOWN; 731 break; 732 733 default: 734 return; 735 } 736 737 SendMessageW(GetParent(Wnd), 738 (0 != (GetWindowLongPtrW(Wnd, GWL_STYLE ) & SBS_VERT) ? 739 WM_VSCROLL : WM_HSCROLL), wParam, (LPARAM) Wnd); 740 } 741 742 /*********************************************************************** 743 * IntScrollHandleScrollEvent 744 * 745 * Handle a mouse or timer event for the scrollbar. 746 * 'Pt' is the location of the mouse event in drawing coordinates 747 */ 748 static VOID FASTCALL 749 IntScrollHandleScrollEvent(HWND Wnd, INT SBType, UINT Msg, POINT Pt) 750 { 751 static POINT PrevPt; /* Previous mouse position for timer events */ 752 static UINT TrackThumbPos; /* Thumb position when tracking started. */ 753 static INT LastClickPos; /* Position in the scroll-bar of the last 754 button-down event. */ 755 static INT LastMousePos; /* Position in the scroll-bar of the last 756 mouse event. */ 757 758 DWORD HitTest; 759 HWND WndOwner, WndCtl; 760 BOOL Vertical; 761 HDC Dc; 762 SCROLLBARINFO ScrollBarInfo; 763 SETSCROLLBARINFO NewInfo; 764 765 if (! IntGetScrollBarInfo(Wnd, SBType, &ScrollBarInfo)) 766 { 767 return; 768 } 769 if ((ScrollTrackHitTest == SCROLL_NOWHERE) && (Msg != WM_LBUTTONDOWN)) 770 { 771 //// ReactOS : Justin Case something goes wrong. 772 if (Wnd == GetCapture()) 773 { 774 ReleaseCapture(); 775 } 776 //// 777 return; 778 } 779 780 NewInfo.nTrackPos = ScrollTrackingVal; 781 NewInfo.reserved = ScrollBarInfo.reserved; 782 memcpy(NewInfo.rgstate, ScrollBarInfo.rgstate, (CCHILDREN_SCROLLBAR + 1) * sizeof(DWORD)); 783 784 if (SBType == SB_CTL && (GetWindowLongPtrW(Wnd, GWL_STYLE) & (SBS_SIZEGRIP | SBS_SIZEBOX))) 785 { 786 switch(Msg) 787 { 788 case WM_LBUTTONDOWN: /* Initialise mouse tracking */ 789 HideCaret(Wnd); /* hide caret while holding down LBUTTON */ 790 SetCapture(Wnd); 791 PrevPt = Pt; 792 ScrollTrackHitTest = HitTest = SCROLL_THUMB; 793 break; 794 case WM_MOUSEMOVE: 795 GetClientRect(GetParent(GetParent(Wnd)), &ScrollBarInfo.rcScrollBar); 796 PrevPt = Pt; 797 break; 798 case WM_LBUTTONUP: 799 ReleaseCapture(); 800 ScrollTrackHitTest = HitTest = SCROLL_NOWHERE; 801 if (Wnd == GetFocus()) ShowCaret(Wnd); 802 break; 803 case WM_SYSTIMER: 804 Pt = PrevPt; 805 break; 806 } 807 return; 808 } 809 810 Dc = GetDCEx(Wnd, 0, DCX_CACHE | ((SB_CTL == SBType) ? 0 : DCX_WINDOW)); 811 if (SB_VERT == SBType) 812 { 813 Vertical = TRUE; 814 } 815 else if (SB_HORZ == SBType) 816 { 817 Vertical = FALSE; 818 } 819 else 820 { 821 Vertical = (0 != (GetWindowLongPtrW(Wnd, GWL_STYLE) & SBS_VERT)); 822 } 823 WndOwner = (SB_CTL == SBType) ? GetParent(Wnd) : Wnd; 824 WndCtl = (SB_CTL == SBType) ? Wnd : NULL; 825 826 switch (Msg) 827 { 828 case WM_LBUTTONDOWN: /* Initialise mouse tracking */ 829 HideCaret(Wnd); /* hide caret while holding down LBUTTON */ 830 ScrollTrackVertical = Vertical; 831 ScrollTrackHitTest = HitTest = IntScrollHitTest(&ScrollBarInfo, Vertical, Pt, FALSE ); 832 LastClickPos = Vertical ? (Pt.y - ScrollBarInfo.rcScrollBar.top) 833 : (Pt.x - ScrollBarInfo.rcScrollBar.left); 834 LastMousePos = LastClickPos; 835 TrackThumbPos = ScrollBarInfo.xyThumbTop; 836 PrevPt = Pt; 837 if (SBType == SB_CTL && (GetWindowLongPtrW(Wnd, GWL_STYLE) & WS_TABSTOP)) SetFocus(Wnd); 838 SetCapture(Wnd); 839 ScrollBarInfo.rgstate[ScrollTrackHitTest] |= STATE_SYSTEM_PRESSED; 840 NewInfo.rgstate[ScrollTrackHitTest] = ScrollBarInfo.rgstate[ScrollTrackHitTest]; 841 NtUserSetScrollBarInfo(Wnd, IntScrollGetObjectId(SBType), &NewInfo); 842 break; 843 844 case WM_MOUSEMOVE: 845 HitTest = IntScrollHitTest(&ScrollBarInfo, Vertical, Pt, TRUE); 846 PrevPt = Pt; 847 break; 848 849 case WM_LBUTTONUP: 850 HitTest = SCROLL_NOWHERE; 851 ReleaseCapture(); 852 /* if scrollbar has focus, show back caret */ 853 if (Wnd == GetFocus()) ShowCaret(Wnd); 854 ScrollBarInfo.rgstate[ScrollTrackHitTest] &= ~STATE_SYSTEM_PRESSED; 855 NewInfo.rgstate[ScrollTrackHitTest] = ScrollBarInfo.rgstate[ScrollTrackHitTest]; 856 NtUserSetScrollBarInfo(Wnd, IntScrollGetObjectId(SBType), &NewInfo); 857 858 IntDrawScrollInterior(Wnd,Dc,SBType,Vertical,&ScrollBarInfo); 859 IntDrawScrollArrows(Dc, &ScrollBarInfo, Vertical); 860 861 break; 862 863 case WM_SYSTIMER: 864 Pt = PrevPt; 865 HitTest = IntScrollHitTest(&ScrollBarInfo, Vertical, Pt, FALSE); 866 break; 867 868 default: 869 return; /* Should never happen */ 870 } 871 872 TRACE("Event: hwnd=%p bar=%d msg=%s pt=%d,%d hit=%d\n", 873 Wnd, SBType, SPY_GetMsgName(Msg,Wnd), Pt.x, Pt.y, HitTest ); 874 875 switch (ScrollTrackHitTest) 876 { 877 case SCROLL_NOWHERE: /* No tracking in progress */ 878 break; 879 880 case SCROLL_TOP_ARROW: 881 if (HitTest == ScrollTrackHitTest) 882 { 883 if ((WM_LBUTTONDOWN == Msg) || (WM_SYSTIMER == Msg)) 884 { 885 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL, 886 SB_LINEUP, (LPARAM) WndCtl); 887 } 888 SetSystemTimer(Wnd, SCROLL_TIMER, (WM_LBUTTONDOWN == Msg) ? 889 SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, 890 (TIMERPROC) NULL); 891 } 892 else 893 { 894 KillSystemTimer(Wnd, SCROLL_TIMER); 895 } 896 break; 897 898 case SCROLL_TOP_RECT: 899 if (HitTest == ScrollTrackHitTest) 900 { 901 if ((WM_LBUTTONDOWN == Msg) || (WM_SYSTIMER == Msg)) 902 { 903 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL, 904 SB_PAGEUP, (LPARAM) WndCtl); 905 } 906 SetSystemTimer(Wnd, SCROLL_TIMER, (WM_LBUTTONDOWN == Msg) ? 907 SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, 908 (TIMERPROC) NULL); 909 } 910 else 911 { 912 KillSystemTimer(Wnd, SCROLL_TIMER); 913 } 914 break; 915 916 case SCROLL_THUMB: 917 if (Msg == WM_LBUTTONDOWN) 918 { 919 ScrollTrackingWin = Wnd; 920 ScrollTrackingBar = SBType; 921 ScrollTrackingPos = TrackThumbPos + LastMousePos - LastClickPos; 922 ScrollTrackingVal = IntScrollGetThumbVal(Wnd, SBType, &ScrollBarInfo, 923 Vertical, ScrollTrackingPos); 924 NewInfo.nTrackPos = ScrollTrackingVal; 925 NtUserSetScrollBarInfo(Wnd, IntScrollGetObjectId(SBType), &NewInfo); 926 IntScrollDrawMovingThumb(Dc, &ScrollBarInfo, Vertical); 927 } 928 else if (Msg == WM_LBUTTONUP) 929 { 930 ScrollTrackingWin = 0; 931 ScrollTrackingVal = 0; 932 IntDrawScrollInterior(Wnd, Dc, SBType, Vertical, &ScrollBarInfo); 933 } 934 else /* WM_MOUSEMOVE */ 935 { 936 UINT Pos; 937 938 if (! IntScrollPtInRectEx(&ScrollBarInfo.rcScrollBar, Pt, Vertical)) 939 { 940 Pos = LastClickPos; 941 } 942 else 943 { 944 Pt = IntScrollClipPos(&ScrollBarInfo.rcScrollBar, Pt); 945 Pos = Vertical ? (Pt.y - ScrollBarInfo.rcScrollBar.top) 946 : (Pt.x - ScrollBarInfo.rcScrollBar.left); 947 } 948 if (Pos != LastMousePos || ! ScrollMovingThumb) 949 { 950 LastMousePos = Pos; 951 ScrollTrackingPos = TrackThumbPos + Pos - LastClickPos; 952 ScrollTrackingVal = IntScrollGetThumbVal(Wnd, SBType, &ScrollBarInfo, 953 Vertical, ScrollTrackingPos); 954 NewInfo.nTrackPos = ScrollTrackingVal; 955 NtUserSetScrollBarInfo(Wnd, IntScrollGetObjectId(SBType), &NewInfo); 956 IntScrollDrawMovingThumb(Dc, &ScrollBarInfo, Vertical); 957 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL, 958 MAKEWPARAM(SB_THUMBTRACK, ScrollTrackingVal), 959 (LPARAM) WndCtl); 960 } 961 } 962 break; 963 964 case SCROLL_BOTTOM_RECT: 965 if (HitTest == ScrollTrackHitTest) 966 { 967 if ((Msg == WM_LBUTTONDOWN) || (Msg == WM_SYSTIMER)) 968 { 969 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL, 970 SB_PAGEDOWN, (LPARAM) WndCtl); 971 } 972 SetSystemTimer(Wnd, SCROLL_TIMER, (WM_LBUTTONDOWN == Msg) ? 973 SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, 974 (TIMERPROC) NULL); 975 } 976 else 977 { 978 KillSystemTimer(Wnd, SCROLL_TIMER); 979 } 980 break; 981 982 case SCROLL_BOTTOM_ARROW: 983 if (HitTest == ScrollTrackHitTest) 984 { 985 if ((Msg == WM_LBUTTONDOWN) || (Msg == WM_SYSTIMER)) 986 { 987 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL, 988 SB_LINEDOWN, (LPARAM) WndCtl); 989 } 990 SetSystemTimer(Wnd, SCROLL_TIMER, (WM_LBUTTONDOWN == Msg) ? 991 SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, 992 (TIMERPROC) NULL); 993 } 994 else KillSystemTimer(Wnd, SCROLL_TIMER); 995 break; 996 } 997 998 if (Msg == WM_LBUTTONDOWN) 999 { 1000 if (SCROLL_THUMB == HitTest) 1001 { 1002 UINT Val = IntScrollGetThumbVal(Wnd, SBType, &ScrollBarInfo, Vertical, 1003 TrackThumbPos + LastMousePos - LastClickPos); 1004 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL, 1005 MAKEWPARAM(SB_THUMBTRACK, Val), (LPARAM) WndCtl); 1006 } 1007 } 1008 1009 if (Msg == WM_LBUTTONUP) 1010 { 1011 HitTest = ScrollTrackHitTest; 1012 ScrollTrackHitTest = SCROLL_NOWHERE; /* Terminate tracking */ 1013 1014 if (SCROLL_THUMB == HitTest) 1015 { 1016 UINT Val = IntScrollGetThumbVal(Wnd, SBType, &ScrollBarInfo, Vertical, 1017 TrackThumbPos + LastMousePos - LastClickPos); 1018 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL, 1019 MAKEWPARAM(SB_THUMBPOSITION, Val), (LPARAM) WndCtl); 1020 } 1021 /* SB_ENDSCROLL doesn't report thumb position */ 1022 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL, 1023 SB_ENDSCROLL, (LPARAM) WndCtl); 1024 1025 /* Terminate tracking */ 1026 ScrollTrackingWin = 0; 1027 } 1028 1029 ReleaseDC(Wnd, Dc); 1030 } 1031 1032 1033 /*********************************************************************** 1034 * IntScrollCreateScrollBar 1035 * 1036 * Create a scroll bar 1037 */ 1038 static void IntScrollCreateScrollBar( 1039 HWND Wnd /* [in] Handle of window with scrollbar(s) */, 1040 LPCREATESTRUCTW lpCreate /* [in] The style and place of the scroll bar */) 1041 { 1042 SCROLLINFO Info; 1043 1044 Info.cbSize = sizeof(SCROLLINFO); 1045 Info.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; 1046 Info.nMin = 0; 1047 Info.nMax = 0; 1048 Info.nPage = 0; 1049 Info.nPos = 0; 1050 Info.nTrackPos = 0; 1051 NtUserSetScrollInfo(Wnd, SB_CTL, &Info, FALSE); 1052 1053 TRACE("hwnd=%p lpCreate=%p\n", Wnd, lpCreate); 1054 1055 #if 0 /* FIXME */ 1056 if (lpCreate->style & WS_DISABLED) 1057 { 1058 // info->flags = ESB_DISABLE_BOTH; 1059 //NtUserEnableScrollBar(Wnd,SB_CTL,(wParam ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH)); 1060 NtUserMessageCall( Wnd, WM_ENABLE, FALSE, 0, 0, FNID_SCROLLBAR, FALSE); 1061 ERR("Created WS_DISABLED scrollbar\n"); 1062 } 1063 #endif 1064 if (0 != (lpCreate->style & (SBS_SIZEGRIP | SBS_SIZEBOX))) 1065 { 1066 if (0 != (lpCreate->style & SBS_SIZEBOXTOPLEFTALIGN)) 1067 { 1068 MoveWindow(Wnd, lpCreate->x, lpCreate->y, GetSystemMetrics(SM_CXVSCROLL) + 1, 1069 GetSystemMetrics(SM_CYHSCROLL) + 1, FALSE); 1070 } 1071 else if (0 != (lpCreate->style & SBS_SIZEBOXBOTTOMRIGHTALIGN)) 1072 { 1073 MoveWindow(Wnd, lpCreate->x + lpCreate->cx - GetSystemMetrics(SM_CXVSCROLL) - 1, 1074 lpCreate->y + lpCreate->cy - GetSystemMetrics(SM_CYHSCROLL) - 1, 1075 GetSystemMetrics(SM_CXVSCROLL) + 1, 1076 GetSystemMetrics(SM_CYHSCROLL) + 1, FALSE); 1077 } 1078 } 1079 else if (0 != (lpCreate->style & SBS_VERT)) 1080 { 1081 if (0 != (lpCreate->style & SBS_LEFTALIGN)) 1082 { 1083 MoveWindow(Wnd, lpCreate->x, lpCreate->y, 1084 GetSystemMetrics(SM_CXVSCROLL) + 1, lpCreate->cy, FALSE); 1085 } 1086 else if (0 != (lpCreate->style & SBS_RIGHTALIGN)) 1087 { 1088 MoveWindow(Wnd, 1089 lpCreate->x + lpCreate->cx - GetSystemMetrics(SM_CXVSCROLL) - 1, 1090 lpCreate->y, 1091 GetSystemMetrics(SM_CXVSCROLL) + 1, lpCreate->cy, FALSE); 1092 } 1093 } 1094 else /* SBS_HORZ */ 1095 { 1096 if (0 != (lpCreate->style & SBS_TOPALIGN)) 1097 { 1098 MoveWindow(Wnd, lpCreate->x, lpCreate->y, 1099 lpCreate->cx, GetSystemMetrics(SM_CYHSCROLL) + 1, FALSE); 1100 } 1101 else if (0 != (lpCreate->style & SBS_BOTTOMALIGN)) 1102 { 1103 MoveWindow(Wnd, 1104 lpCreate->x, 1105 lpCreate->y + lpCreate->cy - GetSystemMetrics(SM_CYHSCROLL) - 1, 1106 lpCreate->cx, GetSystemMetrics(SM_CYHSCROLL) + 1, FALSE); 1107 } 1108 } 1109 } 1110 1111 /* USER32 INTERNAL FUNCTIONS **************************************************/ 1112 1113 /*********************************************************************** 1114 * ScrollTrackScrollBar 1115 * 1116 * Track a mouse button press on a scroll-bar. 1117 * pt is in screen-coordinates for non-client scroll bars. 1118 */ 1119 VOID FASTCALL 1120 ScrollTrackScrollBar(HWND Wnd, INT SBType, POINT Pt) 1121 { 1122 MSG Msg; 1123 UINT XOffset = 0, YOffset = 0; 1124 1125 if (SBType != SB_CTL) 1126 { // Used with CMD mouse tracking. 1127 PWND pwnd = ValidateHwnd(Wnd); 1128 if (!pwnd) return; 1129 XOffset = pwnd->rcClient.left - pwnd->rcWindow.left; 1130 YOffset = pwnd->rcClient.top - pwnd->rcWindow.top; 1131 // RECT rect; 1132 // WIN_GetRectangles( Wnd, COORDS_CLIENT, &rect, NULL ); 1133 ScreenToClient(Wnd, &Pt); 1134 // Pt.x -= rect.left; 1135 // Pt.y -= rect.top; 1136 Pt.x += XOffset; 1137 Pt.y += YOffset; 1138 } 1139 1140 IntScrollHandleScrollEvent(Wnd, SBType, WM_LBUTTONDOWN, Pt); 1141 1142 do 1143 { 1144 if (!GetMessageW(&Msg, 0, 0, 0)) break; 1145 if (CallMsgFilterW(&Msg, MSGF_SCROLLBAR)) continue; 1146 if ( Msg.message == WM_LBUTTONUP || 1147 Msg.message == WM_MOUSEMOVE || 1148 (Msg.message == WM_SYSTIMER && Msg.wParam == SCROLL_TIMER)) 1149 { 1150 Pt.x = LOWORD(Msg.lParam) + XOffset; 1151 Pt.y = HIWORD(Msg.lParam) + YOffset; 1152 IntScrollHandleScrollEvent(Wnd, SBType, Msg.message, Pt); 1153 } 1154 else 1155 { 1156 TranslateMessage(&Msg); 1157 DispatchMessageW(&Msg); 1158 } 1159 if (!IsWindow(Wnd)) 1160 { 1161 ReleaseCapture(); 1162 break; 1163 } 1164 } while (Msg.message != WM_LBUTTONUP && GetCapture() == Wnd); 1165 } 1166 1167 1168 static DWORD FASTCALL 1169 IntSetScrollInfo(HWND Wnd, LPCSCROLLINFO Info, BOOL bRedraw) 1170 { 1171 DWORD Ret = NtUserSetScrollInfo(Wnd, SB_CTL, Info, bRedraw); 1172 if (Ret) IntNotifyWinEvent(EVENT_OBJECT_VALUECHANGE, Wnd, OBJID_CLIENT, CHILDID_SELF, WEF_SETBYWNDPTI); 1173 return Ret; 1174 } 1175 1176 1177 /*********************************************************************** 1178 * ScrollBarWndProc 1179 */ 1180 LRESULT WINAPI 1181 ScrollBarWndProc_common(WNDPROC DefWindowProc, HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, BOOL unicode ) 1182 { 1183 #ifdef __REACTOS__ // Do this now, remove after Server side is fixed. 1184 PWND pWnd; 1185 PSBWND pSBWnd; 1186 SCROLLINFO ScrollInfo; 1187 1188 pWnd = ValidateHwnd(Wnd); 1189 if (pWnd) 1190 { 1191 if (!pWnd->fnid) 1192 { 1193 TRACE("ScrollBar CTL size %d\n", (sizeof(SBWND)-sizeof(WND))); 1194 if ( pWnd->cbwndExtra != (sizeof(SBWND)-sizeof(WND)) ) 1195 { 1196 ERR("Wrong Extra bytes for Scrollbar!\n"); 1197 return 0; 1198 } 1199 1200 if (Msg != WM_CREATE) 1201 { 1202 return DefWindowProc(Wnd, Msg, wParam, lParam); 1203 } 1204 NtUserSetWindowFNID(Wnd, FNID_SCROLLBAR); 1205 } 1206 else 1207 { 1208 if (pWnd->fnid != FNID_SCROLLBAR) 1209 { 1210 ERR("Wrong window class for Scrollbar!\n"); 1211 return 0; 1212 } 1213 } 1214 } 1215 #endif 1216 1217 if (! IsWindow(Wnd)) 1218 { 1219 return 0; 1220 } 1221 1222 // Must be a scroll bar control! 1223 pSBWnd = (PSBWND)pWnd; 1224 1225 switch (Msg) 1226 { 1227 case WM_CREATE: 1228 IntScrollCreateScrollBar(Wnd, (LPCREATESTRUCTW) lParam); 1229 break; 1230 1231 case WM_ENABLE: 1232 { 1233 return SendMessageW( Wnd, SBM_ENABLE_ARROWS, wParam ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH, 0); 1234 } 1235 1236 case WM_LBUTTONDBLCLK: 1237 case WM_LBUTTONDOWN: 1238 if (GetWindowLongW( Wnd, GWL_STYLE ) & SBS_SIZEGRIP) 1239 { 1240 SendMessageW( GetParent(Wnd), WM_SYSCOMMAND, 1241 SC_SIZE + ((GetWindowLongW( Wnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) ? 1242 WMSZ_BOTTOMLEFT : WMSZ_BOTTOMRIGHT), lParam ); 1243 } 1244 else 1245 { 1246 POINT Pt; 1247 Pt.x = (short)LOWORD(lParam); 1248 Pt.y = (short)HIWORD(lParam); 1249 ScrollTrackScrollBar(Wnd, SB_CTL, Pt); 1250 } 1251 break; 1252 1253 case WM_LBUTTONUP: 1254 case WM_MOUSEMOVE: 1255 case WM_SYSTIMER: 1256 { 1257 POINT Pt; 1258 Pt.x = (short)LOWORD(lParam); 1259 Pt.y = (short)HIWORD(lParam); 1260 IntScrollHandleScrollEvent(Wnd, SB_CTL, Msg, Pt); 1261 } 1262 break; 1263 1264 case WM_KEYDOWN: 1265 IntScrollHandleKbdEvent(Wnd, wParam, lParam); 1266 break; 1267 1268 case WM_KEYUP: 1269 ShowCaret(Wnd); 1270 break; 1271 1272 case WM_SETFOCUS: 1273 { 1274 /* Create a caret when a ScrollBar get focus */ 1275 RECT Rect; 1276 int ArrowSize, ThumbSize, ThumbPos, Vertical; 1277 1278 Vertical = IntScrollGetScrollBarRect(Wnd, SB_CTL, &Rect, 1279 &ArrowSize, &ThumbSize, &ThumbPos); 1280 if (! Vertical) 1281 { 1282 CreateCaret(Wnd, (HBITMAP) 1, ThumbSize - 2, Rect.bottom - Rect.top - 2); 1283 SetCaretPos(ThumbPos + 1, Rect.top + 1); 1284 } 1285 else 1286 { 1287 CreateCaret(Wnd, (HBITMAP) 1, Rect.right - Rect.left - 2, ThumbSize - 2); 1288 SetCaretPos(Rect.top + 1, ThumbPos + 1); 1289 } 1290 ShowCaret(Wnd); 1291 } 1292 break; 1293 1294 case WM_KILLFOCUS: 1295 { 1296 RECT Rect; 1297 int ArrowSize, ThumbSize, ThumbPos, Vertical; 1298 1299 Vertical = IntScrollGetScrollBarRect(Wnd, SB_CTL, &Rect, 1300 &ArrowSize, &ThumbSize, &ThumbPos); 1301 if (! Vertical) 1302 { 1303 Rect.left = ThumbPos + 1; 1304 Rect.right = Rect.left + ThumbSize; 1305 } 1306 else 1307 { 1308 Rect.top = ThumbPos + 1; 1309 Rect.bottom = Rect.top + ThumbSize; 1310 } 1311 HideCaret(Wnd); 1312 InvalidateRect(Wnd, &Rect, FALSE); 1313 DestroyCaret(); 1314 } 1315 break; 1316 1317 case WM_ERASEBKGND: 1318 return 1; 1319 1320 case WM_GETDLGCODE: 1321 return DLGC_WANTARROWS; /* Windows returns this value */ 1322 1323 case WM_PAINT: 1324 { 1325 PAINTSTRUCT Ps; 1326 HDC Dc; 1327 1328 Dc = (0 != wParam ? (HDC) wParam : BeginPaint(Wnd, &Ps)); 1329 1330 if (GetWindowLongPtrW(Wnd, GWL_STYLE) & SBS_SIZEGRIP) 1331 { 1332 IntScrollDrawSizeGrip(Wnd, Dc); 1333 } 1334 else if (0 != (GetWindowLongPtrW(Wnd, GWL_STYLE) & SBS_SIZEBOX)) 1335 { 1336 RECT Rect; 1337 GetClientRect(Wnd, &Rect); 1338 FillRect(Dc, &Rect, GetSysColorBrush(COLOR_SCROLLBAR)); 1339 } 1340 else 1341 { 1342 IntDrawScrollBar(Wnd, Dc, SB_CTL/*, TRUE, TRUE*/); 1343 } 1344 1345 if (0 == wParam) 1346 { 1347 EndPaint(Wnd, &Ps); 1348 } 1349 } 1350 break; 1351 1352 case SBM_GETPOS: 1353 return pSBWnd->SBCalc.pos; 1354 1355 case SBM_GETRANGE: 1356 *(LPINT)wParam = pSBWnd->SBCalc.posMin; 1357 *(LPINT)lParam = pSBWnd->SBCalc.posMax; 1358 // This message does not return a value. 1359 return 0; 1360 1361 case SBM_ENABLE_ARROWS: 1362 return EnableScrollBar( Wnd, SB_CTL, wParam ); 1363 1364 case SBM_SETPOS: 1365 { 1366 ScrollInfo.cbSize = sizeof(SCROLLINFO); 1367 ScrollInfo.fMask = SIF_POS|SIF_PREVIOUSPOS; 1368 ScrollInfo.nPos = wParam; 1369 return IntSetScrollInfo(Wnd, &ScrollInfo, lParam); 1370 } 1371 1372 case SBM_SETRANGEREDRAW: 1373 case SBM_SETRANGE: 1374 { 1375 ScrollInfo.cbSize = sizeof(SCROLLINFO); 1376 ScrollInfo.fMask = SIF_RANGE|SIF_PREVIOUSPOS; 1377 ScrollInfo.nMin = wParam; 1378 ScrollInfo.nMax = lParam; 1379 return IntSetScrollInfo(Wnd, &ScrollInfo, Msg == SBM_SETRANGEREDRAW ? TRUE : FALSE); 1380 } 1381 1382 case SBM_SETSCROLLINFO: 1383 return IntSetScrollInfo(Wnd, (LPCSCROLLINFO)lParam, wParam); 1384 1385 case SBM_GETSCROLLINFO: 1386 { 1387 PSBDATA pSBData = (PSBDATA)&pSBWnd->SBCalc; 1388 DWORD ret = NtUserSBGetParms(Wnd, SB_CTL, pSBData, (SCROLLINFO *) lParam); 1389 if (!ret) 1390 { 1391 ERR("SBM_GETSCROLLINFO No ScrollInfo\n"); 1392 } 1393 return ret; 1394 } 1395 case SBM_GETSCROLLBARINFO: 1396 ((PSCROLLBARINFO)lParam)->cbSize = sizeof(SCROLLBARINFO); 1397 return NtUserGetScrollBarInfo(Wnd, OBJID_CLIENT, (PSCROLLBARINFO)lParam); 1398 1399 case 0x00e5: 1400 case 0x00e7: 1401 case 0x00e8: 1402 case 0x00ec: 1403 case 0x00ed: 1404 case 0x00ee: 1405 case 0x00ef: 1406 WARN("unknown Win32 msg %04x wp=%08lx lp=%08lx\n", 1407 Msg, wParam, lParam ); 1408 break; 1409 1410 default: 1411 if (WM_USER <= Msg) 1412 { 1413 WARN("unknown msg %04x wp=%04lx lp=%08lx\n", Msg, wParam, lParam); 1414 } 1415 if (unicode) 1416 return DefWindowProcW( Wnd, Msg, wParam, lParam ); 1417 else 1418 return DefWindowProcA( Wnd, Msg, wParam, lParam ); 1419 } 1420 1421 return 0; 1422 } 1423 1424 LRESULT WINAPI 1425 ScrollBarWndProcW(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) 1426 { 1427 return ScrollBarWndProc_common(DefWindowProcW, Wnd, Msg, wParam, lParam, TRUE); 1428 } 1429 1430 LRESULT WINAPI 1431 ScrollBarWndProcA(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) 1432 { 1433 return ScrollBarWndProc_common(DefWindowProcA, Wnd, Msg, wParam, lParam, FALSE); 1434 } 1435 1436 1437 /* PUBLIC FUNCTIONS ***********************************************************/ 1438 1439 /* 1440 * @implemented 1441 */ 1442 BOOL 1443 WINAPI 1444 DECLSPEC_HOTPATCH 1445 EnableScrollBar( HWND hwnd, UINT nBar, UINT flags ) 1446 { 1447 BOOL Hook, Ret = FALSE; 1448 1449 LoadUserApiHook(); 1450 1451 Hook = BeginIfHookedUserApiHook(); 1452 1453 /* Bypass SEH and go direct. */ 1454 if (!Hook) 1455 { 1456 Ret = NtUserEnableScrollBar(hwnd, nBar, flags); 1457 if (!Ret) return Ret; 1458 SCROLL_RefreshScrollBar( hwnd, nBar, TRUE, TRUE ); 1459 return Ret; 1460 } 1461 _SEH2_TRY 1462 { 1463 Ret = guah.EnableScrollBar(hwnd, nBar, flags); 1464 } 1465 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1466 { 1467 } 1468 _SEH2_END; 1469 1470 EndUserApiHook(); 1471 1472 return Ret; 1473 } 1474 1475 BOOL WINAPI 1476 RealGetScrollInfo(HWND Wnd, INT SBType, LPSCROLLINFO Info) 1477 { 1478 PWND pWnd; 1479 PSBDATA pSBData; 1480 1481 if (SB_CTL == SBType) 1482 { 1483 return SendMessageW(Wnd, SBM_GETSCROLLINFO, 0, (LPARAM) Info); 1484 } 1485 1486 pWnd = ValidateHwnd(Wnd); 1487 if (!pWnd) return FALSE; 1488 1489 if (SBType < SB_HORZ || SBType > SB_VERT) 1490 { 1491 SetLastError(ERROR_INVALID_PARAMETER); 1492 return FALSE; 1493 } 1494 if (!pWnd->pSBInfo) 1495 { 1496 SetLastError(ERROR_NO_SCROLLBARS); 1497 return FALSE; 1498 } 1499 pSBData = IntGetSBData(pWnd, SBType); 1500 return NtUserSBGetParms(Wnd, SBType, pSBData, Info); 1501 } 1502 1503 /* 1504 * @implemented 1505 */ 1506 BOOL WINAPI GetScrollBarInfo( _In_ HWND hwnd, _In_ LONG idObject, _Inout_ LPSCROLLBARINFO info) 1507 { 1508 BOOL Ret; 1509 PWND pWnd = ValidateHwnd(hwnd); 1510 TRACE("hwnd=%p idObject=%d info=%p\n", hwnd, idObject, info); 1511 if (!pWnd) return FALSE; 1512 Ret = NtUserGetScrollBarInfo(hwnd, idObject, info); // This will be fixed once SB is server side. 1513 /* rcScrollBar needs to be in screen coordinates */ 1514 OffsetRect( &(info->rcScrollBar), pWnd->rcWindow.left, pWnd->rcWindow.top ); 1515 return Ret; 1516 } 1517 1518 /* 1519 * @implemented 1520 */ 1521 BOOL 1522 WINAPI 1523 DECLSPEC_HOTPATCH 1524 GetScrollInfo(HWND Wnd, INT SBType, LPSCROLLINFO Info) 1525 { 1526 BOOL Hook, Ret = FALSE; 1527 1528 LoadUserApiHook(); 1529 1530 Hook = BeginIfHookedUserApiHook(); 1531 1532 /* Bypass SEH and go direct. */ 1533 if (!Hook) return RealGetScrollInfo(Wnd, SBType, Info); 1534 1535 _SEH2_TRY 1536 { 1537 Ret = guah.GetScrollInfo(Wnd, SBType, Info); 1538 } 1539 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1540 { 1541 } 1542 _SEH2_END; 1543 1544 EndUserApiHook(); 1545 1546 return Ret; 1547 } 1548 1549 /* 1550 * @implemented 1551 */ 1552 INT 1553 WINAPI 1554 DECLSPEC_HOTPATCH 1555 GetScrollPos(HWND Wnd, INT Bar) 1556 { 1557 PWND pwnd; 1558 PSBDATA pSBData; 1559 1560 TRACE("Wnd=%p Bar=%d\n", Wnd, Bar); 1561 1562 /* Refer SB_CTL requests to the window */ 1563 if (SB_CTL == Bar) 1564 { 1565 return SendMessageW(Wnd, SBM_GETPOS, (WPARAM) 0, (LPARAM) 0); 1566 } 1567 else if (Bar == SB_HORZ || Bar == SB_VERT ) 1568 { 1569 pwnd = ValidateHwnd(Wnd); 1570 if (!pwnd) return 0; 1571 1572 if (pwnd->pSBInfo) 1573 { 1574 pSBData = IntGetSBData(pwnd, Bar); 1575 return pSBData->pos; 1576 } 1577 1578 SetLastError(ERROR_NO_SCROLLBARS); 1579 TRACE("GetScrollPos No Scroll Info\n"); 1580 return 0; 1581 } 1582 SetLastError(ERROR_INVALID_PARAMETER); 1583 return 0; 1584 } 1585 1586 /* 1587 * @implemented 1588 */ 1589 BOOL 1590 WINAPI 1591 DECLSPEC_HOTPATCH 1592 GetScrollRange(HWND Wnd, int Bar, LPINT MinPos, LPINT MaxPos) 1593 { 1594 PWND pwnd; 1595 PSBDATA pSBData; 1596 1597 TRACE("Wnd=%x Bar=%d Min=%p Max=%p\n", Wnd, Bar, MinPos, MaxPos); 1598 1599 /* Refer SB_CTL requests to the window */ 1600 if (SB_CTL == Bar) 1601 { 1602 return SendMessageW(Wnd, SBM_GETRANGE, (WPARAM) MinPos, (LPARAM) MaxPos); 1603 } 1604 else if (Bar == SB_HORZ || Bar == SB_VERT ) 1605 { 1606 pwnd = ValidateHwnd(Wnd); 1607 if (!pwnd) return FALSE; 1608 1609 if (pwnd->pSBInfo) 1610 { 1611 pSBData = IntGetSBData(pwnd, Bar); 1612 *MinPos = pSBData->posMin; 1613 *MaxPos = pSBData->posMax; 1614 } 1615 else 1616 { 1617 SetLastError(ERROR_NO_SCROLLBARS); 1618 *MinPos = 0; 1619 *MaxPos = 0; 1620 } 1621 return TRUE; 1622 } 1623 SetLastError(ERROR_INVALID_PARAMETER); 1624 return FALSE; 1625 } 1626 1627 INT WINAPI 1628 RealSetScrollInfo(HWND Wnd, int SBType, LPCSCROLLINFO Info, BOOL bRedraw) 1629 { 1630 if (SB_CTL == SBType) 1631 { 1632 return SendMessageW(Wnd, SBM_SETSCROLLINFO, (WPARAM) bRedraw, (LPARAM) Info); 1633 } 1634 else 1635 { 1636 return NtUserSetScrollInfo(Wnd, SBType, Info, bRedraw); 1637 } 1638 } 1639 1640 /* 1641 * @implemented 1642 */ 1643 INT 1644 WINAPI 1645 DECLSPEC_HOTPATCH 1646 SetScrollInfo(HWND Wnd, int SBType, LPCSCROLLINFO Info, BOOL bRedraw) 1647 { 1648 BOOL Hook; 1649 INT Ret = 0; 1650 1651 LoadUserApiHook(); 1652 1653 Hook = BeginIfHookedUserApiHook(); 1654 1655 /* Bypass SEH and go direct. */ 1656 if (!Hook) return RealSetScrollInfo(Wnd, SBType, Info, bRedraw); 1657 1658 _SEH2_TRY 1659 { 1660 Ret = guah.SetScrollInfo(Wnd, SBType, Info, bRedraw); 1661 } 1662 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1663 { 1664 } 1665 _SEH2_END; 1666 1667 EndUserApiHook(); 1668 1669 return Ret; 1670 1671 } 1672 1673 /* 1674 * @implemented 1675 */ 1676 INT 1677 WINAPI 1678 DECLSPEC_HOTPATCH 1679 SetScrollPos(HWND hWnd, INT nBar, INT nPos, BOOL bRedraw) 1680 { 1681 SCROLLINFO ScrollInfo; 1682 1683 ScrollInfo.cbSize = sizeof(SCROLLINFO); 1684 ScrollInfo.fMask = SIF_POS|SIF_PREVIOUSPOS; 1685 ScrollInfo.nPos = nPos; 1686 1687 return RealSetScrollInfo(hWnd, nBar, &ScrollInfo, bRedraw); 1688 } 1689 1690 /* 1691 * @implemented 1692 */ 1693 BOOL 1694 WINAPI 1695 DECLSPEC_HOTPATCH 1696 SetScrollRange(HWND hWnd, INT nBar, INT nMinPos, INT nMaxPos, BOOL bRedraw) 1697 { 1698 PWND pWnd; 1699 SCROLLINFO ScrollInfo; 1700 1701 pWnd = ValidateHwnd(hWnd); 1702 if ( !pWnd ) return FALSE; 1703 1704 if (((LONGLONG)nMaxPos - nMinPos) > MAXLONG) 1705 { 1706 SetLastError(ERROR_INVALID_SCROLLBAR_RANGE); 1707 return FALSE; 1708 } 1709 1710 ScrollInfo.cbSize = sizeof(SCROLLINFO); 1711 ScrollInfo.fMask = SIF_RANGE; 1712 ScrollInfo.nMin = nMinPos; 1713 ScrollInfo.nMax = nMaxPos; 1714 SetScrollInfo(hWnd, nBar, &ScrollInfo, bRedraw); // do not bypass themes. 1715 return TRUE; 1716 } 1717