reactos/win32ss/user/user32/controls/scrollbar.c
2022-02-23 20:32:02 +01:00

1756 lines
49 KiB
C

/*
* ReactOS User32 Library
* - ScrollBar control
*
* Copyright 2001 Casper S. Hornstrup
* Copyright 2003 Thomas Weidenmueller
* Copyright 2003 Filip Navara
*
* Based on Wine code.
*
* Copyright 1993 Martin Ayotte
* Copyright 1994, 1996 Alexandre Julliard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <user32.h>
WINE_DEFAULT_DEBUG_CHANNEL(scrollbar);
/* Definitions for scrollbar hit testing [See SCROLLBARINFO in MSDN] */
#define SCROLL_NOWHERE 0x00 /* Outside the scroll bar */
#define SCROLL_TOP_ARROW 0x01 /* Top or left arrow */
#define SCROLL_TOP_RECT 0x02 /* Rectangle between the top arrow and the thumb */
#define SCROLL_THUMB 0x03 /* Thumb rectangle */
#define SCROLL_BOTTOM_RECT 0x04 /* Rectangle between the thumb and the bottom arrow */
#define SCROLL_BOTTOM_ARROW 0x05 /* Bottom or right arrow */
#define SCROLL_FIRST_DELAY 200 /* Delay (in ms) before first repetition when
holding the button down */
#define SCROLL_REPEAT_DELAY 50 /* Delay (in ms) between scroll repetitions */
#define SCROLL_TIMER 0 /* Scroll timer id */
/* Minimum size of the rectangle between the arrows */
#define SCROLL_MIN_RECT 4
/* Minimum size of the thumb in pixels */
#define SCROLL_MIN_THUMB 6
/* Overlap between arrows and thumb */
#define SCROLL_ARROW_THUMB_OVERLAP 0
/* Thumb-tracking info */
static HWND ScrollTrackingWin = 0;
static INT ScrollTrackingBar = 0;
static INT ScrollTrackingPos = 0;
static INT ScrollTrackingVal = 0;
/* Hit test code of the last button-down event */
static DWORD ScrollTrackHitTest = SCROLL_NOWHERE;
static BOOL ScrollTrackVertical;
/* Is the moving thumb being displayed? */
static BOOL ScrollMovingThumb = FALSE;
HBRUSH DefWndControlColor(HDC hDC, UINT ctlType);
UINT_PTR WINAPI SetSystemTimer(HWND,UINT_PTR,UINT,TIMERPROC);
BOOL WINAPI KillSystemTimer(HWND,UINT_PTR);
/*********************************************************************
* scrollbar class descriptor
*/
const struct builtin_class_descr SCROLL_builtin_class =
{
L"ScrollBar", /* name */
CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC, /* style */
ScrollBarWndProcA, /* procA */
ScrollBarWndProcW, /* procW */
sizeof(SBWND)-sizeof(WND), /* extra */
IDC_ARROW, /* cursor */
0 /* brush */
};
/* PRIVATE FUNCTIONS **********************************************************/
static PSBDATA
IntGetSBData(PWND pwnd, INT Bar)
{
PSBWND pSBWnd;
PSBINFO pSBInfo;
pSBInfo = DesktopPtrToUser(pwnd->pSBInfo);
switch (Bar)
{
case SB_HORZ:
return &pSBInfo->Horz;
case SB_VERT:
return &pSBInfo->Vert;
case SB_CTL:
if ( pwnd->cbwndExtra < (sizeof(SBWND)-sizeof(WND)) )
{
ERR("IntGetSBData Wrong Extra bytes for CTL Scrollbar!\n");
return 0;
}
pSBWnd = (PSBWND)pwnd;
return (PSBDATA)&pSBWnd->SBCalc;
default:
ERR("IntGetSBData Bad Bar!\n");
}
return NULL;
}
static void
IntDrawScrollInterior(HWND hWnd, HDC hDC, INT nBar, BOOL Vertical,
PSCROLLBARINFO ScrollBarInfo)
{
INT ThumbSize = ScrollBarInfo->xyThumbBottom - ScrollBarInfo->xyThumbTop;
INT ThumbTop = ScrollBarInfo->xyThumbTop;
RECT Rect;
HBRUSH hSaveBrush, hBrush;
BOOL TopSelected = FALSE, BottomSelected = FALSE;
if (ScrollBarInfo->rgstate[SCROLL_TOP_RECT] & STATE_SYSTEM_PRESSED)
TopSelected = TRUE;
if (ScrollBarInfo->rgstate[SCROLL_BOTTOM_RECT] & STATE_SYSTEM_PRESSED)
BottomSelected = TRUE;
/*
* Only scrollbar controls send WM_CTLCOLORSCROLLBAR.
* The window-owned scrollbars need to call DefWndControlColor
* to correctly setup default scrollbar colors
*/
if (nBar == SB_CTL)
{
hBrush = GetControlBrush( hWnd, hDC, WM_CTLCOLORSCROLLBAR);
if (!hBrush)
hBrush = GetSysColorBrush(COLOR_SCROLLBAR);
}
else
{
hBrush = DefWndControlColor(hDC, CTLCOLOR_SCROLLBAR);
}
hSaveBrush = SelectObject(hDC, hBrush);
/* Calculate the scroll rectangle */
if (Vertical)
{
Rect.top = ScrollBarInfo->rcScrollBar.top + ScrollBarInfo->dxyLineButton;
Rect.bottom = ScrollBarInfo->rcScrollBar.bottom - ScrollBarInfo->dxyLineButton;
Rect.left = ScrollBarInfo->rcScrollBar.left;
Rect.right = ScrollBarInfo->rcScrollBar.right;
}
else
{
Rect.top = ScrollBarInfo->rcScrollBar.top;
Rect.bottom = ScrollBarInfo->rcScrollBar.bottom;
Rect.left = ScrollBarInfo->rcScrollBar.left + ScrollBarInfo->dxyLineButton;
Rect.right = ScrollBarInfo->rcScrollBar.right - ScrollBarInfo->dxyLineButton;
}
/* Draw the scroll rectangles and thumb */
if (!ScrollBarInfo->xyThumbBottom)
{
PatBlt(hDC, Rect.left, Rect.top, Rect.right - Rect.left,
Rect.bottom - Rect.top, PATCOPY);
/* Cleanup and return */
SelectObject(hDC, hSaveBrush);
return;
}
ThumbTop -= ScrollBarInfo->dxyLineButton;
if (ScrollBarInfo->dxyLineButton)
{
if (Vertical)
{
if (ThumbSize)
{
PatBlt(hDC, Rect.left, Rect.top, Rect.right - Rect.left,
ThumbTop, TopSelected ? BLACKNESS : PATCOPY);
Rect.top += ThumbTop;
PatBlt(hDC, Rect.left, Rect.top + ThumbSize, Rect.right - Rect.left,
Rect.bottom - Rect.top - ThumbSize, BottomSelected ? BLACKNESS : PATCOPY);
Rect.bottom = Rect.top + ThumbSize;
}
else
{
if (ThumbTop)
{
PatBlt(hDC, Rect.left, ScrollBarInfo->dxyLineButton,
Rect.right - Rect.left, Rect.bottom - Rect.top, PATCOPY);
}
}
}
else
{
if (ThumbSize)
{
PatBlt(hDC, Rect.left, Rect.top, ThumbTop,
Rect.bottom - Rect.top, TopSelected ? BLACKNESS : PATCOPY);
Rect.left += ThumbTop;
PatBlt(hDC, Rect.left + ThumbSize, Rect.top,
Rect.right - Rect.left - ThumbSize, Rect.bottom - Rect.top,
BottomSelected ? BLACKNESS : PATCOPY);
Rect.right = Rect.left + ThumbSize;
}
else
{
if (ThumbTop)
{
PatBlt(hDC, ScrollBarInfo->dxyLineButton, Rect.top,
Rect.right - Rect.left, Rect.bottom - Rect.top, PATCOPY);
}
}
}
}
/* Draw the thumb */
if (ThumbSize)
DrawEdge(hDC, &Rect, EDGE_RAISED, BF_RECT | BF_MIDDLE);
/* Cleanup */
SelectObject(hDC, hSaveBrush);
}
static VOID FASTCALL
IntDrawScrollArrows(HDC hDC, PSCROLLBARINFO ScrollBarInfo, BOOL Vertical)
{
RECT RectLT, RectRB;
INT ScrollDirFlagLT, ScrollDirFlagRB;
RectLT = RectRB = ScrollBarInfo->rcScrollBar;
if (Vertical)
{
ScrollDirFlagLT = DFCS_SCROLLUP;
ScrollDirFlagRB = DFCS_SCROLLDOWN;
RectLT.bottom = RectLT.top + ScrollBarInfo->dxyLineButton;
RectRB.top = RectRB.bottom - ScrollBarInfo->dxyLineButton;
}
else
{
ScrollDirFlagLT = DFCS_SCROLLLEFT;
ScrollDirFlagRB = DFCS_SCROLLRIGHT;
RectLT.right = RectLT.left + ScrollBarInfo->dxyLineButton;
RectRB.left = RectRB.right - ScrollBarInfo->dxyLineButton;
}
if (ScrollBarInfo->rgstate[SCROLL_TOP_ARROW] & STATE_SYSTEM_PRESSED)
{
ScrollDirFlagLT |= DFCS_PUSHED | DFCS_FLAT;
}
if (ScrollBarInfo->rgstate[SCROLL_TOP_ARROW] & STATE_SYSTEM_UNAVAILABLE)
{
ScrollDirFlagLT |= DFCS_INACTIVE;
}
if (ScrollBarInfo->rgstate[SCROLL_BOTTOM_ARROW] & STATE_SYSTEM_PRESSED)
{
ScrollDirFlagRB |= DFCS_PUSHED | DFCS_FLAT;
}
if (ScrollBarInfo->rgstate[SCROLL_BOTTOM_ARROW] & STATE_SYSTEM_UNAVAILABLE)
{
ScrollDirFlagRB |= DFCS_INACTIVE;
}
DrawFrameControl(hDC, &RectLT, DFC_SCROLL, ScrollDirFlagLT);
DrawFrameControl(hDC, &RectRB, DFC_SCROLL, ScrollDirFlagRB);
}
static VOID FASTCALL
IntScrollDrawMovingThumb(HDC Dc, PSCROLLBARINFO ScrollBarInfo, BOOL Vertical)
{
INT Pos = ScrollTrackingPos;
INT MaxSize;
INT OldTop;
if (Vertical)
MaxSize = ScrollBarInfo->rcScrollBar.bottom - ScrollBarInfo->rcScrollBar.top;
else
MaxSize = ScrollBarInfo->rcScrollBar.right - ScrollBarInfo->rcScrollBar.left;
MaxSize -= ScrollBarInfo->dxyLineButton + ScrollBarInfo->xyThumbBottom - ScrollBarInfo->xyThumbTop;
if (Pos < ScrollBarInfo->dxyLineButton)
Pos = ScrollBarInfo->dxyLineButton;
else if (MaxSize < Pos)
Pos = MaxSize;
OldTop = ScrollBarInfo->xyThumbTop;
ScrollBarInfo->xyThumbBottom = Pos + ScrollBarInfo->xyThumbBottom - ScrollBarInfo->xyThumbTop;
ScrollBarInfo->xyThumbTop = Pos;
IntDrawScrollInterior(ScrollTrackingWin, Dc, ScrollTrackingBar, Vertical, ScrollBarInfo);
ScrollBarInfo->xyThumbBottom = OldTop + ScrollBarInfo->xyThumbBottom - ScrollBarInfo->xyThumbTop;
ScrollBarInfo->xyThumbTop = OldTop;
ScrollMovingThumb = !ScrollMovingThumb;
}
static LONG FASTCALL
IntScrollGetObjectId(INT SBType)
{
if (SBType == SB_VERT)
return OBJID_VSCROLL;
if (SBType == SB_HORZ)
return OBJID_HSCROLL;
return OBJID_CLIENT;
}
static BOOL FASTCALL
IntGetScrollBarInfo(HWND Wnd, INT Bar, PSCROLLBARINFO ScrollBarInfo)
{
ScrollBarInfo->cbSize = sizeof(SCROLLBARINFO);
return NtUserGetScrollBarInfo(Wnd, IntScrollGetObjectId(Bar), ScrollBarInfo);
}
static VOID FASTCALL
IntUpdateScrollArrows(HWND Wnd, HDC hDC, PSCROLLBARINFO ScrollBarInfo,
SETSCROLLBARINFO *info, INT SBType, INT Arrow,
BOOL Vertical, BOOL Pressed)
{
if (Pressed)
{
ScrollBarInfo->rgstate[Arrow] |= STATE_SYSTEM_PRESSED;
}
else
{
ScrollBarInfo->rgstate[Arrow] &= ~STATE_SYSTEM_PRESSED;
}
/* Update arrow state */
info->rgstate[Arrow] = ScrollBarInfo->rgstate[Arrow];
NtUserSetScrollBarInfo(Wnd, IntScrollGetObjectId(SBType), info);
IntDrawScrollArrows(hDC, ScrollBarInfo, Vertical);
}
void
IntDrawScrollBar(HWND Wnd, HDC DC, INT Bar)
{
//PSBWND pSBWnd;
//INT ThumbSize;
SCROLLBARINFO Info;
BOOL Vertical;
/*
* Get scroll bar info.
*/
switch (Bar)
{
case SB_HORZ:
Vertical = FALSE;
break;
case SB_VERT:
Vertical = TRUE;
break;
case SB_CTL:
Vertical = (GetWindowLongPtrW(Wnd, GWL_STYLE) & SBS_VERT) != 0;
break;
default:
return;
}
if (!IntGetScrollBarInfo(Wnd, Bar, &Info))
{
return;
}
if (IsRectEmpty(&Info.rcScrollBar))
{
return;
}
//ThumbSize = pSBWnd->pSBCalc->pxThumbBottom - pSBWnd->pSBCalc->pxThumbTop;
/*
* Draw the arrows.
*/
if (Info.dxyLineButton)
{
IntDrawScrollArrows(DC, &Info, Vertical);
}
/*
* Draw the interior.
*/
IntDrawScrollInterior(Wnd, DC, Bar, Vertical, &Info);
/*
* If scroll bar has focus, reposition the caret.
*/
if (Wnd == GetFocus() && SB_CTL == Bar)
{
if (Vertical)
{
SetCaretPos(Info.rcScrollBar.top + 1, Info.dxyLineButton + 1);
}
else
{
SetCaretPos(Info.dxyLineButton + 1, Info.rcScrollBar.top + 1);
}
}
}
static BOOL FASTCALL
IntScrollPtInRectEx(LPRECT Rect, POINT Pt, BOOL Vertical)
{
RECT TempRect = *Rect;
int scrollbarWidth;
/* Pad hit rect to allow mouse to be dragged outside of scrollbar and
* still be considered in the scrollbar. */
if (Vertical)
{
scrollbarWidth = Rect->right - Rect->left;
TempRect.left -= scrollbarWidth*8;
TempRect.right += scrollbarWidth*8;
TempRect.top -= scrollbarWidth*2;
TempRect.bottom += scrollbarWidth*2;
}
else
{
scrollbarWidth = Rect->bottom - Rect->top;
TempRect.left -= scrollbarWidth*2;
TempRect.right += scrollbarWidth*2;
TempRect.top -= scrollbarWidth*8;
TempRect.bottom += scrollbarWidth*8;
}
return PtInRect(&TempRect, Pt);
}
static DWORD FASTCALL
IntScrollHitTest(PSCROLLBARINFO ScrollBarInfo, BOOL Vertical, POINT Pt, BOOL Dragging)
{
INT ArrowSize, ThumbSize, ThumbPos;
if ((Dragging && ! IntScrollPtInRectEx(&ScrollBarInfo->rcScrollBar, Pt, Vertical)) ||
! PtInRect(&ScrollBarInfo->rcScrollBar, Pt)) return SCROLL_NOWHERE;
ThumbPos = ScrollBarInfo->xyThumbTop;
ThumbSize = ScrollBarInfo->xyThumbBottom - ThumbPos;
ArrowSize = ScrollBarInfo->dxyLineButton;
if (Vertical)
{
if (Pt.y < ScrollBarInfo->rcScrollBar.top + ArrowSize) return SCROLL_TOP_ARROW;
if (Pt.y >= ScrollBarInfo->rcScrollBar.bottom - ArrowSize) return SCROLL_BOTTOM_ARROW;
if (!ThumbPos) return SCROLL_TOP_RECT;
Pt.y -= ScrollBarInfo->rcScrollBar.top;
if (Pt.y < ThumbPos) return SCROLL_TOP_RECT;
if (Pt.y >= ThumbPos + ThumbSize) return SCROLL_BOTTOM_RECT;
}
else
{
if (Pt.x < ScrollBarInfo->rcScrollBar.left + ArrowSize) return SCROLL_TOP_ARROW;
if (Pt.x >= ScrollBarInfo->rcScrollBar.right - ArrowSize) return SCROLL_BOTTOM_ARROW;
if (!ThumbPos) return SCROLL_TOP_RECT;
Pt.x -= ScrollBarInfo->rcScrollBar.left;
if (Pt.x < ThumbPos) return SCROLL_TOP_RECT;
if (Pt.x >= ThumbPos + ThumbSize) return SCROLL_BOTTOM_RECT;
}
return SCROLL_THUMB;
}
/***********************************************************************
* IntScrollGetScrollBarRect
*
* Compute the scroll bar rectangle, in drawing coordinates (i.e. client
* coords for SB_CTL, window coords for SB_VERT and SB_HORZ).
* 'arrowSize' returns the width or height of an arrow (depending on
* the orientation of the scrollbar), 'thumbSize' returns the size of
* the thumb, and 'thumbPos' returns the position of the thumb
* relative to the left or to the top.
* Return TRUE if the scrollbar is vertical, FALSE if horizontal.
*/
static BOOL FASTCALL
IntScrollGetScrollBarRect(HWND Wnd, INT Bar, RECT *Rect,
INT *ArrowSize, INT *ThumbSize,
INT *ThumbPos)
{
INT Pixels;
BOOL Vertical;
PWND pWnd;
PSBINFO pSBInfo;
PSBDATA pSBData;
PSBWND pSBWnd;
pWnd = ValidateHwnd( Wnd );
if (!pWnd) return FALSE;
pSBInfo = DesktopPtrToUser(pWnd->pSBInfo);
*Rect = pWnd->rcClient;
OffsetRect( Rect, -pWnd->rcWindow.left, -pWnd->rcWindow.top );
if (pWnd->ExStyle & WS_EX_LAYOUTRTL)
mirror_rect( &pWnd->rcWindow, Rect );
switch (Bar)
{
case SB_HORZ:
// WIN_GetRectangles( Wnd, COORDS_WINDOW, NULL, Rect );
Rect->top = Rect->bottom;
Rect->bottom += GetSystemMetrics(SM_CYHSCROLL);
if (pWnd->style & WS_BORDER)
{
Rect->left--;
Rect->right++;
}
else if (pWnd->style & WS_VSCROLL)
{
Rect->right++;
}
Vertical = FALSE;
pSBData = &pSBInfo->Horz;
break;
case SB_VERT:
// WIN_GetRectangles( Wnd, COORDS_WINDOW, NULL, Rect );
if (pWnd->ExStyle & WS_EX_LEFTSCROLLBAR)
{
Rect->right = Rect->left;
Rect->left -= GetSystemMetrics(SM_CXVSCROLL);
}
else
{
Rect->left = Rect->right;
Rect->right += GetSystemMetrics(SM_CXVSCROLL);
}
if (pWnd->style & WS_BORDER)
{
Rect->top--;
Rect->bottom++;
}
else if (pWnd->style & WS_HSCROLL)
{
Rect->bottom++;
}
Vertical = TRUE;
pSBData = &pSBInfo->Vert;
break;
case SB_CTL:
GetClientRect( Wnd, Rect );
Vertical = (pWnd->style & SBS_VERT);
pSBWnd = (PSBWND)pWnd;
pSBData = (PSBDATA)&pSBWnd->SBCalc;
break;
default:
return FALSE;
}
if (Vertical) Pixels = Rect->bottom - Rect->top;
else Pixels = Rect->right - Rect->left;
if (Pixels <= 2 * GetSystemMetrics(SM_CXVSCROLL) + SCROLL_MIN_RECT)
{
if (SCROLL_MIN_RECT < Pixels)
*ArrowSize = (Pixels - SCROLL_MIN_RECT) / 2;
else
*ArrowSize = 0;
*ThumbPos = *ThumbSize = 0;
}
else
{
*ArrowSize = GetSystemMetrics(SM_CXVSCROLL);
Pixels -= (2 * (GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP));
if (pSBData->page)
{
*ThumbSize = MulDiv(Pixels, pSBData->page, (pSBData->posMax - pSBData->posMin + 1));
if (*ThumbSize < SCROLL_MIN_THUMB) *ThumbSize = SCROLL_MIN_THUMB;
}
else *ThumbSize = GetSystemMetrics(SM_CXVSCROLL);
if (((Pixels -= *ThumbSize ) < 0) ||
(( pSBInfo->WSBflags & ESB_DISABLE_BOTH) == ESB_DISABLE_BOTH))
{
/* Rectangle too small or scrollbar disabled -> no thumb */
*ThumbPos = *ThumbSize = 0;
}
else
{
INT Max = pSBData->posMax - max(pSBData->page - 1, 0);
if (pSBData->posMin >= Max)
*ThumbPos = *ArrowSize - SCROLL_ARROW_THUMB_OVERLAP;
else
*ThumbPos = *ArrowSize - SCROLL_ARROW_THUMB_OVERLAP
+ MulDiv(Pixels, (pSBData->pos - pSBData->posMin),(Max - pSBData->posMin));
}
}
return Vertical;
}
/***********************************************************************
* IntScrollGetThumbVal
*
* Compute the current scroll position based on the thumb position in pixels
* from the top of the scroll-bar.
*/
static UINT FASTCALL
IntScrollGetThumbVal(HWND Wnd, INT SBType, PSCROLLBARINFO ScrollBarInfo,
BOOL Vertical, INT Pos)
{
PWND pWnd;
PSBDATA pSBData;
INT Pixels = Vertical ? ScrollBarInfo->rcScrollBar.bottom
- ScrollBarInfo->rcScrollBar.top
: ScrollBarInfo->rcScrollBar.right
- ScrollBarInfo->rcScrollBar.left;
pWnd = ValidateHwnd( Wnd );
if (!pWnd) return FALSE;
pSBData = IntGetSBData(pWnd, SBType);
if ((Pixels -= 2 * ScrollBarInfo->dxyLineButton) <= 0)
{
return pSBData->posMin;
}
if ((Pixels -= (ScrollBarInfo->xyThumbBottom - ScrollBarInfo->xyThumbTop)) <= 0)
{
return pSBData->posMin;
}
Pos = Pos - ScrollBarInfo->dxyLineButton;
if (Pos < 0)
{
Pos = 0;
}
if (Pos > Pixels) Pos = Pixels;
if (!pSBData->page)
Pos *= pSBData->posMax - pSBData->posMin;
else
Pos *= pSBData->posMax - pSBData->posMin - pSBData->page + 1;
return pSBData->posMin + ((Pos + Pixels / 2) / Pixels);
}
/***********************************************************************
* IntScrollClipPos
*/
static POINT IntScrollClipPos(PRECT lpRect, POINT pt)
{
if( pt.x < lpRect->left )
pt.x = lpRect->left;
else
if( pt.x > lpRect->right )
pt.x = lpRect->right;
if( pt.y < lpRect->top )
pt.y = lpRect->top;
else
if( pt.y > lpRect->bottom )
pt.y = lpRect->bottom;
return pt;
}
/***********************************************************************
* IntScrollDrawSizeGrip
*
* Draw the size grip.
*/
static void FASTCALL
IntScrollDrawSizeGrip(HWND Wnd, HDC Dc)
{
RECT Rect;
GetClientRect(Wnd, &Rect);
FillRect(Dc, &Rect, GetSysColorBrush(COLOR_SCROLLBAR));
Rect.left = max(Rect.left, Rect.right - GetSystemMetrics(SM_CXVSCROLL) - 1);
Rect.top = max(Rect.top, Rect.bottom - GetSystemMetrics(SM_CYHSCROLL) - 1);
DrawFrameControl(Dc, &Rect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
}
/***********************************************************************
* SCROLL_RefreshScrollBar
*
* Repaint the scroll bar interior after a SetScrollRange() or
* SetScrollPos() call.
*/
static void SCROLL_RefreshScrollBar( HWND hwnd, INT nBar,
BOOL arrows, BOOL interior )
{
HDC hdc = GetDCEx( hwnd, 0,
DCX_CACHE | ((nBar == SB_CTL) ? 0 : DCX_WINDOW) );
if (!hdc) return;
IntDrawScrollBar( hwnd, hdc, nBar);//, arrows, interior );
ReleaseDC( hwnd, hdc );
}
/***********************************************************************
* IntScrollHandleKbdEvent
*
* Handle a keyboard event (only for SB_CTL scrollbars with focus).
*/
static void FASTCALL
IntScrollHandleKbdEvent(
HWND Wnd /* [in] Handle of window with scrollbar(s) */,
WPARAM wParam /* [in] Variable input including enable state */,
LPARAM lParam /* [in] Variable input including input point */)
{
TRACE("Wnd=%p wParam=%ld lParam=%ld\n", Wnd, wParam, lParam);
/* hide caret on first KEYDOWN to prevent flicker */
if (0 == (lParam & PFD_DOUBLEBUFFER_DONTCARE))
{
HideCaret(Wnd);
}
switch(wParam)
{
case VK_PRIOR:
wParam = SB_PAGEUP;
break;
case VK_NEXT:
wParam = SB_PAGEDOWN;
break;
case VK_HOME:
wParam = SB_TOP;
break;
case VK_END:
wParam = SB_BOTTOM;
break;
case VK_UP:
wParam = SB_LINEUP;
break;
case VK_DOWN:
wParam = SB_LINEDOWN;
break;
default:
return;
}
SendMessageW(GetParent(Wnd),
(0 != (GetWindowLongPtrW(Wnd, GWL_STYLE ) & SBS_VERT) ?
WM_VSCROLL : WM_HSCROLL), wParam, (LPARAM) Wnd);
}
/***********************************************************************
* IntScrollHandleScrollEvent
*
* Handle a mouse or timer event for the scrollbar.
* 'Pt' is the location of the mouse event in drawing coordinates
*/
static VOID FASTCALL
IntScrollHandleScrollEvent(HWND Wnd, INT SBType, UINT Msg, POINT Pt)
{
static POINT PrevPt; /* Previous mouse position for timer events */
static UINT TrackThumbPos; /* Thumb position when tracking started. */
static INT LastClickPos; /* Position in the scroll-bar of the last
button-down event. */
static INT LastMousePos; /* Position in the scroll-bar of the last
mouse event. */
DWORD HitTest;
HWND WndOwner, WndCtl;
BOOL Vertical;
HDC Dc;
SCROLLBARINFO ScrollBarInfo;
SETSCROLLBARINFO NewInfo;
if (! IntGetScrollBarInfo(Wnd, SBType, &ScrollBarInfo))
{
return;
}
if ((ScrollTrackHitTest == SCROLL_NOWHERE) && (Msg != WM_LBUTTONDOWN))
{
//// ReactOS : Justin Case something goes wrong.
if (Wnd == GetCapture())
{
ReleaseCapture();
}
////
return;
}
NewInfo.nTrackPos = ScrollTrackingVal;
NewInfo.reserved = ScrollBarInfo.reserved;
memcpy(NewInfo.rgstate, ScrollBarInfo.rgstate, (CCHILDREN_SCROLLBAR + 1) * sizeof(DWORD));
if (SBType == SB_CTL && (GetWindowLongPtrW(Wnd, GWL_STYLE) & (SBS_SIZEGRIP | SBS_SIZEBOX)))
{
switch(Msg)
{
case WM_LBUTTONDOWN: /* Initialise mouse tracking */
HideCaret(Wnd); /* hide caret while holding down LBUTTON */
SetCapture(Wnd);
PrevPt = Pt;
ScrollTrackHitTest = HitTest = SCROLL_THUMB;
break;
case WM_MOUSEMOVE:
GetClientRect(GetParent(GetParent(Wnd)), &ScrollBarInfo.rcScrollBar);
PrevPt = Pt;
break;
case WM_LBUTTONUP:
ReleaseCapture();
ScrollTrackHitTest = HitTest = SCROLL_NOWHERE;
if (Wnd == GetFocus()) ShowCaret(Wnd);
break;
case WM_SYSTIMER:
Pt = PrevPt;
break;
}
return;
}
Dc = GetDCEx(Wnd, 0, DCX_CACHE | ((SB_CTL == SBType) ? 0 : DCX_WINDOW));
if (SB_VERT == SBType)
{
Vertical = TRUE;
}
else if (SB_HORZ == SBType)
{
Vertical = FALSE;
}
else
{
Vertical = (0 != (GetWindowLongPtrW(Wnd, GWL_STYLE) & SBS_VERT));
}
WndOwner = (SB_CTL == SBType) ? GetParent(Wnd) : Wnd;
WndCtl = (SB_CTL == SBType) ? Wnd : NULL;
switch (Msg)
{
case WM_LBUTTONDOWN: /* Initialise mouse tracking */
HideCaret(Wnd); /* hide caret while holding down LBUTTON */
ScrollTrackVertical = Vertical;
ScrollTrackHitTest = HitTest = IntScrollHitTest(&ScrollBarInfo, Vertical, Pt, FALSE );
LastClickPos = Vertical ? (Pt.y - ScrollBarInfo.rcScrollBar.top)
: (Pt.x - ScrollBarInfo.rcScrollBar.left);
LastMousePos = LastClickPos;
TrackThumbPos = ScrollBarInfo.xyThumbTop;
PrevPt = Pt;
if (SBType == SB_CTL && (GetWindowLongPtrW(Wnd, GWL_STYLE) & WS_TABSTOP)) SetFocus(Wnd);
SetCapture(Wnd);
/* Don't update scrollbar if disabled. */
if (ScrollBarInfo.rgstate[ScrollTrackHitTest] != STATE_SYSTEM_UNAVAILABLE)
{
IntUpdateScrollArrows (Wnd, Dc, &ScrollBarInfo, &NewInfo, SBType, ScrollTrackHitTest, Vertical, TRUE);
}
break;
case WM_MOUSEMOVE:
HitTest = IntScrollHitTest(&ScrollBarInfo, Vertical, Pt, TRUE);
PrevPt = Pt;
break;
case WM_LBUTTONUP:
HitTest = SCROLL_NOWHERE;
ReleaseCapture();
/* if scrollbar has focus, show back caret */
if (Wnd == GetFocus()) ShowCaret(Wnd);
/* Don't update scrollbar if disabled. */
if (ScrollBarInfo.rgstate[ScrollTrackHitTest] != STATE_SYSTEM_UNAVAILABLE)
{
IntUpdateScrollArrows (Wnd, Dc, &ScrollBarInfo, &NewInfo, SBType, ScrollTrackHitTest, Vertical, FALSE);
IntDrawScrollInterior(Wnd,Dc,SBType,Vertical,&ScrollBarInfo);
}
break;
case WM_SYSTIMER:
Pt = PrevPt;
HitTest = IntScrollHitTest(&ScrollBarInfo, Vertical, Pt, FALSE);
break;
default:
return; /* Should never happen */
}
TRACE("Event: hwnd=%p bar=%d msg=%s pt=%d,%d hit=%d\n",
Wnd, SBType, SPY_GetMsgName(Msg,Wnd), Pt.x, Pt.y, HitTest );
switch (ScrollTrackHitTest)
{
case SCROLL_NOWHERE: /* No tracking in progress */
break;
case SCROLL_TOP_ARROW:
if (HitTest == ScrollTrackHitTest)
{
if ((WM_LBUTTONDOWN == Msg) || (WM_SYSTIMER == Msg))
{
SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
SB_LINEUP, (LPARAM) WndCtl);
}
SetSystemTimer(Wnd, SCROLL_TIMER, (WM_LBUTTONDOWN == Msg) ?
SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
(TIMERPROC) NULL);
if (ScrollBarInfo.rgstate[ScrollTrackHitTest] != STATE_SYSTEM_UNAVAILABLE)
{
if (!(ScrollBarInfo.rgstate[ScrollTrackHitTest] &= STATE_SYSTEM_PRESSED))
{
IntUpdateScrollArrows (Wnd, Dc, &ScrollBarInfo, &NewInfo, SBType, ScrollTrackHitTest, Vertical, TRUE);
}
}
}
else
{
IntUpdateScrollArrows (Wnd, Dc, &ScrollBarInfo, &NewInfo, SBType, ScrollTrackHitTest, Vertical, FALSE);
KillSystemTimer(Wnd, SCROLL_TIMER);
}
break;
case SCROLL_TOP_RECT:
if (HitTest == ScrollTrackHitTest)
{
if ((WM_LBUTTONDOWN == Msg) || (WM_SYSTIMER == Msg))
{
SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
SB_PAGEUP, (LPARAM) WndCtl);
}
SetSystemTimer(Wnd, SCROLL_TIMER, (WM_LBUTTONDOWN == Msg) ?
SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
(TIMERPROC) NULL);
}
else
{
KillSystemTimer(Wnd, SCROLL_TIMER);
}
break;
case SCROLL_THUMB:
if (Msg == WM_LBUTTONDOWN)
{
ScrollTrackingWin = Wnd;
ScrollTrackingBar = SBType;
ScrollTrackingPos = TrackThumbPos + LastMousePos - LastClickPos;
ScrollTrackingVal = IntScrollGetThumbVal(Wnd, SBType, &ScrollBarInfo,
Vertical, ScrollTrackingPos);
NewInfo.nTrackPos = ScrollTrackingVal;
NtUserSetScrollBarInfo(Wnd, IntScrollGetObjectId(SBType), &NewInfo);
IntScrollDrawMovingThumb(Dc, &ScrollBarInfo, Vertical);
}
else if (Msg == WM_LBUTTONUP)
{
ScrollTrackingWin = 0;
ScrollTrackingVal = 0;
IntDrawScrollInterior(Wnd, Dc, SBType, Vertical, &ScrollBarInfo);
}
else /* WM_MOUSEMOVE */
{
UINT Pos;
if (! IntScrollPtInRectEx(&ScrollBarInfo.rcScrollBar, Pt, Vertical))
{
Pos = LastClickPos;
}
else
{
Pt = IntScrollClipPos(&ScrollBarInfo.rcScrollBar, Pt);
Pos = Vertical ? (Pt.y - ScrollBarInfo.rcScrollBar.top)
: (Pt.x - ScrollBarInfo.rcScrollBar.left);
}
if (Pos != LastMousePos || ! ScrollMovingThumb)
{
LastMousePos = Pos;
ScrollTrackingPos = TrackThumbPos + Pos - LastClickPos;
ScrollTrackingVal = IntScrollGetThumbVal(Wnd, SBType, &ScrollBarInfo,
Vertical, ScrollTrackingPos);
NewInfo.nTrackPos = ScrollTrackingVal;
NtUserSetScrollBarInfo(Wnd, IntScrollGetObjectId(SBType), &NewInfo);
IntScrollDrawMovingThumb(Dc, &ScrollBarInfo, Vertical);
SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
MAKEWPARAM(SB_THUMBTRACK, ScrollTrackingVal),
(LPARAM) WndCtl);
}
}
break;
case SCROLL_BOTTOM_RECT:
if (HitTest == ScrollTrackHitTest)
{
if ((Msg == WM_LBUTTONDOWN) || (Msg == WM_SYSTIMER))
{
SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
SB_PAGEDOWN, (LPARAM) WndCtl);
}
SetSystemTimer(Wnd, SCROLL_TIMER, (WM_LBUTTONDOWN == Msg) ?
SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
(TIMERPROC) NULL);
}
else
{
KillSystemTimer(Wnd, SCROLL_TIMER);
}
break;
case SCROLL_BOTTOM_ARROW:
if (HitTest == ScrollTrackHitTest)
{
if ((Msg == WM_LBUTTONDOWN) || (Msg == WM_SYSTIMER))
{
SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
SB_LINEDOWN, (LPARAM) WndCtl);
}
SetSystemTimer(Wnd, SCROLL_TIMER, (WM_LBUTTONDOWN == Msg) ?
SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
(TIMERPROC) NULL);
if (ScrollBarInfo.rgstate[ScrollTrackHitTest] != STATE_SYSTEM_UNAVAILABLE)
{
if (!(ScrollBarInfo.rgstate[ScrollTrackHitTest] &= STATE_SYSTEM_PRESSED))
{
TRACE("Set Arrow\n");
IntUpdateScrollArrows (Wnd, Dc, &ScrollBarInfo, &NewInfo, SBType, ScrollTrackHitTest, Vertical, TRUE);
}
}
}
else
{
IntUpdateScrollArrows (Wnd, Dc, &ScrollBarInfo, &NewInfo, SBType, ScrollTrackHitTest, Vertical, FALSE);
KillSystemTimer(Wnd, SCROLL_TIMER);
}
break;
}
if (Msg == WM_LBUTTONDOWN)
{
if (SCROLL_THUMB == HitTest)
{
UINT Val = IntScrollGetThumbVal(Wnd, SBType, &ScrollBarInfo, Vertical,
TrackThumbPos + LastMousePos - LastClickPos);
SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
MAKEWPARAM(SB_THUMBTRACK, Val), (LPARAM) WndCtl);
}
}
if (Msg == WM_LBUTTONUP)
{
HitTest = ScrollTrackHitTest;
ScrollTrackHitTest = SCROLL_NOWHERE; /* Terminate tracking */
if (SCROLL_THUMB == HitTest)
{
UINT Val = IntScrollGetThumbVal(Wnd, SBType, &ScrollBarInfo, Vertical,
TrackThumbPos + LastMousePos - LastClickPos);
SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
MAKEWPARAM(SB_THUMBPOSITION, Val), (LPARAM) WndCtl);
}
/* SB_ENDSCROLL doesn't report thumb position */
SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
SB_ENDSCROLL, (LPARAM) WndCtl);
/* Terminate tracking */
ScrollTrackingWin = 0;
}
ReleaseDC(Wnd, Dc);
}
/***********************************************************************
* IntScrollCreateScrollBar
*
* Create a scroll bar
*/
static void IntScrollCreateScrollBar(
HWND Wnd /* [in] Handle of window with scrollbar(s) */,
LPCREATESTRUCTW lpCreate /* [in] The style and place of the scroll bar */)
{
SCROLLINFO Info;
Info.cbSize = sizeof(SCROLLINFO);
Info.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
Info.nMin = 0;
Info.nMax = 0;
Info.nPage = 0;
Info.nPos = 0;
Info.nTrackPos = 0;
NtUserSetScrollInfo(Wnd, SB_CTL, &Info, FALSE);
TRACE("hwnd=%p lpCreate=%p\n", Wnd, lpCreate);
#if 0 /* FIXME */
if (lpCreate->style & WS_DISABLED)
{
// info->flags = ESB_DISABLE_BOTH;
//NtUserEnableScrollBar(Wnd,SB_CTL,(wParam ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH));
NtUserMessageCall( Wnd, WM_ENABLE, FALSE, 0, 0, FNID_SCROLLBAR, FALSE);
ERR("Created WS_DISABLED scrollbar\n");
}
#endif
if (0 != (lpCreate->style & (SBS_SIZEGRIP | SBS_SIZEBOX)))
{
if (0 != (lpCreate->style & SBS_SIZEBOXTOPLEFTALIGN))
{
MoveWindow(Wnd, lpCreate->x, lpCreate->y, GetSystemMetrics(SM_CXVSCROLL) + 1,
GetSystemMetrics(SM_CYHSCROLL) + 1, FALSE);
}
else if (0 != (lpCreate->style & SBS_SIZEBOXBOTTOMRIGHTALIGN))
{
MoveWindow(Wnd, lpCreate->x + lpCreate->cx - GetSystemMetrics(SM_CXVSCROLL) - 1,
lpCreate->y + lpCreate->cy - GetSystemMetrics(SM_CYHSCROLL) - 1,
GetSystemMetrics(SM_CXVSCROLL) + 1,
GetSystemMetrics(SM_CYHSCROLL) + 1, FALSE);
}
}
else if (0 != (lpCreate->style & SBS_VERT))
{
if (0 != (lpCreate->style & SBS_LEFTALIGN))
{
MoveWindow(Wnd, lpCreate->x, lpCreate->y,
GetSystemMetrics(SM_CXVSCROLL) + 1, lpCreate->cy, FALSE);
}
else if (0 != (lpCreate->style & SBS_RIGHTALIGN))
{
MoveWindow(Wnd,
lpCreate->x + lpCreate->cx - GetSystemMetrics(SM_CXVSCROLL) - 1,
lpCreate->y,
GetSystemMetrics(SM_CXVSCROLL) + 1, lpCreate->cy, FALSE);
}
}
else /* SBS_HORZ */
{
if (0 != (lpCreate->style & SBS_TOPALIGN))
{
MoveWindow(Wnd, lpCreate->x, lpCreate->y,
lpCreate->cx, GetSystemMetrics(SM_CYHSCROLL) + 1, FALSE);
}
else if (0 != (lpCreate->style & SBS_BOTTOMALIGN))
{
MoveWindow(Wnd,
lpCreate->x,
lpCreate->y + lpCreate->cy - GetSystemMetrics(SM_CYHSCROLL) - 1,
lpCreate->cx, GetSystemMetrics(SM_CYHSCROLL) + 1, FALSE);
}
}
}
/* USER32 INTERNAL FUNCTIONS **************************************************/
/***********************************************************************
* ScrollTrackScrollBar
*
* Track a mouse button press on a scroll-bar.
* pt is in screen-coordinates for non-client scroll bars.
*/
VOID FASTCALL
ScrollTrackScrollBar(HWND Wnd, INT SBType, POINT Pt)
{
MSG Msg;
UINT XOffset = 0, YOffset = 0;
if (SBType != SB_CTL)
{ // Used with CMD mouse tracking.
PWND pwnd = ValidateHwnd(Wnd);
if (!pwnd) return;
XOffset = pwnd->rcClient.left - pwnd->rcWindow.left;
YOffset = pwnd->rcClient.top - pwnd->rcWindow.top;
// RECT rect;
// WIN_GetRectangles( Wnd, COORDS_CLIENT, &rect, NULL );
ScreenToClient(Wnd, &Pt);
// Pt.x -= rect.left;
// Pt.y -= rect.top;
Pt.x += XOffset;
Pt.y += YOffset;
}
IntScrollHandleScrollEvent(Wnd, SBType, WM_LBUTTONDOWN, Pt);
do
{
if (!GetMessageW(&Msg, 0, 0, 0)) break;
if (CallMsgFilterW(&Msg, MSGF_SCROLLBAR)) continue;
if ( Msg.message == WM_LBUTTONUP ||
Msg.message == WM_MOUSEMOVE ||
(Msg.message == WM_SYSTIMER && Msg.wParam == SCROLL_TIMER))
{
Pt.x = LOWORD(Msg.lParam) + XOffset;
Pt.y = HIWORD(Msg.lParam) + YOffset;
IntScrollHandleScrollEvent(Wnd, SBType, Msg.message, Pt);
}
else
{
TranslateMessage(&Msg);
DispatchMessageW(&Msg);
}
if (!IsWindow(Wnd))
{
ReleaseCapture();
break;
}
} while (Msg.message != WM_LBUTTONUP && GetCapture() == Wnd);
}
static DWORD FASTCALL
IntSetScrollInfo(HWND Wnd, LPCSCROLLINFO Info, BOOL bRedraw)
{
DWORD Ret = NtUserSetScrollInfo(Wnd, SB_CTL, Info, bRedraw);
if (Ret) IntNotifyWinEvent(EVENT_OBJECT_VALUECHANGE, Wnd, OBJID_CLIENT, CHILDID_SELF, WEF_SETBYWNDPTI);
return Ret;
}
/***********************************************************************
* ScrollBarWndProc
*/
LRESULT WINAPI
ScrollBarWndProc_common(WNDPROC DefWindowProc, HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, BOOL unicode )
{
#ifdef __REACTOS__ // Do this now, remove after Server side is fixed.
PWND pWnd;
PSBWND pSBWnd;
SCROLLINFO ScrollInfo;
pWnd = ValidateHwnd(Wnd);
if (pWnd)
{
if (!pWnd->fnid)
{
TRACE("ScrollBar CTL size %d\n", (sizeof(SBWND)-sizeof(WND)));
if ( pWnd->cbwndExtra < (sizeof(SBWND)-sizeof(WND)) )
{
ERR("Wrong Extra bytes for Scrollbar!\n");
return 0;
}
if (Msg != WM_CREATE)
{
return DefWindowProc(Wnd, Msg, wParam, lParam);
}
NtUserSetWindowFNID(Wnd, FNID_SCROLLBAR);
}
else
{
if (pWnd->fnid != FNID_SCROLLBAR)
{
ERR("Wrong window class for Scrollbar!\n");
return 0;
}
}
}
#endif
if (! IsWindow(Wnd))
{
return 0;
}
// Must be a scroll bar control!
pSBWnd = (PSBWND)pWnd;
switch (Msg)
{
case WM_CREATE:
IntScrollCreateScrollBar(Wnd, (LPCREATESTRUCTW) lParam);
break;
case WM_ENABLE:
{
return SendMessageW( Wnd, SBM_ENABLE_ARROWS, wParam ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH, 0);
}
case WM_LBUTTONDBLCLK:
case WM_LBUTTONDOWN:
if (GetWindowLongW( Wnd, GWL_STYLE ) & SBS_SIZEGRIP)
{
SendMessageW( GetParent(Wnd), WM_SYSCOMMAND,
SC_SIZE + ((GetWindowLongW( Wnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) ?
WMSZ_BOTTOMLEFT : WMSZ_BOTTOMRIGHT), lParam );
}
else
{
POINT Pt;
Pt.x = (short)LOWORD(lParam);
Pt.y = (short)HIWORD(lParam);
ScrollTrackScrollBar(Wnd, SB_CTL, Pt);
}
break;
case WM_LBUTTONUP:
case WM_MOUSEMOVE:
case WM_SYSTIMER:
{
POINT Pt;
Pt.x = (short)LOWORD(lParam);
Pt.y = (short)HIWORD(lParam);
IntScrollHandleScrollEvent(Wnd, SB_CTL, Msg, Pt);
}
break;
case WM_KEYDOWN:
IntScrollHandleKbdEvent(Wnd, wParam, lParam);
break;
case WM_KEYUP:
ShowCaret(Wnd);
break;
case WM_SETFOCUS:
{
/* Create a caret when a ScrollBar get focus */
RECT Rect;
int ArrowSize, ThumbSize, ThumbPos, Vertical;
Vertical = IntScrollGetScrollBarRect(Wnd, SB_CTL, &Rect,
&ArrowSize, &ThumbSize, &ThumbPos);
if (! Vertical)
{
CreateCaret(Wnd, (HBITMAP) 1, ThumbSize - 2, Rect.bottom - Rect.top - 2);
SetCaretPos(ThumbPos + 1, Rect.top + 1);
}
else
{
CreateCaret(Wnd, (HBITMAP) 1, Rect.right - Rect.left - 2, ThumbSize - 2);
SetCaretPos(Rect.top + 1, ThumbPos + 1);
}
ShowCaret(Wnd);
}
break;
case WM_KILLFOCUS:
{
RECT Rect;
int ArrowSize, ThumbSize, ThumbPos, Vertical;
Vertical = IntScrollGetScrollBarRect(Wnd, SB_CTL, &Rect,
&ArrowSize, &ThumbSize, &ThumbPos);
if (! Vertical)
{
Rect.left = ThumbPos + 1;
Rect.right = Rect.left + ThumbSize;
}
else
{
Rect.top = ThumbPos + 1;
Rect.bottom = Rect.top + ThumbSize;
}
HideCaret(Wnd);
InvalidateRect(Wnd, &Rect, FALSE);
DestroyCaret();
}
break;
case WM_ERASEBKGND:
return 1;
case WM_GETDLGCODE:
return DLGC_WANTARROWS; /* Windows returns this value */
case WM_PAINT:
{
PAINTSTRUCT Ps;
HDC Dc;
Dc = (0 != wParam ? (HDC) wParam : BeginPaint(Wnd, &Ps));
if (GetWindowLongPtrW(Wnd, GWL_STYLE) & SBS_SIZEGRIP)
{
IntScrollDrawSizeGrip(Wnd, Dc);
}
else if (0 != (GetWindowLongPtrW(Wnd, GWL_STYLE) & SBS_SIZEBOX))
{
RECT Rect;
GetClientRect(Wnd, &Rect);
FillRect(Dc, &Rect, GetSysColorBrush(COLOR_SCROLLBAR));
}
else
{
IntDrawScrollBar(Wnd, Dc, SB_CTL/*, TRUE, TRUE*/);
}
if (0 == wParam)
{
EndPaint(Wnd, &Ps);
}
}
break;
case SBM_GETPOS:
return pSBWnd->SBCalc.pos;
case SBM_GETRANGE:
*(LPINT)wParam = pSBWnd->SBCalc.posMin;
*(LPINT)lParam = pSBWnd->SBCalc.posMax;
// This message does not return a value.
return 0;
case SBM_ENABLE_ARROWS:
return EnableScrollBar( Wnd, SB_CTL, wParam );
case SBM_SETPOS:
{
ScrollInfo.cbSize = sizeof(SCROLLINFO);
ScrollInfo.fMask = SIF_POS|SIF_PREVIOUSPOS;
ScrollInfo.nPos = wParam;
return IntSetScrollInfo(Wnd, &ScrollInfo, lParam);
}
case SBM_SETRANGEREDRAW:
case SBM_SETRANGE:
{
ScrollInfo.cbSize = sizeof(SCROLLINFO);
ScrollInfo.fMask = SIF_RANGE|SIF_PREVIOUSPOS;
ScrollInfo.nMin = wParam;
ScrollInfo.nMax = lParam;
return IntSetScrollInfo(Wnd, &ScrollInfo, Msg == SBM_SETRANGEREDRAW ? TRUE : FALSE);
}
case SBM_SETSCROLLINFO:
return IntSetScrollInfo(Wnd, (LPCSCROLLINFO)lParam, wParam);
case SBM_GETSCROLLINFO:
{
PSBDATA pSBData = (PSBDATA)&pSBWnd->SBCalc;
DWORD ret = NtUserSBGetParms(Wnd, SB_CTL, pSBData, (SCROLLINFO *) lParam);
if (!ret)
{
ERR("SBM_GETSCROLLINFO No ScrollInfo\n");
}
return ret;
}
case SBM_GETSCROLLBARINFO:
((PSCROLLBARINFO)lParam)->cbSize = sizeof(SCROLLBARINFO);
return NtUserGetScrollBarInfo(Wnd, OBJID_CLIENT, (PSCROLLBARINFO)lParam);
case 0x00e5:
case 0x00e7:
case 0x00e8:
case 0x00ec:
case 0x00ed:
case 0x00ee:
case 0x00ef:
WARN("unknown Win32 msg %04x wp=%08lx lp=%08lx\n",
Msg, wParam, lParam );
break;
default:
if (WM_USER <= Msg)
{
WARN("unknown msg %04x wp=%04lx lp=%08lx\n", Msg, wParam, lParam);
}
if (unicode)
return DefWindowProcW( Wnd, Msg, wParam, lParam );
else
return DefWindowProcA( Wnd, Msg, wParam, lParam );
}
return 0;
}
LRESULT WINAPI
ScrollBarWndProcW(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
return ScrollBarWndProc_common(DefWindowProcW, Wnd, Msg, wParam, lParam, TRUE);
}
LRESULT WINAPI
ScrollBarWndProcA(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
return ScrollBarWndProc_common(DefWindowProcA, Wnd, Msg, wParam, lParam, FALSE);
}
/* PUBLIC FUNCTIONS ***********************************************************/
/*
* @implemented
*/
BOOL
WINAPI
DECLSPEC_HOTPATCH
EnableScrollBar( HWND hwnd, UINT nBar, UINT flags )
{
BOOL Hook, Ret = FALSE;
LoadUserApiHook();
Hook = BeginIfHookedUserApiHook();
/* Bypass SEH and go direct. */
if (!Hook)
{
Ret = NtUserEnableScrollBar(hwnd, nBar, flags);
if (!Ret) return Ret;
SCROLL_RefreshScrollBar( hwnd, nBar, TRUE, TRUE );
return Ret;
}
_SEH2_TRY
{
Ret = guah.EnableScrollBar(hwnd, nBar, flags);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
ERR("Got exception in hooked EnableScrollBar!\n");
}
_SEH2_END;
EndUserApiHook();
return Ret;
}
BOOL WINAPI
RealGetScrollInfo(HWND Wnd, INT SBType, LPSCROLLINFO Info)
{
PWND pWnd;
PSBDATA pSBData;
if (SB_CTL == SBType)
{
return SendMessageW(Wnd, SBM_GETSCROLLINFO, 0, (LPARAM) Info);
}
pWnd = ValidateHwnd(Wnd);
if (!pWnd) return FALSE;
if (SBType < SB_HORZ || SBType > SB_VERT)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (!pWnd->pSBInfo)
{
SetLastError(ERROR_NO_SCROLLBARS);
return FALSE;
}
pSBData = IntGetSBData(pWnd, SBType);
return NtUserSBGetParms(Wnd, SBType, pSBData, Info);
}
/*
* @implemented
*/
BOOL WINAPI GetScrollBarInfo( _In_ HWND hwnd, _In_ LONG idObject, _Inout_ LPSCROLLBARINFO info)
{
BOOL Ret;
PWND pWnd = ValidateHwnd(hwnd);
TRACE("hwnd=%p idObject=%d info=%p\n", hwnd, idObject, info);
if (!pWnd) return FALSE;
Ret = NtUserGetScrollBarInfo(hwnd, idObject, info); // This will be fixed once SB is server side.
/* rcScrollBar needs to be in screen coordinates */
OffsetRect( &(info->rcScrollBar), pWnd->rcWindow.left, pWnd->rcWindow.top );
return Ret;
}
/*
* @implemented
*/
BOOL
WINAPI
DECLSPEC_HOTPATCH
GetScrollInfo(HWND Wnd, INT SBType, LPSCROLLINFO Info)
{
BOOL Hook, Ret = FALSE;
LoadUserApiHook();
Hook = BeginIfHookedUserApiHook();
/* Bypass SEH and go direct. */
if (!Hook) return RealGetScrollInfo(Wnd, SBType, Info);
_SEH2_TRY
{
Ret = guah.GetScrollInfo(Wnd, SBType, Info);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
ERR("Got exception in hooked GetScrollInfo!\n");
}
_SEH2_END;
EndUserApiHook();
return Ret;
}
/*
* @implemented
*/
INT
WINAPI
DECLSPEC_HOTPATCH
GetScrollPos(HWND Wnd, INT Bar)
{
PWND pwnd;
PSBDATA pSBData;
TRACE("Wnd=%p Bar=%d\n", Wnd, Bar);
/* Refer SB_CTL requests to the window */
if (SB_CTL == Bar)
{
return SendMessageW(Wnd, SBM_GETPOS, (WPARAM) 0, (LPARAM) 0);
}
else if (Bar == SB_HORZ || Bar == SB_VERT )
{
pwnd = ValidateHwnd(Wnd);
if (!pwnd) return 0;
if (pwnd->pSBInfo)
{
pSBData = IntGetSBData(pwnd, Bar);
return pSBData->pos;
}
SetLastError(ERROR_NO_SCROLLBARS);
TRACE("GetScrollPos No Scroll Info\n");
return 0;
}
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
/*
* @implemented
*/
BOOL
WINAPI
DECLSPEC_HOTPATCH
GetScrollRange(HWND Wnd, int Bar, LPINT MinPos, LPINT MaxPos)
{
PWND pwnd;
PSBDATA pSBData;
TRACE("Wnd=%x Bar=%d Min=%p Max=%p\n", Wnd, Bar, MinPos, MaxPos);
/* Refer SB_CTL requests to the window */
if (SB_CTL == Bar)
{
return SendMessageW(Wnd, SBM_GETRANGE, (WPARAM) MinPos, (LPARAM) MaxPos);
}
else if (Bar == SB_HORZ || Bar == SB_VERT )
{
pwnd = ValidateHwnd(Wnd);
if (!pwnd) return FALSE;
if (pwnd->pSBInfo)
{
pSBData = IntGetSBData(pwnd, Bar);
*MinPos = pSBData->posMin;
*MaxPos = pSBData->posMax;
}
else
{
SetLastError(ERROR_NO_SCROLLBARS);
*MinPos = 0;
*MaxPos = 0;
}
return TRUE;
}
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
INT WINAPI
RealSetScrollInfo(HWND Wnd, int SBType, LPCSCROLLINFO Info, BOOL bRedraw)
{
if (SB_CTL == SBType)
{
return SendMessageW(Wnd, SBM_SETSCROLLINFO, (WPARAM) bRedraw, (LPARAM) Info);
}
else
{
return NtUserSetScrollInfo(Wnd, SBType, Info, bRedraw);
}
}
/*
* @implemented
*/
INT
WINAPI
DECLSPEC_HOTPATCH
SetScrollInfo(HWND Wnd, int SBType, LPCSCROLLINFO Info, BOOL bRedraw)
{
BOOL Hook;
INT Ret = 0;
LoadUserApiHook();
Hook = BeginIfHookedUserApiHook();
/* Bypass SEH and go direct. */
if (!Hook) return RealSetScrollInfo(Wnd, SBType, Info, bRedraw);
_SEH2_TRY
{
Ret = guah.SetScrollInfo(Wnd, SBType, Info, bRedraw);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
ERR("Got exception in hooked SetScrollInfo!\n");
}
_SEH2_END;
EndUserApiHook();
return Ret;
}
/*
* @implemented
*/
INT
WINAPI
DECLSPEC_HOTPATCH
SetScrollPos(HWND hWnd, INT nBar, INT nPos, BOOL bRedraw)
{
SCROLLINFO ScrollInfo;
ScrollInfo.cbSize = sizeof(SCROLLINFO);
ScrollInfo.fMask = SIF_POS|SIF_PREVIOUSPOS;
ScrollInfo.nPos = nPos;
return SetScrollInfo(hWnd, nBar, &ScrollInfo, bRedraw);
}
/*
* @implemented
*/
BOOL
WINAPI
DECLSPEC_HOTPATCH
SetScrollRange(HWND hWnd, INT nBar, INT nMinPos, INT nMaxPos, BOOL bRedraw)
{
PWND pWnd;
SCROLLINFO ScrollInfo;
pWnd = ValidateHwnd(hWnd);
if ( !pWnd ) return FALSE;
if (((LONGLONG)nMaxPos - nMinPos) > MAXLONG)
{
SetLastError(ERROR_INVALID_SCROLLBAR_RANGE);
return FALSE;
}
ScrollInfo.cbSize = sizeof(SCROLLINFO);
ScrollInfo.fMask = SIF_RANGE;
ScrollInfo.nMin = nMinPos;
ScrollInfo.nMax = nMaxPos;
SetScrollInfo(hWnd, nBar, &ScrollInfo, bRedraw); // do not bypass themes.
return TRUE;
}