Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

509 lines
10 KiB

//-----------------------------------------------------------------------------
// File: flexscrollbar.cpp
//
// Desc: Implements CFlexScrollBar (derived from CFlexWnd), a scroll bar
// control similar to a Windows scroll bar.
//
// Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved.
//-----------------------------------------------------------------------------
#include "common.hpp"
CFlexScrollBar::CFlexScrollBar() :
m_nMin(0),
m_nMax(0),
m_nPage(25),
m_nPos(25),
m_bVert(TRUE),
m_hWndNotify(NULL),
m_bValid(FALSE),
m_bCapture(FALSE),
m_bDragging(FALSE),
m_code(SB_ENDSCROLL),
m_rgbBk(RGB(0,0,0)),
m_rgbFill(RGB(0,0,255)),
m_rgbLine(RGB(0,255,255))
{
}
CFlexScrollBar::~CFlexScrollBar()
{
}
CFlexScrollBar *CreateFlexScrollBar(FLEXSCROLLBARCREATESTRUCT *pcs)
{
CFlexScrollBar *psb = new CFlexScrollBar;
if (psb && psb->Create(pcs))
return psb;
delete psb;
return NULL;
}
BOOL CFlexScrollBar::Create(FLEXSCROLLBARCREATESTRUCT *pcs)
{
if (this == NULL)
return FALSE;
if (pcs == NULL)
return FALSE;
if (pcs->dwSize != sizeof(FLEXSCROLLBARCREATESTRUCT))
return FALSE;
if (pcs->min > pcs->max)
return FALSE;
int range = pcs->max - pcs->min;
if (pcs->page > range)
return FALSE;
m_bVert = ( pcs->dwFlags & FSBF_VERT ) == FSBF_VERT;
SetValues(pcs->min, pcs->max, pcs->page, pcs->pos);
m_hWndNotify = pcs->hWndNotify ? pcs->hWndNotify : pcs->hWndParent;
if (!CFlexWnd::Create(pcs->hWndParent, pcs->rect, pcs->bVisible))
return FALSE;
Calc();
// TODO: make sure that creation sends no notifications.
// all initial notifications should be sent here.
return TRUE;
}
int CFlexScrollBar::GetLineAdjust()
{
return 1;
}
int CFlexScrollBar::GetPageAdjust()
{
return m_nPage > 1 ? m_nPage - 1 : 1;
}
void CFlexScrollBar::AdjustPos(int adj, BOOL bForceCalc)
{
int old = m_nPos;
m_nPos += adj;
if (m_nPos < m_nMin)
m_nPos = m_nMin;
if (m_nPos > m_nMax - m_nPage)
m_nPos = m_nMax - m_nPage;
if (m_nPos == old && !bForceCalc)
return;
if (Calc())
Invalidate();
}
BOOL CFlexScrollBar::FailCalc(BOOL bOldValid)
{
m_bValid = FALSE;
if (bOldValid)
Invalidate();
return m_bValid;
}
BOOL CFlexScrollBar::Calc()
{
BOOL bOldValid = m_bValid;
#define FAIL return FailCalc(bOldValid)
if (!m_hWnd)
FAIL;
SRECT zero;
m_rectLineUp = zero;
m_rectPageUp = zero;
m_rectTrack = zero;
m_rectThumb = zero;
m_rectPageDown = zero;
m_rectLineDown = zero;
SPOINT size = GetClientSize();
int ord = m_bVert ? 1 : 0;
int nord = m_bVert ? 0 : 1;
int length = size.a[ord];
int width = size.a[nord];
int arrowlen = width;
if (width < 1 || length < 2)
FAIL;
int tracklen = length - arrowlen * 2;
int trackofs = arrowlen;
BOOL bOverlappingArrows = tracklen < -1;
int overlap = !bOverlappingArrows ? 0 : -tracklen;
SRECT up, down, track, thumb, temp;
if (overlap > 1)
{
int mid = length / 2;
up.lr.a[nord] = width;
up.lr.a[ord] = mid;
down.ul.a[ord] = mid;
down.lr.a[ord] = length;
down.lr.a[nord] = width;
m_rectLineUp = up;
m_rectLineDown = down;
return m_bValid = TRUE;
}
up.lr.a[nord] = width;
up.lr.a[ord] = arrowlen;
down.lr.a[nord] = width;
down.ul.a[ord] = length - arrowlen;
down.lr.a[ord] = length;
m_rectLineUp = up;
m_rectLineDown = down;
int tmin = up.lr.a[ord];
int tmax = down.ul.a[ord];
int trange = tmax - tmin;
int range = m_nMax - m_nMin;
assert(trange > 0);
if (!(range > 0) || !(trange > 0))
return m_bValid = TRUE;
track.ul.a[ord] = tmin;
track.lr.a[nord] = width;
track.lr.a[ord] = tmax;
m_rectTrack = track;
const int minthumblen = 3;
int thumblen = MulDiv(m_nPage, trange, range);
if (thumblen < minthumblen)
thumblen = minthumblen;
int thumbrange = trange - thumblen /*+ 1*/;
int pagerange = range - m_nPage;
if (!(pagerange > 1) || !(thumbrange > 1))
return m_bValid = TRUE;
int logpos = m_bDragging ? m_nPreDragPos : m_nPos;
int thumbpos = MulDiv(logpos - m_nMin, thumbrange, pagerange) + tmin;
if (m_bDragging)
{
SPOINT rp = m_point, rs = m_startpoint;
int rdelta = rp.a[ord] - rs.a[ord];
thumbpos += rdelta;
if (thumbpos < tmin)
thumbpos = tmin;
if (thumbpos > tmax - thumblen)
thumbpos = tmax - thumblen;
m_nThumbPos = MulDiv(thumbpos - tmin, pagerange, thumbrange) + m_nMin;
if (m_nThumbPos < m_nMin)
m_nThumbPos = m_nMin;
if (m_nThumbPos > m_nMax - m_nPage)
m_nThumbPos = m_nMax - m_nPage;
}
thumb.ul.a[ord] = thumbpos;
thumb.lr.a[nord] = width;
thumb.lr.a[ord] = thumbpos + thumblen;
m_rectThumb = thumb;
temp = track;
temp.lr.a[ord] = thumb.ul.a[ord];
if (temp.lr.a[ord] > temp.ul.a[ord])
m_rectPageUp = temp;
temp = track;
temp.ul.a[ord] = thumb.lr.a[ord];
if (temp.lr.a[ord] > temp.ul.a[ord])
m_rectPageDown = temp;
return m_bValid = TRUE;
#undef FAIL
}
void CFlexScrollBar::SetValues(int min, int max, int page, int pos)
{
m_nMin = min < max ? min : max;
m_nMax = max > min ? max : min;
m_nPage = page;
AdjustPos(pos - m_nPos, TRUE);
}
static BOOL UseRect(const RECT &rect)
{
if (rect.left >= rect.right || rect.bottom <= rect.top)
return FALSE;
return TRUE;
}
static void Rectangle(HDC hDC, RECT rect)
{
if (!UseRect(rect))
return;
Rectangle(hDC, rect.left, rect.top, rect.right, rect.bottom);
}
void CFlexScrollBar::OnPaint(HDC hDC)
{
HDC hBDC = NULL, hODC = NULL;
CBitmap *pbm = NULL;
if (!InRenderMode())
{
hODC = hDC;
pbm = CBitmap::Create(GetClientSize(), RGB(0,0,0), hDC);
if (pbm != NULL)
{
hBDC = pbm->BeginPaintInto();
if (hBDC != NULL)
{
hDC = hBDC;
}
}
}
InternalPaint(hDC);
if (!InRenderMode())
{
if (pbm != NULL)
{
if (hBDC != NULL)
{
pbm->EndPaintInto(hBDC);
pbm->Draw(hODC);
}
delete pbm;
}
}
}
void CFlexScrollBar::InternalPaint(HDC hDC)
{
HGDIOBJ hPen, hOldPen, hBrush, hOldBrush;
hPen = (HGDIOBJ)CreatePen(PS_SOLID, 1, m_rgbBk);
if (hPen != NULL)
{
hOldPen = SelectObject(hDC, hPen),
hOldBrush = SelectObject(hDC, GetStockObject(BLACK_BRUSH));
Rectangle(hDC, m_rectPageUp);
Rectangle(hDC, m_rectPageDown);
Rectangle(hDC, m_rectLineUp);
Rectangle(hDC, m_rectLineDown);
SelectObject(hDC, hOldPen);
DeleteObject(hPen);
hBrush = (HGDIOBJ)CreateSolidBrush(m_rgbFill);
if (hBrush != NULL)
{
SelectObject(hDC, (HGDIOBJ)hBrush);
hPen = (HGDIOBJ)CreatePen(PS_SOLID, 1, m_rgbFill);
if (hPen != NULL)
{
SelectObject(hDC, hPen);
Rectangle(hDC, m_rectThumb);
SelectObject(hDC, hOldPen);
DeleteObject(hPen);
}
hPen = (HGDIOBJ)CreatePen(PS_SOLID, 1, m_rgbLine);
if (hPen != NULL)
{
SelectObject(hDC, hPen);
// draw the two arrows for this scrollbar
for (int i = 0; i < 2; i++)
DrawArrow(hDC, i ? m_rectLineUp : m_rectLineDown, m_bVert, i);
#if 0
// draw the two arrows for this scrollbar
for (int i = 0; i < 2; i++)
{
const RECT &rect = i == 0 ? m_rectLineUp : m_rectLineDown;
SRECT srect = rect;
srect.right--;
srect.bottom--;
int ord = m_bVert ? 1 : 0;
int nord = m_bVert ? 0 : 1;
SPOINT p(i ? srect.lr : srect.ul), b(i ? srect.ul : srect.lr);
b.a[ord] += 2 * i - 1;
SPOINT t = p;
t.a[nord] = (p.a[nord] + b.a[nord]) / 2;
SPOINT u;
u.a[ord] = b.a[ord];
u.a[nord] = p.a[nord];
POINT poly[] = { {t.x, t.y}, {u.x, u.y}, {b.x, b.y} };
Polygon(hDC, poly, 3);
}
#endif
SelectObject(hDC, hOldPen);
DeleteObject(hPen);
}
SelectObject(hDC, hOldBrush);
DeleteObject(hBrush);
}
}
}
BOOL InRect(const RECT &rect, POINT point)
{
return UseRect(rect) && PtInRect(&rect, point);
}
LRESULT CFlexScrollBar::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_SIZE:
Calc();
Invalidate();
return 0;
// make sure flexwnd doesn't do ANYTHING with our mouse messages
case WM_MOUSEMOVE:
case WM_LBUTTONUP:
case WM_LBUTTONDOWN:
case WM_RBUTTONUP:
case WM_RBUTTONDOWN:
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
{
POINT point = {int(signed short(LOWORD(lParam))), int(signed short(HIWORD(lParam)))};
m_point = point;
m_code = HitTest(point);
}
case WM_TIMER:
case WM_CAPTURECHANGED:
break;
default:
return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
}
switch (msg)
{
case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK:
if (m_code == SB_ENDSCROLL)
goto endscroll;
if (m_code == SB_THUMBTRACK)
m_bDragging = TRUE;
else
SetTimer(m_hWnd, 1, 500, NULL);
m_startcode = m_code;
m_startpoint = m_point;
m_nPreDragPos = m_nPos;
m_bCapture = TRUE;
SetCapture();
if (!m_bDragging)
Notify(m_code);
break;
case WM_LBUTTONUP:
case WM_MOUSEMOVE:
if (!m_bDragging)
break;
if (Calc())
{
Invalidate();
// Force repaint the updated scrollbar position. If we don't do this,
// the WM_PAINT message will be pre-empted by the WM_FLEXVSCROLL messages.
// Sometimes this happens during the entire duration of draggin the scroll
// bar. The result is that the scroll bar does not get updated when
// dragging.
SendMessage(m_hWnd, WM_PAINT, 0, 0);
}
Notify(m_startcode);
break;
case WM_TIMER:
if (m_bCapture) switch (wParam)
{
case 1:
KillTimer(m_hWnd, 1);
SetTimer(m_hWnd, 2, 50, NULL);
case 2:
if (m_bDragging)
break;
if (m_code == m_startcode)
Notify(m_code);
break;
}
break;
}
switch (msg)
{
case WM_LBUTTONUP:
case WM_CAPTURECHANGED:
endscroll:
if (m_bCapture)
{
m_bCapture = FALSE;
KillTimer(m_hWnd, 1);
KillTimer(m_hWnd, 2);
ReleaseCapture();
if (m_bDragging)
Notify(SB_THUMBPOSITION);
BOOL bWasDragging = m_bDragging;
m_bDragging = FALSE;
if (bWasDragging)
{
if (Calc())
Invalidate();
}
Notify(SB_ENDSCROLL);
}
break;
}
return 0;
}
void CFlexScrollBar::Notify(int code)
{
if (!m_hWndNotify)
return;
SendMessage(m_hWndNotify, m_bVert ? WM_FLEXVSCROLL : WM_FLEXHSCROLL,
(WPARAM)code, (LPARAM)(LPVOID)this);
}
int CFlexScrollBar::HitTest(POINT point)
{
if (InRect(m_rectLineUp, point))
return SB_LINEUP;
else if (InRect(m_rectLineDown, point))
return SB_LINEDOWN;
else if (InRect(m_rectThumb, point))
return SB_THUMBTRACK;
else if (InRect(m_rectPageUp, point))
return SB_PAGEUP;
else if (InRect(m_rectPageDown, point))
return SB_PAGEDOWN;
else
return SB_ENDSCROLL;
}
void CFlexScrollBar::SetColors(COLORREF bk, COLORREF fill, COLORREF line)
{
m_rgbBk = bk;
m_rgbFill = fill;
m_rgbLine = line;
Invalidate();
}