Figure 2   PupText

PupText.h


 ////////////////////////////////////////////////////////////////
 // Microsoft Systems Journal -- December 1999
 // If this code works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it.
 // Compiles with Visual C++ 6.0, runs on Windows 98 and probably Windows NT too.
 //
 #pragma once
 
 //////////////////
 // Get NONCLIENTMETRICS info: ctor calls SystemParametersInfo.
 //
 class CNonClientMetrics : public NONCLIENTMETRICS {
 public:
    CNonClientMetrics() {
       cbSize = sizeof(NONCLIENTMETRICS);
       SystemParametersInfo(SPI_GETNONCLIENTMETRICS,0,this,0);
    }
 };
 
 //////////////////
 // Popup text window, like tooltip but no timer stuff.
 // Can be right or left-justified relative to creation point.
 //
 class CPopupText : public CWnd {
 public:
    CSize m_szMargins;      // extra space around text: change if you like
    enum {JUSTIFYLEFT=0, JUSTIFYRIGHT};
    CPopupText();
    virtual ~CPopupText();
    BOOL Create(CPoint pt, CWnd* pParentWnd, UINT nStyle=0, UINT nID=0);
 
 protected:
    CFont m_font;           // font to use (same as tooltips)
    UINT  m_nStyle;         // style (see enum below)
 
    virtual void PostNcDestroy();
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
    afx_msg void OnPaint();
    afx_msg LRESULT OnSetText(WPARAM wp, LPARAM lp);
    DECLARE_MESSAGE_MAP()
 };
PupText.cpp

 ////////////////////////////////////////////////////////////////
 // Microsoft Systems Journal -- December 1999
 // If this code works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it.
 // Compiles with Visual C++ 6.0, runs on Windows 98 and probably Windows NT too.
 //
 #include "stdafx.h"
 #include "puptext.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 BEGIN_MESSAGE_MAP(CPopupText,CWnd)
    ON_WM_PAINT()
    ON_MESSAGE(WM_SETTEXT, OnSetText)
 END_MESSAGE_MAP()
 
 CPopupText::CPopupText()
 {
    m_szMargins = CSize(4,4);
    // create font -- use system tooltip font
    CNonClientMetrics ncm;
    m_font.CreateFontIndirect(&ncm.lfStatusFont);
 }
 
 CPopupText::~CPopupText()
 {
 }
 
 //////////////////
 // Create window. pt is upper left or upper-right corner depending on nStyle.
 //
 CPopupText::Create(CPoint pt, CWnd* pParentWnd, UINT nStyle, UINT nID)
 {
    m_nStyle = nStyle;
    return CreateEx(0,
       NULL,
       NULL,
       WS_POPUP|WS_VISIBLE,
       CRect(pt,CSize(0,0)),
       pParentWnd,
       nID);
 }
 
 //////////////////
 // Someone changed the text: resize to fit new text
 //
 LRESULT CPopupText::OnSetText(WPARAM wp, LPARAM lp)
 {
    CClientDC dc = this;
    CFont* pOldFont = dc.SelectObject(&m_font);
    CRect rc;
    GetWindowRect(&rc);
    int x = (m_nStyle & JUSTIFYRIGHT) ? rc.right : rc.left;
    int y = rc.top;
    dc.DrawText(CString((LPCTSTR)lp), &rc, DT_CALCRECT);
    rc.InflateRect(m_szMargins);
    if (m_nStyle & JUSTIFYRIGHT)
       x -= rc.Width();
    SetWindowPos(NULL,x,y,rc.Width(),rc.Height(), SWP_NOZORDER);
    return Default();
 }
 
 //////////////////
 // Paint the text. Use system colors
 //
 void CPopupText::OnPaint()
 {
    CPaintDC dc(this);
    CRect rc;
    GetClientRect(&rc);
    CString s;
    GetWindowText(s);
    CBrush b(GetSysColor(COLOR_INFOBK)); // use tooltip bg color
    dc.FillRect(&rc, &b);
 
    // draw text
    dc.SetBkMode(TRANSPARENT);
    CFont* pOldFont = dc.SelectObject(&m_font);
    dc.SetTextColor(GetSysColor(COLOR_INFOTEXT)); // tooltip text color
    dc.DrawText(s, &rc, DT_SINGLELINE|DT_CENTER|DT_VCENTER);
    dc.SelectObject(pOldFont);
 }
 
 //////////////////
 // Register class if needed
 //
 BOOL CPopupText::PreCreateWindow(CREATESTRUCT& cs) 
 {
    static CString sClassName;
    if (sClassName.IsEmpty())
       sClassName = AfxRegisterWndClass(0);
    cs.lpszClass = sClassName;
    cs.style |= WS_BORDER;
    return CWnd::PreCreateWindow(cs);
 }
 
 //////////////////
 // CPopupText is intended to be used on the stack,
 // not heap, so don't auto-delete.
 //
 void CPopupText::PostNcDestroy()
 {
    // don't delete this
 }

Figure 3   CDeferScrollHook

DeferScroll.h


 ////////////////////////////////////////////////////////////////
 // Microsoft Systems Journal -- December 1999
 // If this code works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it.
 // Compiles with Visual C++ 6.0, runs on Windows 98 and probably Windows NT too.
 //
 #pragma once
 #include "SubClass.h"
 #include "PupText.h"
 
 //////////////////
 // This class implements deferred scrolling for views and list
 // controls, and probably any other kind of scrolling window.
 //
 class CDeferScrollHook : public CSubclassWnd {
 public:
    CDeferScrollHook();
    virtual ~CDeferScrollHook();
    BOOL Install(CWnd* pWnd, UINT nStyle = CPopupText::JUSTIFYRIGHT);
 
    // You must override to provide the "tip" text.
    // If you really don't, just return FALSE.
    virtual BOOL OnGetScrollText(CString& s, UINT nPos)=0;
 
 protected:
    UINT  m_nStyle;         // style for popup text
    BOOL  m_bIgnoreThumb;   // ignore SB_THUMBTRACK
    virtual LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp);
    afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
    DECLARE_DYNAMIC(CDeferScrollHook)
 };
DeferScroll.cpp

 ////////////////////////////////////////////////////////////////
 // Microsoft Systems Journal -- December 1999
 // If this code works, it was written by Paul DiLascia.
 // If not, I don't know who wrote it.
 // Compiles with Visual C++ 6.0, runs on Windows 98 and probably Windows NT too.
 //
 #include "stdafx.h"
 #include "DeferScroll.h"
 #include "PupText.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 IMPLEMENT_DYNAMIC(CDeferScrollHook, CSubclassWnd);
 
 CDeferScrollHook::CDeferScrollHook()
 {
    m_bIgnoreThumb=TRUE;
 }
 
 CDeferScrollHook::~CDeferScrollHook()
 {
 }
 
 BOOL CDeferScrollHook::Install(CWnd* pWnd, UINT nStyle)
 {
    m_nStyle = nStyle; // style for popup scroll tip
    return HookWindow(pWnd);
 }
 
 //////////////////
 // Main hook proc intercepts scrolling notifications
 //
 LRESULT CDeferScrollHook::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
 {
    if (msg==WM_VSCROLL) {
       UINT nSBCode = LOWORD(wp);
       UINT nPos    = HIWORD(wp);
 
       static CPopupText wndTip;  // one and only
       CWnd* pWnd = CWnd::FromHandle(m_hWnd);
 
       switch (nSBCode) {
       case SB_THUMBTRACK:
          if (m_bIgnoreThumb) {
             // ignoring: don't pass to window, but display scroll tip
             CString s;
             if (OnGetScrollText(s,nPos)) {   // if app supplies popup text:
                if (!wndTip) {
                   // create scroll tip window
                   CPoint pt;
                   GetCursorPos(&pt);
                   pt.x -= 10;
                   wndTip.Create(pt, pWnd, m_nStyle);
                }
                wndTip.SetWindowText(s);
                wndTip.Invalidate();
                wndTip.UpdateWindow();
             }
             return 0; // handled; otherwise (not ignoring) pass along
          }
          break;
 
       case SB_THUMBPOSITION:
       {
          // convert SB_THUMBPOSITION to SB_THUMBTRACK, and don't ignore it
          BOOL bSaveIgnoreThumb = m_bIgnoreThumb;
          m_bIgnoreThumb = FALSE;
          pWnd->SendMessage(msg, MAKEWPARAM(SB_THUMBTRACK, nPos), lp);
          m_bIgnoreThumb = bSaveIgnoreThumb;
          return 0; // handled
       }
 
       case SB_ENDSCROLL:
          // end of scrollling: remove scroll tip
          if (wndTip)
             wndTip.DestroyWindow();
          break;
       }
    }
    // I don't handle it: pass along
    return CSubclassWnd::WindowProc(msg, wp, lp);
 }