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