wine/dlls/user32/scroll.c
2022-08-27 16:53:33 +02:00

458 lines
14 KiB
C

/*
* Scrollbar control
*
* 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 St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "user_private.h"
#include "controls.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(scroll);
/* Overlap between arrows and thumb */
#define SCROLL_ARROW_THUMB_OVERLAP 0
/***********************************************************************
* SCROLL_DrawArrows
*
* Draw the scroll bar arrows.
*/
static void SCROLL_DrawArrows( HDC hdc, UINT flags,
RECT *rect, INT arrowSize, BOOL vertical,
BOOL top_pressed, BOOL bottom_pressed )
{
RECT r;
r = *rect;
if( vertical )
r.bottom = r.top + arrowSize;
else
r.right = r.left + arrowSize;
DrawFrameControl( hdc, &r, DFC_SCROLL,
(vertical ? DFCS_SCROLLUP : DFCS_SCROLLLEFT)
| (top_pressed ? (DFCS_PUSHED | DFCS_FLAT) : 0 )
| (flags & ESB_DISABLE_LTUP ? DFCS_INACTIVE : 0 ) );
r = *rect;
if( vertical )
r.top = r.bottom-arrowSize;
else
r.left = r.right-arrowSize;
DrawFrameControl( hdc, &r, DFC_SCROLL,
(vertical ? DFCS_SCROLLDOWN : DFCS_SCROLLRIGHT)
| (bottom_pressed ? (DFCS_PUSHED | DFCS_FLAT) : 0 )
| (flags & ESB_DISABLE_RTDN ? DFCS_INACTIVE : 0) );
}
/***********************************************************************
* SCROLL_DrawInterior
*
* Draw the scroll bar interior (everything except the arrows).
*/
static void SCROLL_DrawInterior( HWND hwnd, HDC hdc, INT nBar,
RECT *rect, INT arrowSize,
INT thumbSize, INT thumbPos, BOOL vertical,
BOOL top_selected, BOOL bottom_selected )
{
RECT r;
HPEN hSavePen;
HBRUSH hSaveBrush,hBrush;
if (nBar == SB_CTL)
{
hBrush = (HBRUSH)SendMessageW( GetParent(hwnd), WM_CTLCOLORSCROLLBAR,
(WPARAM)hdc,(LPARAM)hwnd);
}
else
{
COLORREF bk = GetSysColor( COLOR_3DHILIGHT );
SetTextColor( hdc, GetSysColor( COLOR_3DFACE ));
SetBkColor( hdc, bk );
/* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
* we better use 0x55aa bitmap brush to make scrollbar's background
* look different from the window background.
*/
if (bk == GetSysColor( COLOR_WINDOW ))
hBrush = SYSCOLOR_Get55AABrush();
else
{
hBrush = GetSysColorBrush( COLOR_SCROLLBAR );
UnrealizeObject( hBrush );
}
}
hSavePen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
hSaveBrush = SelectObject( hdc, hBrush );
/* Calculate the scroll rectangle */
r = *rect;
if (vertical)
{
r.top += arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
r.bottom -= (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
}
else
{
r.left += arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
r.right -= (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
}
/* Draw the scroll bar frame */
/* Draw the scroll rectangles and thumb */
if (!thumbPos) /* No thumb to draw */
{
PatBlt( hdc, r.left, r.top, r.right - r.left, r.bottom - r.top, PATCOPY );
/* cleanup and return */
SelectObject( hdc, hSavePen );
SelectObject( hdc, hSaveBrush );
return;
}
if (vertical)
{
PatBlt( hdc, r.left, r.top, r.right - r.left,
thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP),
top_selected ? 0x0f0000 : PATCOPY );
r.top += thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
PatBlt( hdc, r.left, r.top + thumbSize, r.right - r.left,
r.bottom - r.top - thumbSize,
bottom_selected ? 0x0f0000 : PATCOPY );
r.bottom = r.top + thumbSize;
}
else /* horizontal */
{
PatBlt( hdc, r.left, r.top,
thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP),
r.bottom - r.top, top_selected ? 0x0f0000 : PATCOPY );
r.left += thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
PatBlt( hdc, r.left + thumbSize, r.top, r.right - r.left - thumbSize,
r.bottom - r.top, bottom_selected ? 0x0f0000 : PATCOPY );
r.right = r.left + thumbSize;
}
/* Draw the thumb */
SelectObject( hdc, GetSysColorBrush(COLOR_BTNFACE) );
Rectangle( hdc, r.left+1, r.top+1, r.right-1, r.bottom-1 );
DrawEdge( hdc, &r, EDGE_RAISED, BF_RECT );
/* cleanup */
SelectObject( hdc, hSavePen );
SelectObject( hdc, hSaveBrush );
}
void WINAPI USER_ScrollBarDraw( HWND hwnd, HDC hdc, INT nBar, enum SCROLL_HITTEST hit_test,
const struct SCROLL_TRACKING_INFO *tracking_info, BOOL arrows,
BOOL interior, RECT *rect, UINT enable_flags, INT arrowSize,
INT thumbPos, INT thumbSize, BOOL vertical )
{
if (nBar == SB_CTL)
{
DWORD style = GetWindowLongW( hwnd, GWL_STYLE );
if (style & SBS_SIZEGRIP)
{
RECT rc = *rect;
FillRect( hdc, &rc, GetSysColorBrush( COLOR_BTNFACE ) );
rc.left = max( rc.left, rc.right - GetSystemMetrics( SM_CXVSCROLL ) - 1 );
rc.top = max( rc.top, rc.bottom - GetSystemMetrics( SM_CYHSCROLL ) - 1 );
DrawFrameControl( hdc, &rc, DFC_SCROLL, DFCS_SCROLLSIZEGRIP );
return;
}
if (style & SBS_SIZEBOX)
{
FillRect( hdc, rect, GetSysColorBrush( COLOR_BTNFACE ) );
return;
}
}
/* Draw the arrows */
if (arrows && arrowSize)
{
if (vertical == tracking_info->vertical && GetCapture() == hwnd)
SCROLL_DrawArrows( hdc, enable_flags, rect, arrowSize, vertical,
hit_test == tracking_info->hit_test && hit_test == SCROLL_TOP_ARROW,
hit_test == tracking_info->hit_test && hit_test == SCROLL_BOTTOM_ARROW );
else
SCROLL_DrawArrows( hdc, enable_flags, rect, arrowSize, vertical, FALSE, FALSE );
}
if (interior)
{
if (vertical == tracking_info->vertical && GetCapture() == hwnd)
{
SCROLL_DrawInterior( hwnd, hdc, nBar, rect, arrowSize, thumbSize, thumbPos, vertical,
hit_test == tracking_info->hit_test && hit_test == SCROLL_TOP_RECT,
hit_test == tracking_info->hit_test && hit_test == SCROLL_BOTTOM_RECT );
}
else
{
SCROLL_DrawInterior( hwnd, hdc, nBar, rect, arrowSize, thumbSize, thumbPos,
vertical, FALSE, FALSE );
}
}
/* if scroll bar has focus, reposition the caret */
if(hwnd==GetFocus() && (nBar==SB_CTL))
{
if (!vertical)
{
SetCaretPos(thumbPos + 1, rect->top + 1);
}
else
{
SetCaretPos(rect->top + 1, thumbPos + 1);
}
}
}
LRESULT WINAPI USER_ScrollBarProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
{
switch(message)
{
case WM_KEYDOWN:
case WM_KEYUP:
case WM_ENABLE:
case WM_LBUTTONDBLCLK:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_NCMOUSEMOVE:
case WM_NCMOUSELEAVE:
case WM_MOUSEMOVE:
case WM_MOUSELEAVE:
case WM_SYSTIMER:
case WM_SETFOCUS:
case WM_KILLFOCUS:
case WM_CREATE:
case WM_ERASEBKGND:
case WM_GETDLGCODE:
case WM_PAINT:
case SBM_SETRANGEREDRAW:
case SBM_SETRANGE:
case SBM_GETSCROLLINFO:
case SBM_GETSCROLLBARINFO:
case SBM_SETSCROLLINFO:
case WM_SETCURSOR:
case SBM_SETPOS:
case SBM_GETPOS:
case SBM_GETRANGE:
case SBM_ENABLE_ARROWS:
case 0x00e5:
case 0x00e7:
case 0x00e8:
case 0x00ec:
case 0x00ed:
case 0x00ee:
case 0x00ef:
return NtUserMessageCall( hwnd, message, wParam, lParam, 0, NtUserScrollBarWndProc, !unicode );
default:
if (message >= WM_USER)
WARN("unknown msg %04x wp=%04Ix lp=%08Ix\n",
message, wParam, lParam );
if (unicode)
return DefWindowProcW( hwnd, message, wParam, lParam );
else
return DefWindowProcA( hwnd, message, wParam, lParam );
}
return 0;
}
/***********************************************************************
* ScrollBarWndProc_common
*/
LRESULT ScrollBarWndProc_common( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, BOOL unicode )
{
return user_api->pScrollBarWndProc( hwnd, msg, wParam, lParam, unicode );
}
/*************************************************************************
* GetScrollInfo (USER32.@)
*
* GetScrollInfo can be used to retrieve the position, upper bound,
* lower bound, and page size of a scrollbar control.
*
* PARAMS
* hwnd [I] Handle of window with scrollbar(s)
* nBar [I] One of SB_HORZ, SB_VERT, or SB_CTL
* info [IO] fMask specifies which values to retrieve
*
* RETURNS
* TRUE if SCROLLINFO is filled
* ( if nBar is SB_CTL, GetScrollInfo returns TRUE even if nothing
* is filled)
*/
BOOL WINAPI DECLSPEC_HOTPATCH GetScrollInfo(HWND hwnd, INT nBar, LPSCROLLINFO info)
{
TRACE("hwnd=%p nBar=%d info=%p\n", hwnd, nBar, info);
/* Refer SB_CTL requests to the window */
if (nBar == SB_CTL)
{
SendMessageW(hwnd, SBM_GETSCROLLINFO, 0, (LPARAM)info);
return TRUE;
}
return NtUserGetScrollInfo( hwnd, nBar, info );
}
/*************************************************************************
* SetScrollPos (USER32.@)
*
* Sets the current position of the scroll thumb.
*
* PARAMS
* hwnd [I] Handle of window with scrollbar(s)
* nBar [I] One of SB_HORZ, SB_VERT, or SB_CTL
* nPos [I] New value
* bRedraw [I] Should scrollbar be redrawn afterwards?
*
* RETURNS
* Success: Scrollbar position
* Failure: 0
*
* REMARKS
* Note the ambiguity when 0 is returned. Use GetLastError
* to make sure there was an error (and to know which one).
*/
int WINAPI DECLSPEC_HOTPATCH SetScrollPos( HWND hwnd, int bar, int pos, BOOL redraw )
{
SCROLLINFO info;
info.cbSize = sizeof(info);
info.nPos = pos;
info.fMask = SIF_POS | SIF_RETURNPREV;
return NtUserSetScrollInfo( hwnd, bar, &info, redraw );
}
/*************************************************************************
* GetScrollPos (USER32.@)
*
* Gets the current position of the scroll thumb.
*
* PARAMS
* hwnd [I] Handle of window with scrollbar(s)
* nBar [I] One of SB_HORZ, SB_VERT, or SB_CTL
*
* RETURNS
* Success: Current position
* Failure: 0
*
* REMARKS
* There is ambiguity when 0 is returned. Use GetLastError
* to make sure there was an error (and to know which one).
*/
int WINAPI DECLSPEC_HOTPATCH GetScrollPos( HWND hwnd, int bar )
{
SCROLLINFO info;
TRACE( "hwnd=%p bar=%d\n", hwnd, bar );
/* Refer SB_CTL requests to the window */
if (bar == SB_CTL)
return SendMessageW( hwnd, SBM_GETPOS, 0, 0 );
info.cbSize = sizeof(info);
info.fMask = SIF_POS;
return GetScrollInfo( hwnd, bar, &info ) ? info.nPos : 0;
}
/*************************************************************************
* SetScrollRange (USER32.@)
* The SetScrollRange function sets the minimum and maximum scroll box positions
* If nMinPos and nMaxPos is the same value, the scroll bar will hide
*
* Sets the range of the scroll bar.
*
* PARAMS
* hwnd [I] Handle of window with scrollbar(s)
* nBar [I] One of SB_HORZ, SB_VERT, or SB_CTL
* minVal [I] New minimum value
* maxVal [I] New Maximum value
* bRedraw [I] Should scrollbar be redrawn afterwards?
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
BOOL WINAPI DECLSPEC_HOTPATCH SetScrollRange(HWND hwnd, INT nBar, INT minVal, INT maxVal, BOOL bRedraw)
{
SCROLLINFO info;
TRACE("hwnd=%p nBar=%d min=%d max=%d, bRedraw=%d\n", hwnd, nBar, minVal, maxVal, bRedraw);
info.cbSize = sizeof(info);
info.fMask = SIF_RANGE;
info.nMin = minVal;
info.nMax = maxVal;
NtUserSetScrollInfo( hwnd, nBar, &info, bRedraw );
return TRUE;
}
/*************************************************************************
* GetScrollRange (USER32.@)
*
* Gets the range of the scroll bar.
*
* PARAMS
* hwnd [I] Handle of window with scrollbar(s)
* nBar [I] One of SB_HORZ, SB_VERT, or SB_CTL
* lpMin [O] Where to store minimum value
* lpMax [O] Where to store maximum value
*
* RETURNS
* TRUE if values is filled
*/
BOOL WINAPI DECLSPEC_HOTPATCH GetScrollRange( HWND hwnd, int bar, int *min, int *max )
{
SCROLLINFO info;
TRACE( "hwnd=%p nBar=%d lpMin=%p lpMax=%p\n", hwnd, bar, min, max );
/* Refer SB_CTL requests to the window */
if (bar == SB_CTL)
{
SendMessageW( hwnd, SBM_GETRANGE, (WPARAM)min, (LPARAM)max );
return TRUE;
}
info.cbSize = sizeof(info);
info.fMask = SIF_RANGE;
info.nMin = info.nMax = 0;
GetScrollInfo( hwnd, bar, &info );
if (min) *min = info.nMin;
if (max) *max = info.nMax;
return TRUE;
}