www.pudn.com > mu_explorer.rar > CJFlatTabCtrl.cpp


// FlatTab.cpp : implementation 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  
//					 
// ==========================================================================   
 
// 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:52p $ 
 * $Revision: 11 $ 
 * $Archive: /CodeJock/CJLibrary/CJFlatTabCtrl.cpp $ 
 * 
 * $History: CJFlatTabCtrl.cpp $ 
 *  
 * *****************  Version 11  ***************** 
 * User: Kirk Stowell Date: 10/26/99   Time: 10:52p 
 * Updated in $/CodeJock/CJLibrary 
 * Fixed bug with flat tab where in some cases tabs were not completely 
 * painted. 
 *  
 * *****************  Version 10  ***************** 
 * User: Kirk Stowell Date: 10/25/99   Time: 10:52p 
 * Updated in $/CodeJock/CJLibrary 
 * Modified resource include for static builds. 
 *  
 * *****************  Version 9  ***************** 
 * User: Kirk Stowell Date: 10/24/99   Time: 12:15a 
 * Updated in $/CodeJock/CJLibrary 
 * Fixed minor bug with tab font creation. 
 *  
 * *****************  Version 8  ***************** 
 * User: Kirk Stowell Date: 10/24/99   Time: 12:01a 
 * Updated in $/CodeJock/CJLibrary 
 * Fixed potential resource and memory leak problems. 
 *  
 * *****************  Version 7  ***************** 
 * User: Kirk Stowell Date: 10/14/99   Time: 11:37p 
 * Updated in $/CodeJock/CJLibrary 
 * Fixed bug with flat tab control that would have kept one from disabling 
 * the l/r arrow buttons, which caused an exception to be thrown. Thanks 
 * to Ted Crow [crowtc@surge.net] for help with this. 
 *  
 * *****************  Version 6  ***************** 
 * User: Kirk Stowell Date: 10/14/99   Time: 12:25p 
 * Updated in $/CodeJock/CJLibrary 
 * Added source control history to file header. 
 * 
 ***************************************************************************/ 
///////////////////////////////////////////////////////////////////////////// 
 
#include "stdafx.h" 
#include "CJResource.h" 
#include "CJFlatTabCtrl.h" 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
///////////////////////////////////////////////////////////////////////////// 
// CCJFlatTabCtrl 
 
CCJFlatTabCtrl::CCJFlatTabCtrl() 
{ 
	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; 
	m_nMaxTabWidth		  =150; 
} 
 
CCJFlatTabCtrl::~CCJFlatTabCtrl() 
{ 
	_delete(m_pLeftButton); 
	_delete(m_pRightButton); 
	_delete(m_pHomeButton); 
	_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); 
	} 
	while(!m_TabList.IsEmpty()) { 
		CString* pItem = m_TabList.RemoveTail(); 
		_delete(pItem); 
	} 
	while(!m_TabTipList.IsEmpty()) { 
		CString* pItem = m_TabTipList.RemoveTail(); 
		_delete(pItem); 
	} 
 
	// fix potential resource leak - KStowell - 10-21-99 
	m_Font.DeleteObject(); 
	m_BoldFont.DeleteObject(); 
} 
 
void CCJFlatTabCtrl::CreateTabFont(const int iTabHeight) 
{ 
	int iHeight = -(iTabHeight - 6); 
 
	m_Font.DeleteObject(); 
	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); 
 
	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); 
} 
 
int CCJFlatTabCtrl::GetTabWidth(const int nItem) const 
{ 
	CDC dc; 
	dc.CreateCompatibleDC(NULL); 
	CFont* pOldFont = dc.SelectObject((CFont*)&m_BoldFont); 
	CSize size(dc.GetTextExtent(*m_TabList.GetAt(m_TabList.FindIndex(nItem)))); 
	if(size.cx>m_nMaxTabWidth) 
		size.cx=m_nMaxTabWidth; 
	// fix potential resource leak - KStowell - 10-20-99. 
	dc.SelectObject(pOldFont); 
	dc.DeleteDC(); 
	 
	return size.cx + m_iClientHeight + (m_iClientHeight / 2); 
} 
 
int CCJFlatTabCtrl::GetTotalTabWidth(void) const 
{ 
	int iWidth = 0; 
	for (int i = 0; i < m_TabList.GetCount(); i++) 
	{ 
		iWidth += GetTabWidth(i); 
		if (i != m_TabList.GetCount()-1) { 
			iWidth -= (m_iClientHeight / 2); 
		} 
	} 
	return iWidth + 2; 
} 
 
int CCJFlatTabCtrl::GetTotalArrowWidth(void) const 
{ 
	int iWidth = 1; 
	if (m_bHasArrows) 
		iWidth += 32; 
	if (m_bHasHomeEnd) 
		iWidth += 32; 
	return iWidth; 
} 
 
void CCJFlatTabCtrl::InvalidateTabs(void) 
{ 
	if (GetSafeHwnd()) 
	{ 
		// 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); 
		} 
	} 
} 
 
void CCJFlatTabCtrl::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()); 
		} 
	} 
} 
 
int CCJFlatTabCtrl::DrawTab(CDC &dc, const int x, const int y, const int iHeight, bool bSelected, bool bBottom, LPCTSTR lpszTab) const 
{ 
	CFont* pOldFont = dc.SelectObject((CFont*)&m_BoldFont); 
	CSize size(dc.GetTextExtent(lpszTab));	 
	if(size.cx>m_nMaxTabWidth) 
	{ 
		size.cx=m_nMaxTabWidth; 
	} 
	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(PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT)); 
	CPen penShadow(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; 
	} 
 
	CPen* pOldPen = dc.SelectObject(&penOutline); 
	dc.SetBkColor(crBack); 
	CBrush* pOldBrush = dc.SelectObject(&brush); 
	dc.Polygon(points, 4); 
	dc.SelectObject(bSelected ? (CFont*)&m_BoldFont : (CFont*)&m_Font); 
	dc.SetTextColor(crFore); 
	//dc.DrawText(lpszTab, rcText, DT_CENTER);	 
	dc.DrawText(lpszTab, rcText, DT_LEFT);	 
	dc.SelectObject(&penShadow); 
	 
	if (bSelected) { 
		dc.MoveTo(iHalf + x + 1, points[1].y); 
		dc.LineTo(size.cx + iHeight + x, points[1].y); 
		CPen penBack(PS_SOLID, 1, crBack); 
		dc.SelectObject(&penBack); 
		dc.MoveTo(x + 1, points[0].y); 
		dc.LineTo(size.cx + iWidth + x, points[0].y); 
	} 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.SelectObject(pOldFont); 
	dc.SelectObject(pOldPen); 
	dc.SelectObject(pOldBrush); 
	penOutline.DeleteObject(); 
	penShadow.DeleteObject(); 
	brush.DeleteObject(); 
 
	return size.cx + iWidth; 
} 
 
BOOL CCJFlatTabCtrl::InsertItem(const int nItem, LPCTSTR lpszItem,CWnd* pWnd) 
{ 
	if (nItem < 0 || nItem > m_TabList.GetCount()) 
		return -1; 
 
	// add tab to our list 
	CString* pItem = new CString; 
	ASSERT(pItem); 
 
	*pItem = lpszItem; 
	m_TabList.AddTail(pItem); 
	m_TabWndList.AddTail(pWnd); 
    // PGB - Add the tabs tip text to the list 
 
	CString* pTipItem = new CString; 
	ASSERT(pTipItem); 
 
	*pTipItem = lpszItem; 
	m_TabTipList.AddTail(pTipItem); 
 
	if (m_TabList.GetCount() == 1) 
		m_iCurSel = 0; 
 
   	m_iTotalTabWidth = GetTotalTabWidth();      // PGB - Recalculate the total tab width now we have added a new item 
	 
    EnableButtons(); 
	InvalidateTabs(); 
 
	return nItem; 
} 
 
BOOL CCJFlatTabCtrl::DeleteItem(int nItem) 
{ 
	if (nItem < 0 || nItem >= m_TabList.GetCount()) 
		return FALSE; 
	 
	POSITION pos = m_TabList.FindIndex(nItem); 
	ASSERT(pos);	 
	CString* pItem = m_TabList.GetAt(pos); 
	m_TabList.RemoveAt(pos); 
	_delete(pItem); 
	//Delete Wnd; 
	pos=m_TabWndList.FindIndex(nItem); 
	ASSERT(pos); 
	m_TabWndList.RemoveAt(pos);	 
	//Delet Tip 
	pos=m_TabTipList.FindIndex(nItem); 
	ASSERT(pos); 
	pItem = m_TabTipList.GetAt(pos); 
	m_TabTipList.RemoveAt(pos);	 
	_delete(pItem);	 
 
    // 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; 
	if (GetParent()->SendMessage(WM_NOTIFY, id, (LPARAM)&hdr) == 0) 
	{ 
	    if (m_iCurSel >= m_TabList.GetCount()) 
        { 
            SetCurSel(m_TabList.GetCount() - 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; 
		GetParent()->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; 
} 
 
BOOL CCJFlatTabCtrl::DeleteAllItems() 
{ 
	while(!m_TabList.IsEmpty()) { 
		CString* pItem = m_TabList.RemoveTail(); 
		_delete(pItem); 
	} 
 
	while(!m_TabTipList.IsEmpty()) { 
		CString* pItem = m_TabTipList.RemoveTail(); 
		_delete(pItem); 
	} 
 
	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; 
} 
 
BOOL CCJFlatTabCtrl::GetItemRect(int nItem, LPRECT lpRect) 
{ 
	if (nItem < 0 || nItem >= m_TabList.GetCount()) 
		return FALSE; 
	 
	int x = GetTotalArrowWidth(); 
	for (int i = 0; i < nItem; i++) 
	{ 
		x += GetTabWidth(i); 
		if (i != m_TabList.GetCount()) 
			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; 
} 
 
int CCJFlatTabCtrl::HitTest(TCHITTESTINFO *pHitTestInfo) const 
{ 
	// 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 < m_TabList.GetCount(); i++) 
	{ 
		int iTabWidth = GetTabWidth(i); 
		if (i != m_TabList.GetCount()) 
			iTabWidth -= (m_iClientHeight / 2) + 2; 
		if ((x <= iHitX) && (iHitX <= x + iTabWidth)) 
			return i; 
		x += iTabWidth; 
	} 
	 
	// hit point is right of rightmost tab 
	return -1; 
} 
 
int CCJFlatTabCtrl::SetCurSel(int nItem) 
{ 
	if (nItem < 0 || nItem >= m_TabList.GetCount()) 
		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; 
} 
 
BEGIN_MESSAGE_MAP(CCJFlatTabCtrl, CWnd) 
	//{{AFX_MSG_MAP(CCJFlatTabCtrl) 
	ON_WM_PAINT() 
	ON_WM_LBUTTONDOWN() 
	ON_WM_SIZE() 
	ON_WM_CREATE() 
	ON_BN_CLICKED(IDC_LEFTBUTTON, OnLeftArrow) 
	ON_BN_CLICKED(IDC_RIGHTBUTTON, OnRightArrow) 
	ON_BN_CLICKED(IDC_HOMEBUTTON, OnHomeArrow) 
	ON_BN_CLICKED(IDC_ENDBUTTON, OnEndArrow) 
	//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 
///////////////////////////////////////////////////////////////////////////// 
// CCJFlatTabCtrl message handlers 
 
BOOL CCJFlatTabCtrl::PreCreateWindow(CREATESTRUCT& cs)  
{ 
	if (!CWnd::PreCreateWindow(cs)) 
		return FALSE; 
	 
	cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS, 
            ::LoadCursor(NULL, IDC_ARROW), NULL, NULL); 
	 
	return TRUE; 
} 
 
BOOL CCJFlatTabCtrl::PreTranslateMessage(MSG* pMsg) 
{ 
	ASSERT_VALID(this); 
 
    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) *m_TabTipList.GetAt(m_TabTipList.FindIndex(iTab)); 
 
                TRACE2("Mouse over tab %d : %s\n", iTab, strText); 
 
			    m_ToolTip.UpdateTipText(strText, this); 
			    m_ToolTip.RelayEvent(pMsg); 
            } else  { 
				// VC5 compatibility - Kirk Stowell - 10-21-99 
				::SendMessage(m_ToolTip.m_hWnd, TTM_POP, 0, 0L); 
            } 
        } 
        else { 
			// VC5 compatibility - Kirk Stowell - 10-21-99 
			::SendMessage(m_ToolTip.m_hWnd, TTM_POP, 0, 0L); 
        } 
    } 
 
	return CWnd::PreTranslateMessage(pMsg); 
} 
 
void CCJFlatTabCtrl::OnPaint()  
{ 
	CPaintDC dc(this); // device context for painting 
	 
	CBrush brush; 
	brush.CreateSysColorBrush(COLOR_3DFACE); 
	CPen penBlack(PS_SOLID, 1, RGB(0,0,0)); 
	 
	// draw the black line along the left of the control 
	CPen* pOldPen = dc.SelectObject(&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_iTotalTabWidth, m_iClientHeight); 
	CBitmap* pOldBitmap = dcMem.SelectObject(&bitmap); 
	CBrush* pOldBrush = dcMem.SelectObject(&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 < m_TabList.GetCount(); i++) 
	{ 
		if (i != m_iCurSel) 
			x += DrawTab(dcMem, x, 0, m_iClientHeight, false, m_bTabsOnBottom, *m_TabList.GetAt(m_TabList.FindIndex(i))) - iOverlap; 
		else 
		{ 
			iSelX = x; 
			x += GetTabWidth(i) - iOverlap; 
		} 
	} 
    if (m_iCurSel >= 0) 
    	DrawTab(dcMem, iSelX, 0, m_iClientHeight, true, m_bTabsOnBottom, *m_TabList.GetAt(m_TabList.FindIndex(m_iCurSel))); 
	 
	// blit the bitmap onto the control 
	dc.BitBlt(iTotalArrowWidth, 0, m_iClientWidth - iTotalArrowWidth, m_iClientHeight, &dcMem, m_iXOffset, 0, SRCCOPY); 
 
	// fix potential resource leak - KStowell - 10-21-99. 
	dcMem.SelectObject(pOldPen); 
	dcMem.SelectObject(pOldBitmap); 
	dcMem.SelectObject(pOldBrush); 
	dcMem.DeleteDC(); 
	brush.DeleteObject(); 
	penBlack.DeleteObject(); 
	bitmap.DeleteObject(); 
} 
 
void CCJFlatTabCtrl::OnLButtonDown(UINT nFlags, CPoint point)  
{ 
	// TODO: Add your message handler code here and/or call default 
	TCHITTESTINFO test; 
	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; 
		if (AfxGetMainWnd()->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.idFrom=(int)GetItemWnd(iTab); 
			hdr.code = TCN_SELCHANGE; 
			::SendMessage(AfxGetMainWnd()->m_hWnd,WM_NOTIFY, id, (LPARAM)&hdr); 
		} 
	} 
	 
	CWnd::OnLButtonDown(nFlags, point); 
} 
 
void CCJFlatTabCtrl::OnSize(UINT nType, int cx, int cy)  
{ 
	CWnd::OnSize(nType, cx, cy); 
	 
	// 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(); 
} 
 
int CCJFlatTabCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)  
{ 
	if (CWnd::OnCreate(lpCreateStruct) == -1) 
		return -1; 
	 
	// TODO: Add your specialized creation code here 
	if (lpCreateStruct->style & FTS_BOTTOM) { 
		m_bTabsOnBottom = true; 
	} 
 
    if (lpCreateStruct->style & TCS_TOOLTIPS) { 
	    m_ToolTip.Create(this); 
	    m_ToolTip.Activate(TRUE); 
	    m_ToolTip.AddTool(this, _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 CCJMetaFileButton; 
		m_pLeftButton->Create(NULL, WS_CHILD | WS_VISIBLE | WS_DISABLED | BS_OWNERDRAW, rcButton, this, IDC_LEFTBUTTON); 
		m_pLeftButton->SetMetaFiles(m_hLeftArrow, 0, 0, m_hLeftArrowDisabled); 
		 
		m_pRightButton = new CCJMetaFileButton; 
		m_pRightButton->Create(NULL, WS_CHILD | WS_VISIBLE | WS_DISABLED | BS_OWNERDRAW, rcButton, this, 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 CCJMetaFileButton; 
			m_pHomeButton->Create(NULL, WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, rcButton, this, 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 CCJMetaFileButton; 
			m_pEndButton->Create(NULL, WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, rcButton, this, 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; 
} 
 
void CCJFlatTabCtrl::OnLeftArrow()  
{ 
    // 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(); 
    } 
} 
 
void CCJFlatTabCtrl::OnRightArrow()  
{ 
    // 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(); 
    } 
} 
 
void CCJFlatTabCtrl::OnHomeArrow()  
{ 
	// TODO: Add your control notification handler code here 
	m_iXOffset = 0; 
	EnableButtons(); 
	InvalidateTabs(); 
} 
 
void CCJFlatTabCtrl::OnEndArrow()  
{ 
	// TODO: Add your control notification handler code here 
	m_iXOffset = m_iTotalTabWidth + GetTotalArrowWidth() - m_iClientWidth + 1; 
	EnableButtons(); 
	InvalidateTabs(); 
} 
 
void CCJFlatTabCtrl::SetTipText(int nItem, LPCTSTR lpszTabTip) 
{ 
    if (nItem < m_TabList.GetCount()) 
    { 
	    for (int i = 0; i < m_TabList.GetCount(); i++) 
	    { 
            if (i == nItem) 
            { 
                CString *pTipItem = m_TabTipList.GetAt(m_TabTipList.FindIndex(nItem)); 
 
            	*pTipItem = lpszTabTip; 
 
                break; 
            } 
	    } 
    } 
 
    return; 
} 
 
CString CCJFlatTabCtrl::GetTipText(int nItem) 
{ 
    CString strText; 
 
    if (nItem < m_TabList.GetCount()) 
    { 
	    for (int i = 0; i < m_TabList.GetCount(); i++) 
	    { 
            if (i == nItem) 
            { 
                strText = *m_TabTipList.GetAt(m_TabTipList.FindIndex(nItem)); 
                break; 
            } 
	    } 
    } 
 
    return strText; 
} 
 
void CCJFlatTabCtrl::Home() 
{ 
    OnHomeArrow(); 
} 
 
void CCJFlatTabCtrl::SetMaxTabWidth(int nMaxTabWidth) 
{ 
	m_nMaxTabWidth=nMaxTabWidth; 
} 
 
BOOL CCJFlatTabCtrl::SetItemText(const int nItem, LPCTSTR lpszItem) 
{ 
	if (nItem < 0 || nItem > m_TabList.GetCount()) 
		return FALSE;	 
	POSITION pos = m_TabList.FindIndex(nItem); 
	ASSERT(pos);	 
	CString* pOldItem = m_TabList.GetAt(pos);	 
	CString* pItem = new CString; 
	ASSERT(pItem); 
	*pItem = lpszItem; 
	m_TabList.SetAt(pos,pItem); 
	_delete(pOldItem); 
    // PGB - Add the tabs tip text to the list 
	SetTipText(nItem,lpszItem);    
   	m_iTotalTabWidth = GetTotalTabWidth();      // PGB - Recalculate the total tab width now we have added a new item 
	EnableButtons(); 
	InvalidateTabs(); 
	return TRUE; 
} 
 
CWnd* CCJFlatTabCtrl::GetItemWnd(int nItem) 
{ 
	CWnd* pWnd;	 
    if (nItem>-1 && nItem < m_TabList.GetCount()) 
    { 
	     pWnd = (CWnd*)m_TabWndList.GetAt(m_TabWndList.FindIndex(nItem)); 
		 return pWnd; 
    } 
	else 
	{ 
		return NULL; 
	}     
} 
 
 
BOOL CCJFlatTabCtrl::SetItemText(CWnd *pWnd, LPCTSTR lpszItem) 
{ 
	if(pWnd==NULL) 
		return FALSE; 
	int nIndex=0; 
	for (POSITION pos=m_TabWndList.GetHeadPosition(); pos; m_TabWndList.GetNext(pos)) 
	{ 
		CWnd *pMember=m_TabWndList.GetAt(pos); 
		if (pMember==pWnd) 
		{ 
            SetItemText(nIndex, lpszItem); 
			SetTipText(nIndex,lpszItem); 
			// PGB - Add the tabs tip text to the list	 
   			//m_iTotalTabWidth = GetTotalTabWidth();      // PGB - Recalculate the total tab width now we have added a new item 
			//EnableButtons(); 
			//InvalidateTabs(); 
			return TRUE; 
		} 
        nIndex++; 
    } 
	return FALSE; 
} 
 
BOOL CCJFlatTabCtrl::SetItemSel(CWnd *pWnd) 
{ 
	if(pWnd==NULL) 
		return FALSE; 
	int nIndex=0; 
	for (POSITION pos=m_TabWndList.GetHeadPosition(); pos; m_TabWndList.GetNext(pos)) 
	{ 
		CWnd *pMember=m_TabWndList.GetAt(pos); 
		if (pMember==pWnd) 
		{ 
            SetCurSel(nIndex); 
			return TRUE; 
		} 
        nIndex++; 
    } 
	return FALSE; 
}