|
|
//-----------------------------------------------------------------------------
// 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(); }
|