ScrollerCtrl.cpp
上传用户:hnzyys
上传日期:2015-09-10
资源大小:423k
文件大小:15k
源码类别:

通讯编程

开发平台:

Visual 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.    if(m_eState == PAUSED)
  274.    pDC->SetTextColor(m_crForeground);
  275.    else
  276.    {
  277.    pDC->SetTextColor(RGB((int)160,0,0));
  278.    }
  279. int nHeight = pDC->DrawText(m_strText, &rect, uFlags);
  280.    pDC->SelectObject(pOldFont);
  281. return nHeight;
  282. }
  283. // indicate that this control will process arrow keystrokes.
  284. UINT CScrollerCtrl::OnGetDlgCode()
  285. {
  286.    if ( GetStyle()&WS_TABSTOP )
  287.       return DLGC_WANTARROWS;
  288.    return 0;
  289. }
  290. // Grab focus if required.
  291. void CScrollerCtrl::OnLButtonDown(UINT nFlags, CPoint point)
  292. {
  293.    if ( GetStyle()&WS_TABSTOP )
  294.       SetFocus();
  295. }
  296. // Handle keyboard scrolling.
  297. void CScrollerCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  298. {
  299.    switch ( nChar )
  300.    {
  301.    case VK_UP:
  302.       PostMessage(WM_VSCROLL, SB_LINEUP);
  303.       break;
  304.    case VK_DOWN:
  305.       PostMessage(WM_VSCROLL, SB_LINEDOWN);
  306.       break;
  307.    case VK_PRIOR:    // why not VK_PAGEUP?
  308.       PostMessage(WM_VSCROLL, SB_PAGEUP);
  309.       break;
  310.    case VK_NEXT:     // why not VK_PAGEDOWN?
  311.       PostMessage(WM_VSCROLL, SB_PAGEDOWN);
  312.       break;
  313.    }
  314. }
  315. // Handle scrolling.
  316. // This can be triggered by mouse wheel and keyboard, 
  317. // as well as the scrollbar.
  318. void CScrollerCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  319. {
  320.    if ( !(GetStyle()&WS_TABSTOP) )
  321.       return;
  322.    // delay autoscroll after manual scroll
  323.    if ( m_nScrollPause > m_nScrollDelay )
  324.    {
  325.       m_eState = PAUSED;
  326.       m_unTimerPause = SetTimer(m_unTimerPause, m_nScrollPause, NULL);
  327.    }
  328.    switch (nSBCode)
  329.    {
  330.    case SB_BOTTOM:      // Scroll to bottom. 
  331.       m_nScrollOffset = m_sizeBuffer.cy-m_nContentHeight;
  332.       break;
  333.    case SB_TOP:         // Scroll to top. 
  334.       m_nScrollOffset = 0;
  335.       break;
  336.    case SB_LINEDOWN:    // Scroll one line down. 
  337.       m_nScrollOffset -= 8;
  338.       break;
  339.    case SB_LINEUP:      // Scroll one line up. 
  340.       m_nScrollOffset += 8;
  341.       break;
  342.    case SB_PAGEDOWN:    // Scroll one page down. 
  343.       m_nScrollOffset -= m_sizeBuffer.cy;
  344.       break;
  345.    case SB_PAGEUP:      // Scroll one page up. 
  346.       m_nScrollOffset += m_sizeBuffer.cy;
  347.       break;
  348.    case SB_THUMBPOSITION:
  349.    case SB_THUMBTRACK:
  350.       m_nScrollOffset = -((int)nPos);
  351.       break;
  352.    default:
  353.       return;
  354.    }
  355.    // constrain
  356. if ( m_nScrollOffset < m_sizeBuffer.cy-m_nContentHeight )
  357. m_nScrollOffset = m_sizeBuffer.cy-m_nContentHeight;
  358.    else if ( m_nScrollOffset > 0 )
  359.       m_nScrollOffset = 0;
  360.    // scroll
  361.    SetScrollPos(SB_VERT, -m_nScrollOffset);
  362.    Invalidate(FALSE);
  363. }
  364. // Handle mouse wheel scrolling.
  365. // I'll put actual effort into handling non-WHEEL_DELTA increments
  366. // when i actually get a mouse that sends them. ;~)
  367. BOOL CScrollerCtrl::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
  368. {
  369.    int nDist = abs(zDelta)/WHEEL_DELTA+1;
  370.    while ( nDist-- )
  371.    {
  372.       PostMessage(WM_VSCROLL, zDelta > 0 ? SB_LINEUP : SB_LINEDOWN);
  373.    }
  374.    return TRUE;
  375. }
  376. // Timers are used for three purposes:
  377. //    + Periodic checking to show/hide the scrollbar
  378. //    + Automatic scrolling (when m_eState == SCROLLING)
  379. //    + Ending automatic scrolling pauses (when m_eState == PAUSED)
  380. // This is accomplished via two timers:
  381. //    The first does double duty, handling both the scrollbar and
  382. //    automatic scrolling; it is active for the life of the window.
  383. //    The second is used for ending autoscroll pauses.
  384. void CScrollerCtrl::OnTimer(UINT nIDEvent)
  385. {
  386.    // don't autoscroll or hide scrollbar when user is dragging the scroll button.
  387.    if ( this == GetCapture() )
  388.       return;
  389.    // the scrollbar can be shown if the following are true:
  390.    //    + The content size is larger than the window
  391.    //    + The mouse cursor is over the window
  392.    //    + The top-level parent containing this control is active
  393.    if ( m_bShowScroll )
  394.    {
  395.    CRect rectWindow;
  396.    GetWindowRect(&rectWindow);
  397.       CPoint pntCursor;
  398.       ::GetCursorPos(&pntCursor);
  399.       if ( m_sizeBuffer.cy < m_nContentHeight && GetTopLevelParent() == GetActiveWindow() && rectWindow.PtInRect(pntCursor) )
  400.       {
  401.          SetScrollRange(SB_VERT, 0, m_nContentHeight-m_sizeBuffer.cy, FALSE);
  402.          SetScrollPos(SB_VERT, -m_nScrollOffset, TRUE);
  403.          ShowScrollBar(SB_VERT, TRUE);
  404.       }
  405.       else if ( this != GetCapture() )
  406.       {
  407.          ShowScrollBar(SB_VERT, FALSE);
  408.       }
  409.    }
  410.    if ( nIDEvent == m_unTimerPause )
  411.    {
  412.       m_eState = SCROLLING;
  413.       KillTimer(m_unTimerPause);
  414.    }
  415.    if ( m_eState == SCROLLING )
  416.    {
  417.    --m_nScrollOffset;
  418.    if ( m_nContentHeight+m_nScrollOffset < 0 )
  419.       {
  420.          // scrolling is complete; restart
  421.          m_nScrollOffset = m_bWrap ? 0 : m_sizeBuffer.cy;
  422.          CWnd* pwndParent = GetParent();
  423.          if ( NULL != pwndParent )
  424.             pwndParent->SendMessage(WM_COMMAND, MAKELPARAM(GetDlgCtrlID(), SC_SCROLL_COMPLETE), (LPARAM)GetSafeHwnd());
  425.       }
  426.       // pause at top and bottom
  427.       if ( 0 == m_nScrollOffset || (m_nScrollOffset+m_nContentHeight == m_sizeBuffer.cy && m_sizeBuffer.cy < m_nContentHeight) )
  428.       {
  429.          if ( m_nScrollPause > m_nScrollDelay )
  430.          {
  431.             m_eState = PAUSED;
  432.             m_unTimerPause = SetTimer(m_unTimerPause, m_nScrollPause, NULL);
  433.          }
  434.       }
  435.    Invalidate(FALSE);
  436.    }
  437. }