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