| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444 |
- // 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;
- }
- BOOL CScrollHelper::OnMouseHWheel(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.
- OnHScroll(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.
- OnHScroll(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
|