Click here to Skip to main content
15,895,746 members
Articles / Desktop Programming / WTL

A fast and lightweight cell control

Rate me:
Please Sign up or sign in to vote.
4.42/5 (31 votes)
11 Mar 2008CPOL1 min read 91K   4.5K   81  
A fast and lightweight cell control for displaying tabular data. The cell is a custom control derived from ATL::CWindow.
// FlatTab.h : header file
//
// Copyright ?1999 Ian Brumby
//
// This source code may be used in compiled form in any way you desire. 
// Source file(s) may be redistributed unmodified by any means PROVIDING
// they are not sold for profit without the authors expressed written consent,
// and providing that this notice and the authors name and all copyright
// notices remain intact.
//
// ==========================================================================  
// HISTORY:	  
// ==========================================================================  
//			1.4 	21 Jul 1999	- Initial release.
//			1.5		30 Aug 1999 - Several enhancements by Phil Baxter 
//					<Phil.Baxter@mrjinx.demon.co.uk>
// ==========================================================================  

// Excel uses FF_ROMAN and Font Height = ~(control's height - 4)
// Excel draws its tabs right to left
// Excel has 3DLIGHT highlighting on the left diagonal
// Excel has home/end buttons
// Excel's buttons change width as the control's height changes
//
/////////////////////////////////////////////////////////////////////////////
/****************************************************************************
 *
 * $Date: 10/26/99 10:40p $
 * $Revision: 8 $
 * $Archive: /CodeJock/Include/CJFlatTabCtrl.h $
 *
 * $History: CJFlatTabCtrl.h $
 * 
 * *****************  Version 8  *****************
 * User: Kirk Stowell Date: 10/26/99   Time: 10:40p
 * Updated in $/CodeJock/Include
 * Made class methods virtual for inheritance purposes.
 * 
 * *****************  Version 7  *****************
 * User: Kirk Stowell Date: 10/24/99   Time: 12:15a
 * Updated in $/CodeJock/Include
 * Fixed minor bug with tab font creation.
 * 
 * *****************  Version 6  *****************
 * User: Kirk Stowell Date: 10/24/99   Time: 12:01a
 * Updated in $/CodeJock/Include
 * Fixed potential resource and memory leak problems.
 * 
 * *****************  Version 5  *****************
 * User: Kirk Stowell Date: 10/14/99   Time: 12:25p
 * Updated in $/CodeJock/Include
 * Added source control history to file header.
 *
 ***************************************************************************/
/////////////////////////////////////////////////////////////////////////////
// ==========================================================================  
// Port to ATL/WTL (ATLFlatTab.h)
//
// Rashid Thadha 03/02/2001
// rashidthadha@hotmail.com
//
// Rashid Thadha 3/8/2002
// rashidthadha@hotmail.com
// few bug fixes and enhancement for Horizontal scroll bar capabilities
// ==========================================================================  

#if !defined(ATL_FLATTAB_H)
#define ATL_FLATTAB_H

#include "ATLMetaFileButton.h"

#include <list>
using namespace std;

#define IDC_LEFTBUTTON                  5031
#define IDC_RIGHTBUTTON                 5032
#define IDC_HOMEBUTTON                  5033
#define IDC_ENDBUTTON                   5034

// control styles
#define FTS_BOTTOM              0x0002
#define FTS_HASARROWS           0x0004
#define FTS_HASHOMEEND          0x0008 // Must be used with FTS_HASARROWS

typedef list<CString*, allocator<CString*> > CTabList;

// CCJFlatTabCtrl is a CWnd derived class which is used to create a flat tab
// control simular to DevStudio.
class CFlatTabCtrl : public CWindowImpl<CFlatTabCtrl, CWindow>
{
    // Construction
public:
    // ===========================================================================
    // Function name	: CFlatTabCtrl
    // Description	  : Constructor 
    // Return type		: 
    // ===========================================================================
    
    CFlatTabCtrl() 
    {
        m_bHasArrows          = false;
        m_bHasHomeEnd         = false;
        m_bTabsOnBottom       = false;
        m_iCurSel             = -1;
        m_pLeftButton         = NULL;
        m_pRightButton        = NULL;
        m_pHomeButton         = NULL;
        m_pEndButton          = NULL;
        m_hLeftArrow          = NULL;
        m_hLeftArrowDisabled  = NULL;
        m_hRightArrow         = NULL;
        m_hRightArrowDisabled = NULL;
        m_hHomeArrow          = NULL;
        m_hHomeArrowDisabled  = NULL;
        m_hEndArrow           = NULL;
        m_hEndArrowDisabled   = NULL;
        m_iXOffset            = 0;
    }
    
    
    BEGIN_MSG_MAP(CFlatTabCtrl)
        MESSAGE_HANDLER(WM_PAINT, OnPaint)
        MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
        MESSAGE_HANDLER(WM_SIZE, OnSize)
        MESSAGE_HANDLER(WM_CREATE, OnCreate)
        COMMAND_HANDLER(IDC_LEFTBUTTON, BN_CLICKED, OnLeftArrow)
        COMMAND_HANDLER(IDC_RIGHTBUTTON, BN_CLICKED, OnRightArrow)
        COMMAND_HANDLER(IDC_HOMEBUTTON, BN_CLICKED, OnHomeArrow)
        COMMAND_HANDLER(IDC_ENDBUTTON, BN_CLICKED, OnEndArrow)
        REFLECT_NOTIFICATIONS()
        END_MSG_MAP()
        
        DECLARE_WND_CLASS_EX("MFXFlatTabCtrl", CS_DBLCLKS, NULL);
    
    // Attributes
public:
    
    // Operations
public:
    BOOL PreTranslateMessage(MSG* pMsg)
    {
        if (::IsWindow(m_ToolTip.m_hWnd))                                                                                                                                                                                                                                                                                                                                                                                                                         
        {
            if (pMsg->message == WM_MOUSEMOVE && pMsg->hwnd == m_hWnd)
            {
                CPoint Point(LOWORD(pMsg->lParam), HIWORD(pMsg->lParam));
                
                TCHITTESTINFO test;
                test.pt = Point;
                int iTab = HitTest(&test);
                
                if (iTab >= 0)
                {
                    CString strText;
                    strText = (LPCTSTR) * (GetTabTipListItem(iTab));
                    
                    _U_STRINGorID stext(strText);
                    m_ToolTip.UpdateTipText(stext, m_hWnd);
                    m_ToolTip.RelayEvent(pMsg);
                } 
                else  
                {
                    // VC5 compatibility - Kirk Stowell - 10-21-99
#ifndef _MSC_VER
                    ::SendMessage(m_ToolTip.m_hWnd, TTM_POP, 0, 0L);
#endif
                }
            }
            else 
            {
                // VC5 compatibility - Kirk Stowell - 10-21-99
#ifndef _MSC_VER
                ::SendMessage(m_ToolTip.m_hWnd, TTM_POP, 0, 0L);
#endif
            }
        }
        
        return (BOOL)SendMessage(WM_FORWARDMSG, 0, (LPARAM)pMsg);
    }
    
    
    // ===========================================================================
    // Function name	: InsertItem
    // Description	  : 
    // Return type		: virtual BOOL 
    // Argument       : const int nItem
    // Argument       : LPCTSTR lpszItem
    // ===========================================================================
    virtual BOOL InsertItem(const int nItem, LPCTSTR lpszItem)
    {
        if (nItem < 0 || nItem > TabListCount())
            return -1;
        
        // add tab to our list
        CString* pItem = new CString;
        ATLASSERT(pItem);
        
        *pItem = lpszItem;
        m_TabList.insert(m_TabList.end(), pItem);
        
        // PGB - Add the tabs tip text to the list
        
        CString* pTipItem = new CString;
        ATLASSERT(pTipItem);
        
        *pTipItem = lpszItem;
        m_TabTipList.insert(m_TabTipList.end(), pTipItem);
        
        if (TabListCount() == 1)
            m_iCurSel = 0;
        
        m_iTotalTabWidth = GetTotalTabWidth();      // PGB - Recalculate the total tab width now we have added a new item
        
        EnableButtons();
        InvalidateTabs();
        
        return nItem;
    }
    
    // ===========================================================================
    // Function name	: TabListCount
    // Description	  : 
    // Return type		: int 
    // ===========================================================================
    int TabListCount()
    {
        return (int)m_TabList.size();
    }
    
    // ===========================================================================
    // Function name	: DeleteItem
    // Description	  : 
    // Return type		: virtual BOOL 
    // Argument       : int nItem
    // ===========================================================================
    virtual BOOL DeleteItem(int nItem)
    {
        if (nItem < 0 || nItem >= TabListCount())
            return FALSE;
        
        int nIndex = 0;
        CTabList::iterator ListIterator;	
        for (ListIterator = m_TabList.begin(); ListIterator != m_TabList.end(); ++ListIterator)        
        {
            if (nIndex == nItem)
            {
                CString* pItem = *ListIterator;
                
                if (pItem)
                {
                    delete pItem;
                    pItem = NULL;
                }
                if (ListIterator != m_TabList.end())
                    m_TabList.erase(ListIterator);
                
                break;
            }
            nIndex++;
        }
        
        nIndex = 0;
        CTabList::iterator ListIterator2;	
        for (ListIterator2 = m_TabTipList.begin(); ListIterator2 != m_TabTipList.end(); ++ListIterator2)        
        {
            if (nIndex == nItem)
            {
                CString* pItem = *ListIterator2;
                
                if (pItem)
                {
                    delete pItem;
                    pItem = NULL;
                }
                if (ListIterator2 != m_TabTipList.end())
                    m_TabTipList.erase(ListIterator2);
                
                break;
            }
            nIndex++;
        }
        
        // PGB - Stole the following code from the OnLButtonDown() function
        // in order to cause the tab control to show the newly selected item
        // after deleting an item from the list
        
        // warn parent that the selection is about to change
        int id = GetDlgCtrlID();
        NMHDR hdr;
        hdr.hwndFrom = m_hWnd;
        hdr.idFrom = id;
        hdr.code = TCN_SELCHANGING;
        CWindow wnd(GetParent());
        if (wnd.SendMessage(WM_NOTIFY, id, (LPARAM)&hdr) == 0)
        {
            int nTabListCount = TabListCount();
            if (m_iCurSel >= nTabListCount)
            {
                SetCurSel(nTabListCount - 1); // PGB - Reset the currently selected tab to the last one in the list
                InvalidateTabs();
            }
            
            // notify parent that the selection has changed
            hdr.hwndFrom = m_hWnd;
            hdr.idFrom = id;
            hdr.code = TCN_SELCHANGE;
            wnd.SendMessage(WM_NOTIFY, id, (LPARAM)&hdr);
        }
        
        m_iTotalTabWidth = GetTotalTabWidth();      // PGB - Recalculate the total tab width now we have deleted an item
        
        EnableButtons();
        InvalidateTabs();
        
        return TRUE;
    }
    
    // ===========================================================================
    // Function name	: DeleteAllItems
    // Description	  : 
    // Return type		: virtual BOOL 
    // ===========================================================================
    virtual BOOL DeleteAllItems()
    {
        CTabList::iterator ListIterator;	
        for (ListIterator = m_TabList.begin(); ListIterator != m_TabList.end(); ++ListIterator)        
        {
            CString* pItem = *ListIterator;
            
            if (pItem)
            {
                delete pItem;
                pItem = NULL;
            }
        }
        m_TabList.erase(m_TabList.begin(), m_TabList.end());
        
        CTabList::iterator ListIterator2;	
        for (ListIterator2= m_TabTipList.begin(); ListIterator2 != m_TabTipList.end(); ++ListIterator2)        
        {
            CString* pItem = *ListIterator2;
            
            if (pItem)
            {
                delete pItem;
                pItem = NULL;
            }
        }
        m_TabTipList.erase(m_TabTipList.begin(), m_TabTipList.end());
        
        m_iCurSel = -1;                             // PGB - Reset the currently selected tab to -1 as we have no tabs in our list now.
        
        m_iTotalTabWidth = GetTotalTabWidth();      // PGB - Recalculate the total tab width now we have deleted all items
        
        EnableButtons();
        InvalidateTabs();
        
        return TRUE;
    }
    
    // ===========================================================================
    // Function name	: GetItemRect
    // Description	  : 
    // Return type		: virtual BOOL 
    // Argument       : int nItem
    // Argument       : LPRECT lpRect
    // ===========================================================================
    virtual BOOL GetItemRect(int nItem, LPRECT lpRect)
    {
        int nTabListCount = TabListCount();
        if (nItem < 0 || nItem >= nTabListCount)
            return FALSE;
        
        int x = GetTotalArrowWidth();
        for (int i = 0; i < nItem; i++)
        {
            x += GetTabWidth(i);
            if (i != nTabListCount)
                x -=(m_iClientHeight / 2) + 2;
        }
        lpRect->left = x - m_iXOffset;
        lpRect->top = 0;
        lpRect->right = lpRect->left + GetTabWidth(nItem);
        lpRect->bottom = m_iClientHeight;
        return TRUE;
    }
    
    // ===========================================================================
    // Function name	: HitTest
    // Description	  : 
    // Return type		: virtual int 
    // Argument       : TCHITTESTINFO *pHitTestInfo
    // ===========================================================================
    virtual int HitTest(TCHITTESTINFO *pHitTestInfo)
    {
        int nTabListCount = TabListCount();
        // ignore hits on the buttons
        int iHitX = pHitTestInfo->pt.x;
        if (iHitX < GetTotalArrowWidth())
            return -1;
        
        // check if any tabs were hit
        int x = GetTotalArrowWidth() - m_iXOffset;
        for (int i = 0; i < nTabListCount; i++)
        {
            int iTabWidth = GetTabWidth(i);
            if (i != nTabListCount)
                iTabWidth -=(m_iClientHeight / 2) + 2;
            if ((x <= iHitX) && (iHitX <= x + iTabWidth))
                return i;
            x += iTabWidth;
        }
        
        // hit point is right of rightmost tab
        return -1;
    }
    
    // ===========================================================================
    virtual int GetCurSel() const {return m_iCurSel;}
    void SetTabItem(int nItem,LPCTSTR lpszText)
	{
    //CString* GetTabListItem(int nItem)
    //{
        int nIndex = 0;
        CTabList::iterator ListIterator;	
        for (ListIterator = m_TabList.begin(); ListIterator != m_TabList.end(); ++ListIterator)        
        {
            if (nIndex == nItem)
            {
				if((**ListIterator)!=lpszText){
					delete *ListIterator;
					*ListIterator=new CString(lpszText);
					//if(m_hWnd)
					m_iTotalTabWidth=GetTotalTabWidth();
				}
				return ;
                //return *ListIterator;
            }
            nIndex++;
        }
	}
    // ===========================================================================
    // Function name	: SetCurSel
    // Description	  : 
    // Return type		: virtual int 
    // Argument       : int nItem
    // ===========================================================================
    virtual int SetCurSel(int nItem)
    {
        if (nItem < 0 || nItem >= TabListCount())
            return -1;
        
        int iPrevSel = m_iCurSel;
        m_iCurSel = nItem;
        
        // test if we need to center on the selected tab
        CRect rcItem;
        if (GetItemRect(nItem, &rcItem))
        {
            // test if the tab is off on the left
            int iTotalArrowWidth = GetTotalArrowWidth();
            rcItem.left -= iTotalArrowWidth;
            if (rcItem.left < 0)
                m_iXOffset += rcItem.left;
            else
            {
                // test if the tab is off on the right
                rcItem.right -= iTotalArrowWidth;
                int iTabAreaWidth = m_iClientWidth - iTotalArrowWidth;
                if (rcItem.right > iTabAreaWidth)
                    m_iXOffset +=(rcItem.right - iTabAreaWidth);
            }
        }
        
        EnableButtons();
        InvalidateTabs();
        
        return iPrevSel;
    }
    
    // ===========================================================================
    virtual int GetItemCount() { return TabListCount();}
    
    // ===========================================================================
    // Function name	: SetTipText
    // Description	  : 
    // Return type		: virtual void 
    // Argument       : int nItem
    // Argument       : LPCTSTR lpszTabTip
    // ===========================================================================
    virtual void SetTipText(int nItem, LPCTSTR lpszTabTip)
    {
        int nTabListCount = TabListCount();
        if (nItem < nTabListCount)
        {
            for (int i = 0; i < nTabListCount; i++)
            {
                if (i == nItem)
                {
                    CString *pTipItem = GetTabTipListItem(nItem);
                    *pTipItem = lpszTabTip;
                    break;
                }
            }
        }
    }
    
    CString* GetTabTipListItem(int nItem)
    {
        int nIndex = 0;
        CTabList::iterator ListIterator;	
        for (ListIterator = m_TabTipList.begin(); ListIterator != m_TabTipList.end(); ++ListIterator)        
        {
            if (nIndex == nItem)
            {
                return *ListIterator;
            }
            nIndex++;
        }
        
        return NULL;
    }
    
    CString* GetTabListItem(int nItem)
    {
        int nIndex = 0;
        CTabList::iterator ListIterator;	
        for (ListIterator = m_TabList.begin(); ListIterator != m_TabList.end(); ++ListIterator)        
        {
            if (nIndex == nItem)
            {
                return *ListIterator;
            }
            nIndex++;
        }
        
        return NULL;
    }
    
    // ===========================================================================
    // Function name	: GetTipText
    // Description	  : 
    // Return type		: virtual CString 
    // Argument       : int nItem
    // ===========================================================================
    virtual CString GetTipText(int nItem)
    {
        int nTabListCount = TabListCount();
        CString strText;
        if (nItem < nTabListCount)
        {
            for (int i = 0; i < nTabListCount; i++)
            {
                if (i == nItem)
                {
                    strText = * (GetTabTipListItem(nItem));
                    break;
                }
            }
        }
        
        return strText;
    }
    
    // ===========================================================================
    // Function name	: Home
    // Description	  : 
    // Return type		: virtual void 
    // ===========================================================================
    virtual void Home()
    {
        m_iXOffset = 0;
        EnableButtons();
        InvalidateTabs();
    }
    
    // Overrides
    protected:
        
        // Implementation
    public:
        // ===========================================================================
        // Function name	: ~CFlatTabCtrl
        // Description	  : Destructor
        // Return type		: virtual 
        // ===========================================================================
        virtual ~CFlatTabCtrl()
        {
            if (m_pLeftButton)
                delete m_pLeftButton;
            if (m_pRightButton)
                delete m_pRightButton;
            if (m_pHomeButton)
                delete m_pHomeButton;
            if (m_pEndButton)
                delete m_pEndButton;
            
            // free HENHMETAFILE handles.
            if (m_hLeftArrow)
            {
                ::DeleteEnhMetaFile(m_hLeftArrow);
            }
            if (m_hLeftArrowDisabled)
            {
                ::DeleteEnhMetaFile(m_hLeftArrowDisabled);
            }
            if (m_hRightArrow)
            {
                ::DeleteEnhMetaFile(m_hRightArrow);
            }
            if (m_hRightArrowDisabled)
            {
                ::DeleteEnhMetaFile(m_hRightArrowDisabled);
            }
            if (m_hHomeArrow)
            {
                ::DeleteEnhMetaFile(m_hHomeArrow);
            }
            if (m_hHomeArrowDisabled)
            {
                ::DeleteEnhMetaFile(m_hHomeArrowDisabled);
            }
            if (m_hEndArrow)
            {
                ::DeleteEnhMetaFile(m_hEndArrow);
            }
            if (m_hEndArrowDisabled)
            {
                ::DeleteEnhMetaFile(m_hEndArrowDisabled);
            }
            
            CTabList::iterator ListIterator;	
            for (ListIterator = m_TabList.begin(); ListIterator != m_TabList.end(); ++ListIterator)        
            {		
                CString* pItem = *ListIterator;
                if (pItem)
                    delete pItem;
            }
            
            CTabList::iterator ListIterator2;	
            for (ListIterator2 = m_TabTipList.begin(); ListIterator2 != m_TabTipList.end(); ++ListIterator2)        
            {		
                CString* pItem = *ListIterator2;
                if (pItem)
                    delete pItem;
            }
            
            // fix potential resource leak - KStowell - 10-21-99
            if (m_Font)
                m_Font.DeleteObject();
            if (m_BoldFont)
                m_BoldFont.DeleteObject();
        }

        int GetVisibleTabWidth()
        {
            return m_iTotalTabWidth + GetTotalArrowWidth() + 1;
        }
        
        // Generated message map functions
    protected:
        // ===========================================================================
        // Function name	: OnPaint
        // Description	  : 
        // Return type		: LRESULT 
        // Argument       : UINT Msg
        // Argument       : WPARAM wParam
        // Argument       : LPARAM lParam
        // Argument       : BOOL& bHandled
        // ===========================================================================
        LRESULT OnPaint(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
        {
            CPaintDC dc(m_hWnd); // device context for painting
            
            CBrush brush;
            brush.CreateSysColorBrush(COLOR_3DFACE);
            CPen penBlack;
            penBlack.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
            
            // draw the black line along the left of the control
            HPEN pOldPen = dc.SelectPen(penBlack);
            dc.MoveTo(0, 0);
            dc.LineTo(0, m_iClientHeight);
            
            // draw the black line along the top/bottom of the control
            int iTotalArrowWidth = GetTotalArrowWidth();
            int iEndOfTab = iTotalArrowWidth + m_iTotalTabWidth - m_iXOffset;
            dc.MoveTo(iEndOfTab, m_bTabsOnBottom ? 0 : m_iClientHeight - 1);
            dc.LineTo(m_iClientWidth, m_bTabsOnBottom ? 0 : m_iClientHeight - 1);
            

            // fill the empty background area
            int iFillHeight = m_iClientHeight - (m_bTabsOnBottom ? 0 : 1);
            CRect rcBack(iEndOfTab, m_bTabsOnBottom ? 1 : 0, m_iClientWidth, iFillHeight);
            dc.FillRect(rcBack, brush);
            
            // create a bitmap of all the tabs
            CDC dcMem;
            dcMem.CreateCompatibleDC(NULL);
            CBitmap bitmap;
            bitmap.CreateCompatibleBitmap(dc.m_hDC, m_iTotalTabWidth, m_iClientHeight);
            HBITMAP pOldBitmap = dcMem.SelectBitmap(bitmap);
            HBRUSH pOldBrush = dcMem.SelectBrush(brush);
            CRect rc(0, m_bTabsOnBottom ? 1 : 0, m_iTotalTabWidth, iFillHeight);
            dcMem.FillRect(rc, brush);
            
            int iOverlap = (m_iClientHeight / 2) + 2;
            int x = 0;
            int iSelX = 0;
            for (int i = 0; i < TabListCount(); i++)
            {
                if (i != m_iCurSel)
                    x += DrawTab(dcMem, x, 0, m_iClientHeight, false, m_bTabsOnBottom, * (GetTabListItem(i))) - iOverlap;
                else
                {
                    iSelX = x;
                    x += GetTabWidth(i) - iOverlap;
                }
            }
            if (m_iCurSel >= 0 && TabListCount())
                DrawTab(dcMem, iSelX, 0, m_iClientHeight, true, m_bTabsOnBottom, * (GetTabListItem(m_iCurSel)));
            
            // blit the bitmap onto the control
            dc.BitBlt(iTotalArrowWidth, 0, m_iClientWidth - iTotalArrowWidth, m_iClientHeight, dcMem.m_hDC, m_iXOffset, 0, SRCCOPY);
            
            // draw the final line on the right
            dc.MoveTo(m_iClientWidth-1, 0);
            dc.LineTo(m_iClientWidth-1, m_iClientHeight);

            // fix potential resource leak - KStowell - 10-21-99.
            //dcMem.SelectPen(pOldPen);
            dc.SelectPen(pOldPen);
            dcMem.SelectBitmap(pOldBitmap);
            dcMem.SelectBrush(pOldBrush);
            dcMem.DeleteDC();
            brush.DeleteObject();
            penBlack.DeleteObject();
            bitmap.DeleteObject();
            
            return 0;
        }
        
        // ===========================================================================
        // Function name	: OnLButtonDown
        // Description	  : 
        // Return type		: LRESULT 
        // Argument       : UINT Msg
        // Argument       : WPARAM wParam
        // Argument       : LPARAM lParam
        // Argument       : BOOL& bHandled
        // ===========================================================================
        LRESULT OnLButtonDown(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
        {
            TCHITTESTINFO test;
            WORD x, y;
            x = LOWORD(lParam);
            y = HIWORD(lParam);
            CPoint point(x, y);
            test.pt = point;
            int iTab = HitTest(&test);
            if ((iTab != -1) && (iTab != m_iCurSel))
            {
                // warn parent that the selection is about to change
                int id = GetDlgCtrlID();
                NMHDR hdr;
                hdr.hwndFrom = m_hWnd;
                hdr.idFrom = id;
                hdr.code = TCN_SELCHANGING;
                CWindow wnd(GetParent());
                
                if (wnd.SendMessage(WM_NOTIFY, id, (LPARAM)&hdr) == 0)
                {
                    // parent has given permission for the selection to change
                    SetCurSel(iTab);
                    InvalidateTabs();
                    
                    // notify parent that the selection has changed
                    hdr.hwndFrom = m_hWnd;
                    hdr.idFrom = id;
                    hdr.code = TCN_SELCHANGE;
                    wnd.SendMessage(WM_NOTIFY, id, (LPARAM)&hdr);
                }
            }
            
            return 0;
        }
        
        // ===========================================================================
        // Function name	: OnSize
        // Description	  : 
        // Return type		: LRESULT 
        // Argument       : UINT Msg
        // Argument       : WPARAM wParam
        // Argument       : LPARAM lParam
        // Argument       : BOOL& bHandled
        // ===========================================================================
        LRESULT OnSize(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
        {
            WORD cx, cy;
            cx = LOWORD(lParam);
            cy = HIWORD(lParam);
            
            // TODO: Add your message handler code here
            if (m_bHasHomeEnd)
            {
                m_pHomeButton->MoveWindow(1, 0, 16, cy);
                m_pLeftButton->MoveWindow(17, 0, 16, cy);
                m_pRightButton->MoveWindow(33, 0, 16, cy);
                m_pEndButton->MoveWindow(49, 0, 16, cy);
            }
            else 
            {
                if (m_bHasArrows)
                {
                    // TCC: ADDED to handle situations where ther are no arrows
                    m_pLeftButton->MoveWindow(1, 0, 16, cy);
                    m_pRightButton->MoveWindow(17, 0, 16, cy);
                }
            }
            
            m_iClientWidth  = cx;
            m_iClientHeight = cy;
            
            CreateTabFont(cy);
            
            m_iTotalTabWidth = GetTotalTabWidth();
            EnableButtons();
            return 0;
        }
        
        // ===========================================================================
        // Function name	: OnCreate
        // Description	  : 
        // Return type		: LRESULT 
        // Argument       : UINT Msg
        // Argument       : WPARAM wParam
        // Argument       : LPARAM lParam
        // Argument       : BOOL& bHandled
        // ===========================================================================
        LRESULT OnCreate(UINT Msg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
        {    
            LPCREATESTRUCT lpCreateStruct = (LPCREATESTRUCT)lParam;
            
            // TODO: Add your specialized creation code here
            if (lpCreateStruct->style & FTS_BOTTOM)
            {
                m_bTabsOnBottom = true;
            }
            
            if (lpCreateStruct->style & TCS_TOOLTIPS)
            {
                m_ToolTip.Create(m_hWnd);
                m_ToolTip.Activate(TRUE);
                m_ToolTip.AddTool(m_hWnd, _T("Flat Tab Tool Tip"));
                m_ToolTip.SendMessage(TTM_SETMAXTIPWIDTH, 0, SHRT_MAX); // Allow multiline tooltips
            }
            
            if (lpCreateStruct->style & FTS_HASARROWS)
            {
                m_bHasArrows = true;
                POINT points[4];
                points[0].x = 100;
                points[0].y = 0;
                points[1].x = 0;
                points[1].y = 50;
                points[2].x = 100;
                points[2].y = 100;
                points[3].x = 100;
                points[3].y = 0;
                
                ////////////////////////////////////////////////////////////////////////////////
                // This portion of the code was re-written to help eliminate a nasty resource
                // leak. It would seem that the CMetaFileDC was not freeing the GDI resources
                // completely whenever CMetaFileDC::CloseEnhanced() was called. This may be due 
                // to a BUG in MFC or perhaps this class, in any event, this seems to resolve 
                // the issue. - KStowell - 10-23-99.
                ////////////////////////////////////////////////////////////////////////////////
                HPEN   hPen			  = ::CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
                HPEN   hPenDisabled   = ::CreatePen(PS_SOLID, 1, RGB(128, 128, 128));
                HBRUSH hBrush		  = ::CreateSolidBrush(RGB(0, 0, 0));
                HBRUSH hBrushDisabled = ::CreateSolidBrush(RGB(128, 128, 128));;
                
                // create left arrow metafile
                HDC hMetaDC	     = ::CreateEnhMetaFile(NULL, NULL, NULL, NULL);
                HPEN hOldPen     = (HPEN)::SelectObject(hMetaDC, hPen);
                HBRUSH hOldBrush = (HBRUSH)::SelectObject(hMetaDC, hBrush);
                ::Polygon(hMetaDC, points, 4);
                ::SelectObject(hMetaDC, hOldPen);
                ::SelectObject(hMetaDC, hOldBrush);
                m_hLeftArrow = ::CloseEnhMetaFile(hMetaDC);
                
                // create disabled left arrow metafile
                hMetaDC	  = ::CreateEnhMetaFile(NULL, NULL, NULL, NULL);
                hOldPen   = (HPEN)::SelectObject(hMetaDC, hPenDisabled);
                hOldBrush = (HBRUSH)::SelectObject(hMetaDC, hBrushDisabled);
                ::Polygon(hMetaDC, points, 4);
                ::SelectObject(hMetaDC, hOldPen);
                ::SelectObject(hMetaDC, hOldBrush);
                m_hLeftArrowDisabled = ::CloseEnhMetaFile(hMetaDC);
                
                points[0].x = 0;
                points[0].y = 0;
                points[1].x = 100;
                points[1].y = 50;
                points[2].x = 0;
                points[2].y = 100;
                points[3].x = 0;
                points[3].y = 0;
                
                // create right arrow metafile
                hMetaDC	  = ::CreateEnhMetaFile(NULL, NULL, NULL, NULL);
                hOldPen   = (HPEN)::SelectObject(hMetaDC, hPen);
                hOldBrush = (HBRUSH)::SelectObject(hMetaDC, hBrush);
                ::Polygon(hMetaDC, points, 4);
                ::SelectObject(hMetaDC, hOldPen);
                ::SelectObject(hMetaDC, hOldBrush);
                m_hRightArrow = ::CloseEnhMetaFile(hMetaDC);
                
                // create disabled right arrow metafile
                hMetaDC	  = ::CreateEnhMetaFile(NULL, NULL, NULL, NULL);
                hOldPen   = (HPEN)::SelectObject(hMetaDC, hPenDisabled);
                hOldBrush = (HBRUSH)::SelectObject(hMetaDC, hBrushDisabled);
                ::Polygon(hMetaDC, points, 4);
                ::SelectObject(hMetaDC, hOldPen);
                ::SelectObject(hMetaDC, hOldBrush);
                m_hRightArrowDisabled = ::CloseEnhMetaFile(hMetaDC);
                
                CRect rcButton(0, 0, 16, 16);
                m_pLeftButton = new CMetaFileButton;
                m_pLeftButton->Create(m_hWnd, rcButton, NULL, WS_CHILD | WS_VISIBLE | WS_DISABLED | BS_OWNERDRAW, 0, IDC_LEFTBUTTON);
                m_pLeftButton->SetMetaFiles(m_hLeftArrow, 0, 0, m_hLeftArrowDisabled);
                
                m_pRightButton = new CMetaFileButton;
                m_pRightButton->Create(m_hWnd, rcButton, NULL, WS_CHILD | WS_VISIBLE | WS_DISABLED | BS_OWNERDRAW, 0, IDC_RIGHTBUTTON);
                m_pRightButton->SetMetaFiles(m_hRightArrow, 0, 0, m_hRightArrowDisabled);
                
                if (lpCreateStruct->style & FTS_HASHOMEEND)
                {
                    m_bHasHomeEnd = true;
                    
                    // Setup the Home arrow button
                    points[0].x = 100;
                    points[0].y = 0;
                    points[1].x = 0;
                    points[1].y = 50;
                    points[2].x = 100;
                    points[2].y = 100;
                    points[3].x = 100;
                    points[3].y = 0;
                    
                    // create right arrow metafile
                    HDC hMetaDC	     = ::CreateEnhMetaFile(NULL, NULL, NULL, NULL);
                    HPEN hOldPen     = (HPEN)::SelectObject(hMetaDC, hPen);
                    HBRUSH hOldBrush = (HBRUSH)::SelectObject(hMetaDC, hBrush);
                    ::Polygon(hMetaDC, points, 4);
                    ::MoveToEx(hMetaDC, 0, 0, NULL);
                    ::LineTo(hMetaDC, 0, 100);
                    ::SelectObject(hMetaDC, hOldPen);
                    ::SelectObject(hMetaDC, hOldBrush);
                    m_hHomeArrow = ::CloseEnhMetaFile(hMetaDC);
                    
                    // create disabled right arrow metafile
                    hMetaDC	  = ::CreateEnhMetaFile(NULL, NULL, NULL, NULL);
                    hOldPen   = (HPEN)::SelectObject(hMetaDC, hPenDisabled);
                    hOldBrush = (HBRUSH)::SelectObject(hMetaDC, hBrushDisabled);
                    ::Polygon(hMetaDC, points, 4);
                    ::MoveToEx(hMetaDC, 0, 0, NULL);
                    ::LineTo(hMetaDC, 0, 100);
                    ::SelectObject(hMetaDC, hOldPen);
                    ::SelectObject(hMetaDC, hOldBrush);
                    m_hHomeArrowDisabled = ::CloseEnhMetaFile(hMetaDC);
                    
                    m_pHomeButton = new CMetaFileButton;
                    m_pHomeButton->Create(m_hWnd, rcButton, NULL, WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, IDC_HOMEBUTTON);
                    m_pHomeButton->SetMetaFiles(m_hHomeArrow, 0, 0, m_hHomeArrowDisabled);
                    
                    // Setup the End arrow button
                    points[0].x = 0;
                    points[0].y = 0;
                    points[1].x = 100;
                    points[1].y = 50;
                    points[2].x = 0;
                    points[2].y = 100;
                    points[3].x = 0;
                    points[3].y = 0;
                    
                    // create right arrow metafile
                    hMetaDC	  = ::CreateEnhMetaFile(NULL, NULL, NULL, NULL);
                    hOldPen   = (HPEN)::SelectObject(hMetaDC, hPen);
                    hOldBrush = (HBRUSH)::SelectObject(hMetaDC, hBrush);
                    ::Polygon(hMetaDC, points, 4);
                    ::MoveToEx(hMetaDC, 100, 0, NULL);
                    ::LineTo(hMetaDC, 100, 100);
                    ::SelectObject(hMetaDC, hOldPen);
                    ::SelectObject(hMetaDC, hOldBrush);
                    m_hEndArrow = ::CloseEnhMetaFile(hMetaDC);
                    
                    // create disabled right arrow metafile
                    hMetaDC	  = ::CreateEnhMetaFile(NULL, NULL, NULL, NULL);
                    hOldPen   = (HPEN)::SelectObject(hMetaDC, hPenDisabled);
                    hOldBrush = (HBRUSH)::SelectObject(hMetaDC, hBrushDisabled);
                    ::Polygon(hMetaDC, points, 4);
                    ::MoveToEx(hMetaDC, 100, 0, NULL);
                    ::LineTo(hMetaDC, 100, 100);
                    ::SelectObject(hMetaDC, hOldPen);
                    ::SelectObject(hMetaDC, hOldBrush);
                    m_hEndArrowDisabled = ::CloseEnhMetaFile(hMetaDC);
                    
                    m_pEndButton = new CMetaFileButton;
                    m_pEndButton->Create(m_hWnd, rcButton, NULL, WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, IDC_ENDBUTTON);
                    m_pEndButton->SetMetaFiles(m_hEndArrow, 0, 0, m_hEndArrowDisabled);
                    
                    m_pHomeButton->MoveWindow(1, 0, 16, 16);
                    m_pLeftButton->MoveWindow(17, 0, 16, 16);
                    m_pRightButton->MoveWindow(33, 0, 16, 16);
                    m_pEndButton->MoveWindow(49, 0, 16, 16);
                }
                else 
                {
                    m_pLeftButton->MoveWindow(1, 0, 16, 16);
                    m_pRightButton->MoveWindow(17, 0, 16, 16);
                }
                
                // fix potential resource leak - KStowell - 10-23-99
                ::DeleteObject(hPen);
                ::DeleteObject(hPenDisabled);
                ::DeleteObject(hBrush);
                ::DeleteObject(hBrushDisabled);
      }
      
      return 0;
  }
  
  // ===========================================================================
  // Function name	: OnLeftArrow
  // Description	  : 
  // Return type		: LRESULT 
  // Argument       : WORD wNotifyCode
  // Argument       : WORD wID
  // Argument       : HWND hWndCtl
  // Argument       : BOOL& bHandled
  // ===========================================================================
  LRESULT OnLeftArrow(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
  {
      // Move the tabs right, ensuring that we move right by one
      // whole tab each time ala Microsoft Access
      
      CPoint Point(GetTotalArrowWidth() + 1, 1);
      
      TCHITTESTINFO test;
      test.pt = Point;
      int iTab = HitTest(&test);
      
      if (iTab != -1)
      {
          m_iXOffset = 0;
          for (int iLoop = 0; iLoop <= iTab - 1; iLoop++)
          {
              RECT rect;
              if (GetItemRect(iLoop, &rect))
              {
                  m_iXOffset += rect.left;
              }
          }
          m_iXOffset -= GetTotalArrowWidth();
          
          EnableButtons();
          InvalidateTabs();
      }
      
      return 0;
  }
  
  // ===========================================================================
  // Function name	: OnRightArrow
  // Description	  : 
  // Return type		: LRESULT 
  // Argument       : WORD wNotifyCode
  // Argument       : WORD wID
  // Argument       : HWND hWndCtl
  // Argument       : BOOL& bHandled
  // ===========================================================================
  LRESULT OnRightArrow(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
  {
      // Move the tabs left, ensuring that we move left by one
      // whole tab each time ala Microsoft Access
      
      CPoint Point(GetTotalArrowWidth() + 1, 1);
      
      TCHITTESTINFO test;
      test.pt = Point;
      int iTab = HitTest(&test);
      
      if (iTab != -1)
      {
          m_iXOffset = 0;
          for (int iLoop = 0; iLoop <= iTab + 1; iLoop++)
          {
              RECT rect;
              if (GetItemRect(iLoop, &rect))
              {
                  m_iXOffset += rect.left;
              }
          }
          m_iXOffset -= GetTotalArrowWidth();
          
          EnableButtons();
          InvalidateTabs();
      }
      
      return 0;
  }
  
  // ===========================================================================
  // Function name	: OnHomeArrow
  // Description	  : 
  // Return type		: LRESULT 
  // Argument       : WORD wNotifyCode
  // Argument       : WORD wID
  // Argument       : HWND hWndCtl
  // Argument       : BOOL& bHandled
  // ===========================================================================
  LRESULT OnHomeArrow(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
  {
      m_iXOffset = 0;
      EnableButtons();
      InvalidateTabs();
      return 0;
  }
  
  // ===========================================================================
  // Function name	: OnEndArrow
  // Description	  : 
  // Return type		: LRESULT 
  // Argument       : WORD wNotifyCode
  // Argument       : WORD wID
  // Argument       : HWND hWndCtl
  // Argument       : BOOL& bHandled
  // ===========================================================================
  LRESULT OnEndArrow(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
  {
      // TODO: Add your control notification handler code here
      m_iXOffset = m_iTotalTabWidth + GetTotalArrowWidth() - m_iClientWidth + 1;
      EnableButtons();
      InvalidateTabs();
      return 0;
  }
  
private:
    
    // ===========================================================================
    // Function name	: CreateTabFont
    // Description	  : 
    // Return type		: void 
    // Argument       : const int iTabHeight
    // ===========================================================================
    void CreateTabFont(const int iTabHeight)
    {
        int iHeight = - (iTabHeight - 6);
        
        if (m_Font)
        {
            m_Font.DeleteObject();
            m_Font = NULL;
        }
        m_Font.CreateFont(iHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
            OUT_STROKE_PRECIS, CLIP_STROKE_PRECIS, PROOF_QUALITY, FF_SWISS, NULL);
        
        if (m_BoldFont)
            m_BoldFont.DeleteObject();
        m_BoldFont.CreateFont(iHeight, 0, 0, 0, FW_EXTRABOLD, FALSE, FALSE, FALSE, ANSI_CHARSET,
            OUT_STROKE_PRECIS, CLIP_STROKE_PRECIS, PROOF_QUALITY, FF_SWISS, NULL);
    }
    
    // ===========================================================================
    // Function name	: GetTabWidth
    // Description	  : 
    // Return type		: int 
    // Argument       : const int nItem
    // ===========================================================================
    int GetTabWidth(const int nItem)
    {
        CDC dc;
        dc.CreateCompatibleDC(NULL);
        HFONT pOldFont = dc.SelectFont(m_BoldFont);
        CSize size;
        dc.GetTextExtent(* (GetTabListItem(nItem)), -1, &size);
        
        // fix potential resource leak - KStowell - 10-20-99.
        dc.SelectFont(pOldFont);
        dc.DeleteDC();
        
        return size.cx + m_iClientHeight + (m_iClientHeight / 2);
    }
    
    // ===========================================================================
    // Function name	: GetTotalArrowWidth
    // Description	  : 
    // Return type		: int 
    // Argument       : void
    // ===========================================================================
    int GetTotalArrowWidth(void) const
    {
        int iWidth = 1;
        if (m_bHasArrows)
            iWidth += 32;
        if (m_bHasHomeEnd)
            iWidth += 32;
        return iWidth;
    }
    // ===========================================================================
    // Function name	: GetTotalTabWidth
    // Description	  : 
    // Return type		: int 
    // Argument       : void
    // ===========================================================================
    int GetTotalTabWidth(void)
    {
        int iWidth = 0;
        int nTabListCount = TabListCount();
        for (int i = 0; i < nTabListCount; i++)
        {
            iWidth += GetTabWidth(i);
            if (i != nTabListCount - 1)
            {
                iWidth -=(m_iClientHeight / 2);
            }
        }
        return iWidth + 2;  
    }
    
    // ===========================================================================
    // Function name	: InvalidateTabs
    // Description	  : 
    // Return type		: void 
    // Argument       : void
    // ===========================================================================
    void InvalidateTabs(void)
    {
        if (m_hWnd)
        {
            // invalidate the visible tab area
            // to minimise flicker - don't erase the background
            CRect rcTabs;
            
            rcTabs.left = GetTotalArrowWidth();
            rcTabs.top = 0;
            rcTabs.right = rcTabs.left + (m_iTotalTabWidth - m_iXOffset);
            rcTabs.bottom = m_iClientHeight;
            InvalidateRect(&rcTabs, false);
            // invalidate the blank area to the right of the tabs
            if (rcTabs.right < m_iClientWidth)
            {
                rcTabs.left = rcTabs.right;
                rcTabs.right = m_iClientWidth;
                InvalidateRect(&rcTabs, true);
            }
        }
    }
    
    // ===========================================================================
    // Function name	: EnableButtons
    // Description	  : 
    // Return type		: void 
    // Argument       : void
    // ===========================================================================
    void EnableButtons(void)
    {
        if (m_bHasArrows)  // TCC: ADDED to handle situations where ther are no arrows
        {
            m_pLeftButton->EnableWindow(m_iXOffset);
            if (m_iClientWidth + m_iXOffset < m_iTotalTabWidth + GetTotalArrowWidth() + 1)
                m_pRightButton->EnableWindow(true);
            else
                m_pRightButton->EnableWindow(false);
            if (m_bHasHomeEnd)
            {
                m_pHomeButton->EnableWindow(m_iXOffset);
                m_pEndButton->EnableWindow(m_pRightButton->IsWindowEnabled());
            }
        }
    }
    
    // ===========================================================================
    // Function name	: DrawTab
    // Description	    : 
    // Return type		: int 
    // Argument         : CDC &dc
    // Argument         : const int x
    // Argument         : const int y
    // Argument         : const int iHeight
    // Argument         : bool bSelected
    // Argument         : bool bBottom
    // Argument         : LPCTSTR lpszTab
    // ===========================================================================
    int DrawTab(CDC &dc, const int x, const int y, const int iHeight, bool bSelected, bool bBottom, LPCTSTR lpszTab) const
    {
        HFONT pOldFont = dc.SelectFont(m_BoldFont);
        CSize size;
        dc.GetTextExtent(lpszTab, -1, &size);
        CRect rcText;
        rcText.left = iHeight + x - 4;
        rcText.top = 2 + y;
        rcText.right = size.cx + iHeight + x - 4;
        rcText.bottom = size.cy + 2 + y;
        
        int iHalf = iHeight / 2;
        int iWidth = iHeight + iHalf;
        
        COLORREF crBack;
        COLORREF crFore;
        if (bSelected)
        {
            crBack = GetSysColor(COLOR_WINDOW);
            crFore = GetSysColor(COLOR_WINDOWTEXT);
        }
        else 
        {
            crBack = GetSysColor(COLOR_3DFACE);
            crFore = GetSysColor(COLOR_BTNTEXT);
        }
        
        CPen penOutline;
        penOutline.CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT));
        CPen penShadow;
        penShadow.CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW));
        CBrush brush;
        brush.CreateSolidBrush(crBack);
        
        POINT points[4];
        points[0].x = x;
        points[0].y = iHeight + y - 1;
        points[1].x = iHalf + x;
        points[1].y = y;
        points[2].x = size.cx + iHeight + x;
        points[2].y = y;
        points[3].x = size.cx + iWidth + x;
        points[3].y = iHeight + y - 1;
        
        if (bBottom)
        {
            // swap vertical coordinates
            points[0].y = points[1].y;
            points[2].y = points[3].y;
            points[1].y = points[2].y;
            points[3].y = points[0].y;
        }
        
        HPEN pOldPen = dc.SelectPen(penOutline);
        dc.SetBkColor(crBack);
        HBRUSH pOldBrush = dc.SelectBrush(brush);
        dc.Polygon(points, 4);
        dc.SelectFont(bSelected ? m_BoldFont : m_Font);
        dc.SetTextColor(crFore);
        dc.DrawText(lpszTab, -1, rcText, DT_CENTER);
        dc.SelectPen(penShadow);
        
        if (bSelected)
        {
            dc.MoveTo(iHalf + x + 1, points[1].y);
            dc.LineTo(size.cx + iHeight + x, points[1].y);
            //CPen penBack;
            //penBack.CreatePen(PS_SOLID, 1, crBack);
            const HPEN hPenOld=dc.SelectPen(CreatePen(PS_SOLID, 1, crBack));
            dc.MoveTo(x + 1, points[0].y);
            dc.LineTo(size.cx + iWidth + x, points[0].y);
			DeleteObject(dc.SelectPen(hPenOld));
        }
        else 
        {
            dc.MoveTo(iHalf + x + 1, points[1].y);
            dc.LineTo(size.cx + iHeight + x - 1, points[1].y);
            dc.LineTo(size.cx + iWidth + x - 1, points[0].y);
        }
        
        // fix potential resource leak - KStowell - 10-21-99
        dc.SelectFont(pOldFont);
        dc.SelectPen(pOldPen);
        dc.SelectBrush(pOldBrush);
        penOutline.DeleteObject();
        penShadow.DeleteObject();
        brush.DeleteObject();
        
        return size.cx + iWidth;
    }
    
    bool m_bHasArrows;
    bool m_bHasHomeEnd;
    bool m_bTabsOnBottom;
    int m_iCurSel;
    int m_iClientWidth;
    int m_iClientHeight;
    int m_iTotalTabWidth;
    int m_iXOffset;
    CTabList m_TabList;
    CTabList m_TabTipList;
    CFont m_Font;
    CFont m_BoldFont;
    CMetaFileButton *m_pLeftButton;
    CMetaFileButton *m_pRightButton;
    CMetaFileButton *m_pHomeButton;
    CMetaFileButton *m_pEndButton;
    HENHMETAFILE m_hLeftArrow;
    HENHMETAFILE m_hLeftArrowDisabled;
    HENHMETAFILE m_hRightArrow;
    HENHMETAFILE m_hRightArrowDisabled;
    HENHMETAFILE m_hHomeArrow;
    HENHMETAFILE m_hHomeArrowDisabled;
    HENHMETAFILE m_hEndArrow;
    HENHMETAFILE m_hEndArrowDisabled;
    
    CToolTipCtrl m_ToolTip;
};


#endif

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
China China
My name is Yanxueming,i live in Chengdu China.Graduated from UESTC in 1999.

Comments and Discussions