|
|
@@ -0,0 +1,400 @@
|
|
|
+// Filename: ScrollHelper.cpp
|
|
|
+// 2005-07-01 nschan Initial revision.
|
|
|
+// 2005-09-08 nschan Added GetClientRectSB() function.
|
|
|
+
|
|
|
+#include "stdafx.h"
|
|
|
+#include "ScrollHelper.h"
|
|
|
+
|
|
|
+#ifdef _DEBUG
|
|
|
+#define new DEBUG_NEW
|
|
|
+#undef THIS_FILE
|
|
|
+static char THIS_FILE[] = __FILE__;
|
|
|
+#endif
|
|
|
+
|
|
|
+// Helper function to get client rect with possible
|
|
|
+// modification by adding scrollbar width/height.
|
|
|
+static void GetClientRectSB(CWnd* pWnd, CRect& rect)
|
|
|
+{
|
|
|
+ ASSERT( pWnd != NULL );
|
|
|
+
|
|
|
+ CRect winRect;
|
|
|
+ pWnd->GetWindowRect(&winRect);
|
|
|
+ pWnd->ScreenToClient(&winRect);
|
|
|
+
|
|
|
+ pWnd->GetClientRect(&rect);
|
|
|
+
|
|
|
+ int cxSB = ::GetSystemMetrics(SM_CXVSCROLL);
|
|
|
+ int cySB = ::GetSystemMetrics(SM_CYHSCROLL);
|
|
|
+
|
|
|
+ if ( winRect.right >= (rect.right + cxSB) )
|
|
|
+ rect.right += cxSB;
|
|
|
+ if ( winRect.bottom >= (rect.bottom + cySB) )
|
|
|
+ rect.bottom += cySB;
|
|
|
+}
|
|
|
+
|
|
|
+// CScrollHelper /////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+CScrollHelper::CScrollHelper()
|
|
|
+{
|
|
|
+ m_attachWnd = NULL;
|
|
|
+ m_pageSize = CSize(0,0);
|
|
|
+ m_displaySize = CSize(0,0);
|
|
|
+ m_scrollPos = CSize(0,0);
|
|
|
+}
|
|
|
+
|
|
|
+CScrollHelper::~CScrollHelper()
|
|
|
+{
|
|
|
+ DetachWnd();
|
|
|
+}
|
|
|
+
|
|
|
+void CScrollHelper::AttachWnd(CWnd* pWnd)
|
|
|
+{
|
|
|
+ m_attachWnd = pWnd;
|
|
|
+}
|
|
|
+
|
|
|
+void CScrollHelper::DetachWnd()
|
|
|
+{
|
|
|
+ m_attachWnd = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+void CScrollHelper::SetDisplaySize(int displayWidth, int displayHeight)
|
|
|
+{
|
|
|
+ int cxSB = ::GetSystemMetrics(SM_CXVSCROLL);
|
|
|
+ int cySB = ::GetSystemMetrics(SM_CYHSCROLL);
|
|
|
+
|
|
|
+ m_displaySize = CSize(displayWidth + cxSB, displayHeight + cySB);
|
|
|
+
|
|
|
+ if ( m_attachWnd != NULL && ::IsWindow(m_attachWnd->m_hWnd) )
|
|
|
+ UpdateScrollInfo();
|
|
|
+}
|
|
|
+
|
|
|
+const CSize& CScrollHelper::GetDisplaySize() const
|
|
|
+{
|
|
|
+ return m_displaySize;
|
|
|
+}
|
|
|
+
|
|
|
+const CSize& CScrollHelper::GetScrollPos() const
|
|
|
+{
|
|
|
+ return m_scrollPos;
|
|
|
+}
|
|
|
+
|
|
|
+const CSize& CScrollHelper::GetPageSize() const
|
|
|
+{
|
|
|
+ return m_pageSize;
|
|
|
+}
|
|
|
+
|
|
|
+void CScrollHelper::ScrollToOrigin(bool scrollLeft, bool scrollTop)
|
|
|
+{
|
|
|
+ if ( m_attachWnd == NULL )
|
|
|
+ return;
|
|
|
+
|
|
|
+ if ( scrollLeft )
|
|
|
+ {
|
|
|
+ if ( m_displaySize.cx > 0 && m_pageSize.cx > 0 && m_scrollPos.cx > 0 )
|
|
|
+ {
|
|
|
+ int deltaPos = -m_scrollPos.cx;
|
|
|
+ m_scrollPos.cx += deltaPos;
|
|
|
+ m_attachWnd->SetScrollPos(SB_HORZ, m_scrollPos.cx, TRUE);
|
|
|
+ m_attachWnd->ScrollWindow(-deltaPos, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( scrollTop )
|
|
|
+ {
|
|
|
+ if ( m_displaySize.cy > 0 && m_pageSize.cy > 0 && m_scrollPos.cy > 0 )
|
|
|
+ {
|
|
|
+ int deltaPos = -m_scrollPos.cy;
|
|
|
+ m_scrollPos.cy += deltaPos;
|
|
|
+ m_attachWnd->SetScrollPos(SB_VERT, m_scrollPos.cy, TRUE);
|
|
|
+ m_attachWnd->ScrollWindow(0, -deltaPos);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CScrollHelper::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
|
|
|
+{
|
|
|
+ if ( m_attachWnd == NULL )
|
|
|
+ return;
|
|
|
+
|
|
|
+ const int lineOffset = 60;
|
|
|
+
|
|
|
+ // Compute the desired change or delta in scroll position.
|
|
|
+ int deltaPos = 0;
|
|
|
+ switch( nSBCode )
|
|
|
+ {
|
|
|
+ case SB_LINELEFT:
|
|
|
+ // Left scroll arrow was pressed.
|
|
|
+ deltaPos = -lineOffset;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case SB_LINERIGHT:
|
|
|
+ // Right scroll arrow was pressed.
|
|
|
+ deltaPos = lineOffset;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case SB_PAGELEFT:
|
|
|
+ // User clicked inbetween left arrow and thumb.
|
|
|
+ deltaPos = -m_pageSize.cx;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case SB_PAGERIGHT:
|
|
|
+ // User clicked inbetween thumb and right arrow.
|
|
|
+ deltaPos = m_pageSize.cx;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case SB_THUMBTRACK:
|
|
|
+ // Scrollbar thumb is being dragged.
|
|
|
+ deltaPos = Get32BitScrollPos(SB_HORZ, pScrollBar) - m_scrollPos.cx;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case SB_THUMBPOSITION:
|
|
|
+ // Scrollbar thumb was released.
|
|
|
+ deltaPos = Get32BitScrollPos(SB_HORZ, pScrollBar) - m_scrollPos.cx;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ // We don't process other scrollbar messages.
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Compute the new scroll position.
|
|
|
+ int newScrollPos = m_scrollPos.cx + deltaPos;
|
|
|
+
|
|
|
+ // If the new scroll position is negative, we adjust
|
|
|
+ // deltaPos in order to scroll the window back to origin.
|
|
|
+ if ( newScrollPos < 0 )
|
|
|
+ deltaPos = -m_scrollPos.cx;
|
|
|
+
|
|
|
+ // If the new scroll position is greater than the max scroll position,
|
|
|
+ // we adjust deltaPos in order to scroll the window precisely to the
|
|
|
+ // maximum position.
|
|
|
+ int maxScrollPos = m_displaySize.cx - m_pageSize.cx;
|
|
|
+ if ( newScrollPos > maxScrollPos )
|
|
|
+ deltaPos = maxScrollPos - m_scrollPos.cx;
|
|
|
+
|
|
|
+ // Scroll the window if needed.
|
|
|
+ if ( deltaPos != 0 )
|
|
|
+ {
|
|
|
+ m_scrollPos.cx += deltaPos;
|
|
|
+ m_attachWnd->SetScrollPos(SB_HORZ, m_scrollPos.cx, TRUE);
|
|
|
+ m_attachWnd->ScrollWindow(-deltaPos, 0);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CScrollHelper::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
|
|
|
+{
|
|
|
+ if ( m_attachWnd == NULL )
|
|
|
+ return;
|
|
|
+
|
|
|
+ const int lineOffset = 60;
|
|
|
+
|
|
|
+ // Compute the desired change or delta in scroll position.
|
|
|
+ int deltaPos = 0;
|
|
|
+ switch( nSBCode )
|
|
|
+ {
|
|
|
+ case SB_LINEUP:
|
|
|
+ // Up arrow button on scrollbar was pressed.
|
|
|
+ deltaPos = -lineOffset;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case SB_LINEDOWN:
|
|
|
+ // Down arrow button on scrollbar was pressed.
|
|
|
+ deltaPos = lineOffset;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case SB_PAGEUP:
|
|
|
+ // User clicked inbetween up arrow and thumb.
|
|
|
+ deltaPos = -m_pageSize.cy;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case SB_PAGEDOWN:
|
|
|
+ // User clicked inbetween thumb and down arrow.
|
|
|
+ deltaPos = m_pageSize.cy;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case SB_THUMBTRACK:
|
|
|
+ // Scrollbar thumb is being dragged.
|
|
|
+ deltaPos = Get32BitScrollPos(SB_VERT, pScrollBar) - m_scrollPos.cy;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case SB_THUMBPOSITION:
|
|
|
+ // Scrollbar thumb was released.
|
|
|
+ deltaPos = Get32BitScrollPos(SB_VERT, pScrollBar) - m_scrollPos.cy;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ // We don't process other scrollbar messages.
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Compute the new scroll position.
|
|
|
+ int newScrollPos = m_scrollPos.cy + deltaPos;
|
|
|
+
|
|
|
+ // If the new scroll position is negative, we adjust
|
|
|
+ // deltaPos in order to scroll the window back to origin.
|
|
|
+ if ( newScrollPos < 0 )
|
|
|
+ deltaPos = -m_scrollPos.cy;
|
|
|
+
|
|
|
+ // If the new scroll position is greater than the max scroll position,
|
|
|
+ // we adjust deltaPos in order to scroll the window precisely to the
|
|
|
+ // maximum position.
|
|
|
+ int maxScrollPos = m_displaySize.cy - m_pageSize.cy;
|
|
|
+ if ( newScrollPos > maxScrollPos )
|
|
|
+ deltaPos = maxScrollPos - m_scrollPos.cy;
|
|
|
+
|
|
|
+ // Scroll the window if needed.
|
|
|
+ if ( deltaPos != 0 )
|
|
|
+ {
|
|
|
+ m_scrollPos.cy += deltaPos;
|
|
|
+ m_attachWnd->SetScrollPos(SB_VERT, m_scrollPos.cy, TRUE);
|
|
|
+ m_attachWnd->ScrollWindow(0, -deltaPos);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+BOOL CScrollHelper::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
|
|
|
+{
|
|
|
+ if ( m_attachWnd == NULL )
|
|
|
+ return FALSE;
|
|
|
+
|
|
|
+ // Don't do anything if the vertical scrollbar is not enabled.
|
|
|
+ int scrollMin = 0, scrollMax = 0;
|
|
|
+ m_attachWnd->GetScrollRange(SB_VERT, &scrollMin, &scrollMax);
|
|
|
+ if ( scrollMin == scrollMax )
|
|
|
+ return FALSE;
|
|
|
+
|
|
|
+ // Compute the number of scrolling increments requested.
|
|
|
+ int numScrollIncrements = abs(zDelta) / WHEEL_DELTA;
|
|
|
+
|
|
|
+ // Each scrolling increment corresponds to a certain number of
|
|
|
+ // scroll lines (one scroll line is like a SB_LINEUP or SB_LINEDOWN).
|
|
|
+ // We need to query the system parameters for this value.
|
|
|
+ int numScrollLinesPerIncrement = 0;
|
|
|
+ ::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &numScrollLinesPerIncrement, 0);
|
|
|
+
|
|
|
+ // Check if a page scroll was requested.
|
|
|
+ if ( numScrollLinesPerIncrement == WHEEL_PAGESCROLL )
|
|
|
+ {
|
|
|
+ // Call the vscroll message handler to do the work.
|
|
|
+ OnVScroll(zDelta > 0 ? SB_PAGEUP : SB_PAGEDOWN, 0, NULL);
|
|
|
+ return TRUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Compute total number of lines to scroll.
|
|
|
+ int numScrollLines = numScrollIncrements * numScrollLinesPerIncrement;
|
|
|
+
|
|
|
+ // Adjust numScrollLines to slow down the scrolling a bit more.
|
|
|
+ numScrollLines = max(numScrollLines/3, 1);
|
|
|
+
|
|
|
+ // Do the scrolling.
|
|
|
+ for(int i = 0; i < numScrollLines; ++i)
|
|
|
+ {
|
|
|
+ // Call the vscroll message handler to do the work.
|
|
|
+ OnVScroll(zDelta > 0 ? SB_LINEUP : SB_LINEDOWN, 0, NULL);
|
|
|
+ }
|
|
|
+
|
|
|
+ return TRUE;
|
|
|
+}
|
|
|
+
|
|
|
+void CScrollHelper::OnSize(UINT nType, int cx, int cy)
|
|
|
+{
|
|
|
+ UpdateScrollInfo();
|
|
|
+}
|
|
|
+
|
|
|
+int CScrollHelper::Get32BitScrollPos(int bar, CScrollBar* pScrollBar)
|
|
|
+{
|
|
|
+ // Code below is from MSDN Article ID 152252, "How To Get
|
|
|
+ // 32-bit Scroll Position During Scroll Messages".
|
|
|
+
|
|
|
+ // First determine if the user scrolled a scroll bar control
|
|
|
+ // on the window or scrolled the window itself.
|
|
|
+ ASSERT( m_attachWnd != NULL );
|
|
|
+ HWND hWndScroll;
|
|
|
+ if ( pScrollBar == NULL )
|
|
|
+ hWndScroll = m_attachWnd->m_hWnd;
|
|
|
+ else
|
|
|
+ hWndScroll = pScrollBar->m_hWnd;
|
|
|
+
|
|
|
+ SCROLLINFO si;
|
|
|
+ si.cbSize = sizeof(SCROLLINFO);
|
|
|
+ si.fMask = SIF_TRACKPOS;
|
|
|
+ ::GetScrollInfo(hWndScroll, bar, &si);
|
|
|
+
|
|
|
+ int scrollPos = si.nTrackPos;
|
|
|
+
|
|
|
+ return scrollPos;
|
|
|
+}
|
|
|
+
|
|
|
+void CScrollHelper::UpdateScrollInfo()
|
|
|
+{
|
|
|
+ if ( m_attachWnd == NULL )
|
|
|
+ return;
|
|
|
+
|
|
|
+ // Get the width/height of the attached wnd that includes the area
|
|
|
+ // covered by the scrollbars (if any). The reason we need this is
|
|
|
+ // because when scrollbars are present, both cx/cy and GetClientRect()
|
|
|
+ // when accessed from OnSize() do not include the scrollbar covered
|
|
|
+ // areas. In other words, their values are smaller than what you would
|
|
|
+ // expect.
|
|
|
+ CRect rect;
|
|
|
+ GetClientRectSB(m_attachWnd, rect);
|
|
|
+ CSize windowSize(rect.Width(), rect.Height());
|
|
|
+
|
|
|
+ // Update horizontal scrollbar.
|
|
|
+ CSize deltaPos(0,0);
|
|
|
+ UpdateScrollBar(SB_HORZ, windowSize.cx, m_displaySize.cx,
|
|
|
+ m_pageSize.cx, m_scrollPos.cx, deltaPos.cx);
|
|
|
+
|
|
|
+ // Update vertical scrollbar.
|
|
|
+ UpdateScrollBar(SB_VERT, windowSize.cy, m_displaySize.cy,
|
|
|
+ m_pageSize.cy, m_scrollPos.cy, deltaPos.cy);
|
|
|
+
|
|
|
+ // See if we need to scroll the window back in place.
|
|
|
+ // This is needed to handle the case where the scrollbar is
|
|
|
+ // moved all the way to the right for example, and controls
|
|
|
+ // at the left side disappear from the view. Then the user
|
|
|
+ // resizes the window wider until scrollbars disappear. Without
|
|
|
+ // this code below, the controls off the page will be gone forever.
|
|
|
+ if ( deltaPos.cx != 0 || deltaPos.cy != 0 )
|
|
|
+ {
|
|
|
+ m_attachWnd->ScrollWindow(deltaPos.cx, deltaPos.cy);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CScrollHelper::UpdateScrollBar(int bar, int windowSize, int displaySize,
|
|
|
+ LONG& pageSize, LONG& scrollPos, LONG& deltaPos)
|
|
|
+{
|
|
|
+ int scrollMax = 0;
|
|
|
+ deltaPos = 0;
|
|
|
+ if ( windowSize < displaySize )
|
|
|
+ {
|
|
|
+ scrollMax = displaySize - 1;
|
|
|
+ if ( pageSize > 0 && scrollPos > 0 )
|
|
|
+ {
|
|
|
+ // Adjust the scroll position when the window size is changed.
|
|
|
+ scrollPos = (LONG)(1.0 * scrollPos * windowSize / pageSize);
|
|
|
+ }
|
|
|
+ pageSize = windowSize;
|
|
|
+ scrollPos = min(scrollPos, displaySize - pageSize - 1);
|
|
|
+ deltaPos = m_attachWnd->GetScrollPos(bar) - scrollPos;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Force the scrollbar to go away.
|
|
|
+ pageSize = 0;
|
|
|
+ scrollPos = 0;
|
|
|
+ deltaPos = m_attachWnd->GetScrollPos(bar);
|
|
|
+ }
|
|
|
+
|
|
|
+ SCROLLINFO si;
|
|
|
+ memset(&si, 0, sizeof(SCROLLINFO));
|
|
|
+ si.cbSize = sizeof(SCROLLINFO);
|
|
|
+ si.fMask = SIF_ALL; // SIF_ALL = SIF_PAGE | SIF_RANGE | SIF_POS;
|
|
|
+ si.nMin = 0;
|
|
|
+ si.nMax = scrollMax;
|
|
|
+ si.nPage = pageSize;
|
|
|
+ si.nPos = scrollPos;
|
|
|
+ m_attachWnd->SetScrollInfo(bar, &si, TRUE);
|
|
|
+}
|
|
|
+
|
|
|
+// END
|
|
|
+
|