ScrollerCtrl.cpp
上传用户:zengj883
上传日期:2013-02-03
资源大小:670k
文件大小:15k
源码类别:

源码/资料

开发平台:

C/C++

  1. // CScrollerCtrl : class implementation // Copyright 2002, Joshua Heyer //  You are free to use this code for whatever you want, provided you // give credit where credit is due: if you use this code without substantial // modification, please presearve this comment block. //  I'm providing this code in the hope that it is useful to someone, as i have // gotten much use out of other peoples code over the years. //  If you see value in it, make some improvements, etc., i would appreciate it  // if you sent me some feedback. //  Have fun! //
  2. #include "stdafx.h"
  3. #include "ScrollerCtrl.h"
  4. // command messages:
  5. // sent when text has scrolled completely off the window
  6. const int   CScrollerCtrl::SC_SCROLL_COMPLETE = 0;
  7. // defaults
  8. const int   CScrollerCtrl::nSCROLL_DELAY  = 80;    // time between each frame (milliseconds)
  9. const int   CScrollerCtrl::nSCROLL_PAUSE  = 5000;  // time to pause before autoscrolling (milliseconds)
  10. const int   CScrollerCtrl::nMARGIN        = 5;     // (pixels)
  11. const int   CScrollerCtrl::nFONT_SIZE     = 12;    // (points)
  12. const int   CScrollerCtrl::nFONT_WEIGHT   = FW_SEMIBOLD;
  13. const char* CScrollerCtrl::szFONT_NAME    = "Comic Sans MS";
  14. // initialization
  15. CScrollerCtrl::CScrollerCtrl()
  16. {
  17. m_crBackground   = RGB(0,0,0);
  18. m_crForeground   = RGB(255,255,255);
  19.    m_pbmpPattern     = NULL;
  20. m_pbmpLogo        = NULL;
  21. m_nContentHeight  = 0;
  22. m_nScrollOffset   = 0;
  23.    m_unTimerPause    = 2;
  24.    m_nScrollDelay    = nSCROLL_DELAY;
  25.    m_nScrollPause    = nSCROLL_PAUSE;
  26.    m_eState          = PAUSED;
  27.    m_bTilePattern    = TRUE;
  28.    m_bShowScroll     = FALSE;
  29.    m_bWrap           = TRUE;
  30. m_sizeBuffer      = CSize(0,0);
  31. }
  32. // create the window: 
  33. //    remove WS_VSCROLL to avoid showing scrollbar.
  34. //    remove WS_TABSTOP to disable keyboard scrolling.
  35. BOOL CScrollerCtrl::Create(const RECT& rect, CWnd* pParentWnd, UINT uStyle, UINT nID)
  36. {
  37.    if ( NULL == m_font.GetSafeHandle() )
  38.       SetFont(szFONT_NAME, nFONT_SIZE, nFONT_WEIGHT);
  39.    // remember if user specified the style, but don't show initially
  40.    m_bShowScroll = uStyle&WS_VSCROLL;
  41.    uStyle &= ~WS_VSCROLL;
  42. if ( CWnd::Create(::AfxRegisterWndClass(CS_HREDRAW|CS_PARENTDC|CS_VREDRAW,::LoadCursor(NULL,IDC_ARROW)), "Scroller", uStyle, rect, pParentWnd, nID) )
  43. {
  44.       m_eState = PAUSED;
  45.       SetTimer(1, m_nScrollDelay, NULL);
  46. m_unTimerPause = SetTimer(m_unTimerPause, m_nScrollPause, NULL);
  47. return TRUE;
  48. }
  49. return FALSE;
  50. }
  51. // activate/deactivate wrapping mode:
  52. // if not set, content is scrolled completely off screen
  53. // before being repeated.
  54. void CScrollerCtrl::SetWrapping(BOOL bWrap)
  55. {
  56.    m_bWrap = bWrap;
  57.    if ( NULL != m_hWnd )
  58.       Invalidate(FALSE);
  59. }
  60. // Sets the color used for the background (if no pattern is set) 
  61. // or margins (if pattern is set and not tiled)
  62. void CScrollerCtrl::SetBgColor(COLORREF clrBg)
  63. {
  64.    m_crBackground = clrBg;
  65.    if ( NULL != m_hWnd )
  66.       Invalidate(FALSE);
  67. }
  68. // Sets the color used for text
  69. void CScrollerCtrl::SetFgColor(COLORREF clrBg)
  70. {
  71.    m_crForeground = clrBg;
  72.    if ( NULL != m_hWnd )
  73.       Invalidate(FALSE);
  74. }
  75. // Sets the font; size is in points, see LOGFONT documentation for weight constants
  76. void CScrollerCtrl::SetFont(const CString& strName, int nSize, int nWeight)
  77. {
  78.    if ( NULL != m_font.GetSafeHandle() )
  79.       m_font.DeleteObject();
  80.    LOGFONT logFont;
  81.    memset(&logFont,0,sizeof(logFont));
  82.    strncpy(logFont.lfFaceName,strName,LF_FACESIZE);
  83.    logFont.lfPitchAndFamily = FF_SWISS;
  84.    logFont.lfQuality = ANTIALIASED_QUALITY;
  85.    logFont.lfWeight = nWeight;
  86.    logFont.lfHeight = nSize*10;
  87.    // actually create the font; if for some reason this fails, use a default
  88.    if ( !m_font.CreatePointFontIndirect(&logFont) )
  89.       m_font.CreateStockObject(SYSTEM_FONT);
  90. }
  91. // Sets the text to be displayed
  92. void CScrollerCtrl::SetText(const CString& strText)
  93. {
  94.    m_strText = strText;
  95.    if ( NULL != m_hWnd )
  96.    {
  97.       // force RecalcLayout()
  98.       m_sizeBuffer = CSize(0,0);
  99.       Invalidate(FALSE);
  100.    }
  101. }
  102. // Sets the bitmap to be displayed above the text
  103. CBitmap* CScrollerCtrl::SetLogo(CBitmap* pbmpLogo)
  104. {
  105.    CBitmap* pbmpOld = m_pbmpLogo;
  106.    m_pbmpLogo = pbmpLogo;
  107.    if ( NULL != m_hWnd )
  108.    {
  109.       // force RecalcLayout()
  110.       m_sizeBuffer = CSize(0,0);
  111.       Invalidate(FALSE);
  112.    }
  113.    return pbmpOld;
  114. }
  115. // Sets the background pattern
  116. CBitmap* CScrollerCtrl::SetPattern(CBitmap* pbmpPattern, BOOL bTile)
  117. {
  118.    m_bTilePattern = bTile;
  119.    CBitmap* pbmpOld = m_pbmpPattern;
  120.    m_pbmpPattern = pbmpPattern;
  121.    if ( NULL != m_hWnd )
  122.       Invalidate(FALSE);
  123.    return pbmpOld;
  124. }
  125. // Sets the time (in milliseconds) between frames (autoscrolling speed) 
  126. // (will revert to default if less than 0)
  127. void CScrollerCtrl::SetScrollDelay(int nScrollDelay)
  128. {
  129.    m_nScrollDelay = nScrollDelay;
  130.    if ( m_nScrollDelay < 0 )
  131.       m_nScrollDelay = nSCROLL_DELAY;
  132.    if ( NULL != m_hWnd )
  133.       SetTimer(1, m_nScrollDelay, NULL);
  134. }
  135. // Sets the delay (in milliseconds) when autoscrolling pauses 
  136. // (will disable pausing if set less than scroll delay)
  137. void CScrollerCtrl::SetScrollPause(int nScrollPause)
  138. {
  139.    m_nScrollPause = nScrollPause;
  140. }
  141. BEGIN_MESSAGE_MAP(CScrollerCtrl, CWnd)
  142. ON_WM_ERASEBKGND()
  143. ON_WM_GETDLGCODE()
  144. ON_WM_KEYDOWN()
  145. ON_WM_MOUSEWHEEL()
  146. ON_WM_PAINT()
  147. ON_WM_TIMER()
  148.    ON_WM_VSCROLL()
  149.    ON_WM_LBUTTONDOWN()
  150. END_MESSAGE_MAP()
  151. // CScrollerCtrl message handlers
  152. // entire window is updated in OnPaint() so do nothing here.
  153. BOOL CScrollerCtrl::OnEraseBkgnd(CDC* pDC)
  154. {
  155. return FALSE;
  156. }
  157. // resize buffer first if necessary, then compose onto buffer,
  158. // wrapping if specified, finally update the screen.
  159. void CScrollerCtrl::OnPaint()
  160. {
  161. CPaintDC dc(this);
  162. CDC dcBackBuffer;
  163. dcBackBuffer.CreateCompatibleDC(&dc);
  164.    // resize buffer if neccessary, calculate content size.
  165.    RecalcLayout(&dc);
  166. CBitmap* pOldBmp = dcBackBuffer.SelectObject(&m_bmpBackBuffer);
  167.    FillBackground(&dcBackBuffer);
  168.    int nOffset = nMARGIN + m_nScrollOffset;
  169.    do
  170.    {
  171.       nOffset += DrawLogo(&dcBackBuffer, nOffset) + nMARGIN;
  172.    nOffset += DrawBodyText(&dcBackBuffer, nOffset) + nMARGIN*2;
  173.    } while ( nOffset < m_sizeBuffer.cy && m_bWrap );
  174. dc.BitBlt(0,0,m_sizeBuffer.cx,m_sizeBuffer.cy,&dcBackBuffer,0,0,SRCCOPY);
  175. // cleanup
  176. dcBackBuffer.SelectObject(pOldBmp);
  177. }
  178. // if buffer size does not match window size, resize buffer.
  179. // Calculate content size.
  180. void CScrollerCtrl::RecalcLayout(CDC* pDC)
  181. {
  182. CRect rectClient;
  183. GetClientRect(&rectClient);
  184. if ( m_sizeBuffer != rectClient.Size() )
  185. {
  186. m_sizeBuffer = rectClient.Size();
  187. if ( m_bmpBackBuffer.GetSafeHandle() != NULL )
  188. m_bmpBackBuffer.DeleteObject();
  189. m_bmpBackBuffer.CreateCompatibleBitmap(pDC, m_sizeBuffer.cx, m_sizeBuffer.cy);
  190.       m_nContentHeight = nMARGIN*3;
  191.       m_nContentHeight += DrawLogo(pDC, 0, FALSE);
  192.       m_nContentHeight += DrawBodyText(pDC, 0, FALSE);
  193. }
  194. }
  195. // Draw the background; uses background color unless a pattern
  196. // bitmap is set, in which case that will be tiled or centered.
  197. void CScrollerCtrl::FillBackground(CDC* pDC)
  198. {
  199.    CRect rectClient;
  200.    GetClientRect(&rectClient);
  201.    if ( NULL == m_pbmpPattern )
  202.    {
  203.    pDC->FillSolidRect(rectClient, m_crBackground);
  204.    }
  205.    else
  206.    {
  207.       CDC dcPat;
  208.       dcPat.CreateCompatibleDC(pDC);
  209.       CBitmap* pbmpOld = dcPat.SelectObject(m_pbmpPattern);
  210.       BITMAP bitmap;
  211.       if ( m_pbmpPattern->GetBitmap(&bitmap) && bitmap.bmWidth > 0 && bitmap.bmHeight > 0 )
  212.       {
  213.          if ( m_bTilePattern )
  214.          {
  215.             for (int y=0; y<rectClient.bottom+bitmap.bmHeight; y += bitmap.bmHeight)
  216.             {
  217.                for (int x=0; x<rectClient.right+bitmap.bmWidth; x += bitmap.bmWidth)
  218.                {
  219.                   pDC->BitBlt(x,y, bitmap.bmWidth, bitmap.bmHeight, &dcPat, 0,0, SRCCOPY);
  220.                }
  221.             }
  222.          }
  223.          else
  224.          {
  225.           pDC->FillSolidRect(rectClient, m_crBackground);
  226.             pDC->BitBlt((m_sizeBuffer.cx-bitmap.bmWidth)/2,(m_sizeBuffer.cy-bitmap.bmHeight)/2, bitmap.bmWidth, bitmap.bmHeight, &dcPat, 0,0, SRCCOPY);
  227.          }
  228.       }
  229.       dcPat.SelectObject(pbmpOld);
  230.    }
  231. }
  232. // Draws the logo (if specified) at the given offset.
  233. // If bDraw is false, calculates height, but does not draw.
  234. // Returns height of logo.
  235. int CScrollerCtrl::DrawLogo(CDC* pDC, int nOffset, BOOL bDraw)
  236. {
  237.    if ( NULL == m_pbmpLogo )
  238.       return 0;
  239.    BITMAP bitmap;
  240.    memset(&bitmap,0,sizeof(bitmap));
  241.    if ( m_pbmpLogo->GetBitmap(&bitmap) && bDraw && bitmap.bmWidth > 0 && bitmap.bmHeight > 0 )
  242.    {
  243.       CDC dcLogo;
  244.       dcLogo.CreateCompatibleDC(pDC);
  245.       CBitmap* pbmpOld = dcLogo.SelectObject(m_pbmpLogo);
  246.       pDC->BitBlt((m_sizeBuffer.cx-bitmap.bmWidth)/2,nOffset, bitmap.bmWidth, bitmap.bmHeight, &dcLogo, 0,0, SRCCOPY);
  247.       dcLogo.SelectObject(pbmpOld);
  248.    }
  249.    return bitmap.bmHeight;
  250. }
  251. // Draws the text at the specified offset.
  252. // If bDraw is false, will calculate text height, but not draw.
  253. // Returns height of text.
  254. int CScrollerCtrl::DrawBodyText(CDC* pDC, int nOffset, BOOL bDraw)
  255. {
  256. CRect rect(nMARGIN,nOffset,m_sizeBuffer.cx-nMARGIN,m_sizeBuffer.cy);
  257. UINT uFlags = bDraw ? DT_EXPANDTABS|DT_NOPREFIX|DT_WORDBREAK : DT_EXPANDTABS|DT_NOCLIP|DT_CALCRECT|DT_NOPREFIX|DT_WORDBREAK;
  258.    CFont* pOldFont = pDC->SelectObject(&m_font);
  259.    pDC->SetBkMode(TRANSPARENT);
  260.    // draw shadow if displayed over pattern
  261.    if ( bDraw && NULL != m_pbmpPattern )
  262.    {
  263.       // offset 1/10 of font size
  264.       LOGFONT logFont;
  265.       m_font.GetLogFont(&logFont);
  266.       int nShadowOffset = MulDiv(-logFont.lfHeight, 72, pDC->GetDeviceCaps(LOGPIXELSY)*10);
  267.       // get color between forground and background for shadow (not correct i'm sure)       int red = (GetRValue(m_crForeground) + GetRValue(m_crBackground)*2)/3;       int green = (GetGValue(m_crForeground) + GetGValue(m_crBackground)*2)/3;       int blue = (GetBValue(m_crForeground) + GetBValue(m_crBackground)*2)/3;       COLORREF crDarker = RGB(red, green, blue);
  268.       pDC->SetTextColor(crDarker);
  269.       rect.OffsetRect(nShadowOffset,nShadowOffset);
  270.       pDC->DrawText(m_strText, &rect, uFlags);
  271.       rect.OffsetRect(-nShadowOffset,-nShadowOffset);
  272.    }
  273.    pDC->SetTextColor(m_crForeground);
  274. int nHeight = pDC->DrawText(m_strText, &rect, uFlags);
  275.    pDC->SelectObject(pOldFont);
  276. return nHeight;
  277. }
  278. // indicate that this control will process arrow keystrokes.
  279. UINT CScrollerCtrl::OnGetDlgCode()
  280. {
  281.    if ( GetStyle()&WS_TABSTOP )
  282.       return DLGC_WANTARROWS;
  283.    return 0;
  284. }
  285. // Grab focus if required.
  286. void CScrollerCtrl::OnLButtonDown(UINT nFlags, CPoint point)
  287. {
  288.    if ( GetStyle()&WS_TABSTOP )
  289.       SetFocus();
  290. }
  291. // Handle keyboard scrolling.
  292. void CScrollerCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  293. {
  294.    switch ( nChar )
  295.    {
  296.    case VK_UP:
  297.       PostMessage(WM_VSCROLL, SB_LINEUP);
  298.       break;
  299.    case VK_DOWN:
  300.       PostMessage(WM_VSCROLL, SB_LINEDOWN);
  301.       break;
  302.    case VK_PRIOR:    // why not VK_PAGEUP?
  303.       PostMessage(WM_VSCROLL, SB_PAGEUP);
  304.       break;
  305.    case VK_NEXT:     // why not VK_PAGEDOWN?
  306.       PostMessage(WM_VSCROLL, SB_PAGEDOWN);
  307.       break;
  308.    }
  309. }
  310. // Handle scrolling.
  311. // This can be triggered by mouse wheel and keyboard, 
  312. // as well as the scrollbar.
  313. void CScrollerCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  314. {
  315.    if ( !(GetStyle()&WS_TABSTOP) )
  316.       return;
  317.    // delay autoscroll after manual scroll
  318.    if ( m_nScrollPause > m_nScrollDelay )
  319.    {
  320.       m_eState = PAUSED;
  321.       m_unTimerPause = SetTimer(m_unTimerPause, m_nScrollPause, NULL);
  322.    }
  323.    switch (nSBCode)
  324.    {
  325.    case SB_BOTTOM:      // Scroll to bottom. 
  326.       m_nScrollOffset = m_sizeBuffer.cy-m_nContentHeight;
  327.       break;
  328.    case SB_TOP:         // Scroll to top. 
  329.       m_nScrollOffset = 0;
  330.       break;
  331.    case SB_LINEDOWN:    // Scroll one line down. 
  332.       m_nScrollOffset -= 8;
  333.       break;
  334.    case SB_LINEUP:      // Scroll one line up. 
  335.       m_nScrollOffset += 8;
  336.       break;
  337.    case SB_PAGEDOWN:    // Scroll one page down. 
  338.       m_nScrollOffset -= m_sizeBuffer.cy;
  339.       break;
  340.    case SB_PAGEUP:      // Scroll one page up. 
  341.       m_nScrollOffset += m_sizeBuffer.cy;
  342.       break;
  343.    case SB_THUMBPOSITION:
  344.    case SB_THUMBTRACK:
  345.       m_nScrollOffset = -((int)nPos);
  346.       break;
  347.    default:
  348.       return;
  349.    }
  350.    // constrain
  351. if ( m_nScrollOffset < m_sizeBuffer.cy-m_nContentHeight )
  352. m_nScrollOffset = m_sizeBuffer.cy-m_nContentHeight;
  353.    else if ( m_nScrollOffset > 0 )
  354.       m_nScrollOffset = 0;
  355.    // scroll
  356.    SetScrollPos(SB_VERT, -m_nScrollOffset);
  357.    Invalidate(FALSE);
  358. }
  359. // Handle mouse wheel scrolling.
  360. // I'll put actual effort into handling non-WHEEL_DELTA increments
  361. // when i actually get a mouse that sends them. ;~)
  362. BOOL CScrollerCtrl::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
  363. {
  364.    int nDist = abs(zDelta)/WHEEL_DELTA+1;
  365.    while ( nDist-- )
  366.    {
  367.       PostMessage(WM_VSCROLL, zDelta > 0 ? SB_LINEUP : SB_LINEDOWN);
  368.    }
  369.    return TRUE;
  370. }
  371. // Timers are used for three purposes:
  372. //    + Periodic checking to show/hide the scrollbar
  373. //    + Automatic scrolling (when m_eState == SCROLLING)
  374. //    + Ending automatic scrolling pauses (when m_eState == PAUSED)
  375. // This is accomplished via two timers:
  376. //    The first does double duty, handling both the scrollbar and
  377. //    automatic scrolling; it is active for the life of the window.
  378. //    The second is used for ending autoscroll pauses.
  379. void CScrollerCtrl::OnTimer(UINT nIDEvent)
  380. {
  381.    // don't autoscroll or hide scrollbar when user is dragging the scroll button.
  382.    if ( this == GetCapture() )
  383.       return;
  384.    // the scrollbar can be shown if the following are true:
  385.    //    + The content size is larger than the window
  386.    //    + The mouse cursor is over the window
  387.    //    + The top-level parent containing this control is active
  388.    if ( m_bShowScroll )
  389.    {
  390.    CRect rectWindow;
  391.    GetWindowRect(&rectWindow);
  392.       CPoint pntCursor;
  393.       ::GetCursorPos(&pntCursor);
  394.       if ( m_sizeBuffer.cy < m_nContentHeight && GetTopLevelParent() == GetActiveWindow() && rectWindow.PtInRect(pntCursor) )
  395.       {
  396.          SetScrollRange(SB_VERT, 0, m_nContentHeight-m_sizeBuffer.cy, FALSE);
  397.          SetScrollPos(SB_VERT, -m_nScrollOffset, TRUE);
  398.          ShowScrollBar(SB_VERT, TRUE);
  399.       }
  400.       else if ( this != GetCapture() )
  401.       {
  402.          ShowScrollBar(SB_VERT, FALSE);
  403.       }
  404.    }
  405.    if ( nIDEvent == m_unTimerPause )
  406.    {
  407.       m_eState = SCROLLING;
  408.       KillTimer(m_unTimerPause);
  409.    }
  410.    if ( m_eState == SCROLLING )
  411.    {
  412.    --m_nScrollOffset;
  413.    if ( m_nContentHeight+m_nScrollOffset < 0 )
  414.       {
  415.          // scrolling is complete; restart
  416.          m_nScrollOffset = m_bWrap ? 0 : m_sizeBuffer.cy;
  417.          CWnd* pwndParent = GetParent();
  418.          if ( NULL != pwndParent )
  419.             pwndParent->SendMessage(WM_COMMAND, MAKELPARAM(GetDlgCtrlID(), SC_SCROLL_COMPLETE), (LPARAM)GetSafeHwnd());
  420.       }
  421.       // pause at top and bottom
  422.       if ( 0 == m_nScrollOffset || (m_nScrollOffset+m_nContentHeight == m_sizeBuffer.cy && m_sizeBuffer.cy < m_nContentHeight) )
  423.       {
  424.          if ( m_nScrollPause > m_nScrollDelay )
  425.          {
  426.             m_eState = PAUSED;
  427.             m_unTimerPause = SetTimer(m_unTimerPause, m_nScrollPause, NULL);
  428.          }
  429.       }
  430.    Invalidate(FALSE);
  431.    }
  432. }