www.pudn.com > TabClient.rar > ftab.cpp


//////////////////////////////////////////////////////////////// 
// Copyright 1999 Paul DiLascia 
// If this program works, it was written by Paul DiLascia. 
// If not, I don't know who wrote it. 
// 
#include "stdafx.h" 
#include "ftab.h" 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
const CXOFFSET = 8;		// defined pitch of trapezoid slant 
const CXMARGIN = 10;		// left/right text margin 
const CYMARGIN = 2;		// top/bottom text margin 
const CYBORDER = 1;		// top border thickness 
 
////////////////// 
// Compute the the points, rect and region for a tab. 
// Input x is starting x pos. 
// 
 
CFolderTabCtrl::CFolderTabCtrl() 
{ 
	m_iCurItem = 0; 
	m_dwFtabStyle = 0; 
	m_nDesiredWidth = 0; 
	m_RectColor = GetSysColor(COLOR_3DFACE); //RECT内TAB页外的颜色设置 
} 
 
CFolderTabCtrl::~CFolderTabCtrl() 
{ 
	while (!m_lsTabs.IsEmpty()) 
		delete (CFolderTab*)m_lsTabs.RemoveHead(); 
} 
 
////////////////////////////////////////////////////////////////// 
// CFolderTabCtrl 
 
IMPLEMENT_DYNAMIC(CFolderTabCtrl, CWnd) 
BEGIN_MESSAGE_MAP(CFolderTabCtrl, CWnd) 
	//{{AFX_MSG_MAP(CFolderTabCtrl) 
	ON_WM_PAINT() 
	ON_WM_LBUTTONDOWN() 
	ON_WM_CTLCOLOR() 
	//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 
///////////////////////////////////////////////////////////////////////////// 
// CFolderTabCtrl message handlers 
 
int CFolderTab::ComputeRgn(CDC& dc, int x) 
{ 
	m_rgn.DeleteObject(); 
 
	CRect& rc = m_rect; 
	rc.SetRectEmpty(); 
 
	// calculate desired text rectangle 
	dc.DrawText(m_sText, &rc, DT_CALCRECT); 
	rc.right  += 2*CXOFFSET + 2*CXMARGIN;						// add margins 
	rc.bottom = rc.top + GetSystemMetrics(SM_CYHSCROLL);	// ht = scrollbar ht 
	rc += CPoint(x,0);												// shift right 
 
	// create trapezoid region 
	CPoint pts[4]; 
	GetTrapezoid(rc, pts); 
	m_rgn.CreatePolygonRgn(pts, 4, WINDING); 
 
	return rc.Width(); 
} 
 
////////////////// 
// Given the boundint rect, compute trapezoid region. 
// Note that the right and bottom edges not included in rect or 
// trapezoid; these are normal rules of geometry.  
// 
void CFolderTab::GetTrapezoid(const CRect& rc, CPoint* pts) const 
{ 
	pts[0] = rc.TopLeft(); 
	pts[1] = CPoint(rc.left + CXOFFSET,   rc.bottom); 
	pts[2] = CPoint(rc.right- CXOFFSET-1, rc.bottom); 
	pts[3] = CPoint(rc.right-1, rc.top); 
} 
 
 
////////////////// 
// Draw tab in normal or highlighted state 
// 
int CFolderTab::Draw(CDC& dc, CFont& font, BOOL bSelected) 
{ 
	COLORREF bgColor = bSelected ? RGB(255,255,255):RGB(0,180,220);//RGB(160,180,220):RGB(0,180,220);//GetSysColor(bSelected ? COLOR_WINDOW     : COLOR_3DFACE); 
	COLORREF fgColor = bSelected ? RGB(0,0,220):RGB(160,0,220);//RGB(0,0,220):RGB(160,0,220);//GetSysColor(bSelected ? COLOR_WINDOWTEXT : COLOR_BTNTEXT); 
 
	CBrush brush(bgColor);					 // background brush 
	dc.SetBkColor(bgColor);					 // text background 
	dc.SetTextColor(RGB(0,0,0));				 // text color = fg color 
 
	CPen blackPen(PS_SOLID, 1, RGB(0,0,0));	// black 
	CPen shadowPen(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW)); 
 
	// Fill trapezoid 
	CPoint pts[4]; 
	CRect rc = m_rect; 
	GetTrapezoid(rc, pts); 
	CPen* pOldPen = dc.SelectObject(&blackPen); 
	dc.FillRgn(&m_rgn, &brush); 
 
	// Draw edges. This is requires two corrections: 
	// 1) Trapezoid dimensions don't include the right and bottom edges, 
	// so must use one pixel less on bottom (cybottom) 
	// 2) the endpoint of LineTo is not included when drawing the line, so 
	// must add one pixel (cytop) 
	// 
	pts[1].y--;			// correction #1: true bottom edge y-coord 
	pts[2].y--;			// ...ditto 
	pts[3].y--;			// correction #2:	extend final LineTo 
	dc.MoveTo(pts[0]);						// upper left 
	dc.LineTo(pts[1]);						// bottom left 
	dc.SelectObject(&blackPen);			// bottom line is shadow color 
	dc.MoveTo(pts[1]);						// line is inside trapezoid bottom 
	dc.LineTo(pts[2]);						// ... 
	dc.SelectObject(&blackPen);			// upstroke is black 
	dc.LineTo(pts[3]);						// y-1 to include endpoint 
	if (!bSelected) { 
		// if not highlighted, upstroke has a 3D shadow, one pixel inside 
		pts[2].x--;		// offset left one pixel 
		pts[3].x--;		// ...ditto 
		dc.SelectObject(&shadowPen); 
		dc.MoveTo(pts[2]); 
		dc.LineTo(pts[3]); 
	} 
	dc.SelectObject(pOldPen); 
 
	// draw text 
	rc.DeflateRect(CXOFFSET + CXMARGIN, CYMARGIN); 
	CFont* pOldFont = dc.SelectObject(&font); 
	dc.DrawText(m_sText, &rc, DT_CENTER|DT_VCENTER|DT_SINGLELINE); 
	dc.SelectObject(pOldFont); 
 
	return m_rect.right; 
} 
 
 
////////////////// 
// Create folder tab control from static control. 
// Destroys the static control. This is convenient for dialogs 
// 
BOOL CFolderTabCtrl::CreateFromStatic(UINT nID, CWnd* pParent,COLORREF clr) 
{ 
	m_RectColor=clr; 
	CStatic wndStatic; 
	if (!wndStatic.SubclassDlgItem(nID, pParent)) 
		return FALSE; 
	CRect rc; 
	wndStatic.GetWindowRect(&rc); 
	pParent->ScreenToClient(&rc); 
	wndStatic.DestroyWindow(); 
	rc.bottom = rc.top + GetDesiredHeight(); 
	return Create(WS_CHILD|WS_VISIBLE, rc, pParent, nID); 
} 
 
////////////////// 
// Create folder tab control. 
// 
BOOL CFolderTabCtrl::Create(DWORD dwStyle, const RECT& rc, 
	CWnd* pParent, UINT nID, DWORD dwFtabStyle) 
{ 
	ASSERT(pParent); 
	ASSERT(dwStyle & WS_CHILD); 
 
	m_dwFtabStyle = dwFtabStyle; 
 
	static LPCTSTR lpClassName = _T("PDFolderTab"); 
	static BOOL bRegistered = FALSE; // registered? 
	if (!bRegistered) { 
		WNDCLASS wc; 
		memset(&wc, 0, sizeof(wc)); 
		wc.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; 
		wc.lpfnWndProc = (WNDPROC)::DefWindowProc; // will get hooked by MFC 
		wc.hInstance = AfxGetInstanceHandle(); 
		wc.hCursor = LoadCursor(NULL, IDC_ARROW); 
		wc.hbrBackground = CreateSolidBrush(m_RectColor);//GetSysColor(COLOR_3DFACE)); 
		wc.lpszMenuName = NULL; 
		wc.lpszClassName = lpClassName; 
		if (!AfxRegisterClass(&wc)) { 
			TRACE("*** CFolderTabCtrl::AfxRegisterClass failed!\n"); 
			return FALSE; 
		} 
		bRegistered = TRUE; 
	} 
	if (!CWnd::CreateEx(0, lpClassName, NULL, dwStyle, rc, pParent, nID)) 
		return FALSE; 
 
	// initialize fonts 
	LOGFONT lf; 
	memset(&lf, 0, sizeof(lf)); 
	lf.lfHeight = GetSystemMetrics(SM_CYHSCROLL)-CYMARGIN; 
	lf.lfWeight = FW_NORMAL; 
	lf.lfCharSet = DEFAULT_CHARSET; 
	_tcscpy(lf.lfFaceName, _T("宋体")); 
	m_fontNormal.CreateFontIndirect(&lf); 
	lf.lfHeight -= 2; 
	m_fontSelected.CreateFontIndirect(&lf); 
 
	return TRUE; 
} 
 
////////////////// 
// copy a font 
// 
static void CopyFont(CFont& dst, CFont& src) 
{ 
	dst.DeleteObject(); 
	LOGFONT lf; 
	VERIFY(src.GetLogFont(&lf)); 
	dst.CreateFontIndirect(&lf); 
} 
 
////////////////// 
// Set normal, selected fonts 
// 
void CFolderTabCtrl::SetFonts(CFont& fontNormal, CFont& fontSelected) 
{ 
	CopyFont(m_fontNormal, fontNormal); 
	CopyFont(m_fontSelected, m_fontSelected); 
} 
 
////////////////// 
// Paint function 
// 
void CFolderTabCtrl::OnPaint()  
{ 
	CPaintDC dc(this); // device context for painting 
 
	CRect rc; 
	GetClientRect(&rc); 
 
	CFolderTab* pCurTab = NULL; 
 
	// draw all the normal (non-selected) tabs 
	int n = GetItemCount(); 
	for (int i=0; iDraw(dc, m_fontNormal, FALSE) > rc.right) 
			break; 
	} 
	// draw selected tab last so it will be "on top" of the others 
	if (pCurTab) 
		pCurTab->Draw(dc, m_fontNormal, TRUE); 
 
	// draw border: line along the top edge, excluding seleted tab 
	CRect rcCurTab(0,0,0,0); 
	if (pCurTab) 
	{ 
		rcCurTab = pCurTab->GetRect(); 
		rcCurTab.bottom += 10; 
	} 
 
	CPen blackPen(PS_SOLID, 1, RGB(0,0,0));	// black 
	CPen* pOldPen = dc.SelectObject(&blackPen); 
	dc.MoveTo(rcCurTab.right, rcCurTab.top); 
	dc.LineTo(rc.right, rc.top); 
	if (m_dwFtabStyle & FTS_FULLBORDER) { 
		dc.MoveTo(rc.right-1, rc.top); 
		dc.LineTo(rc.right-1, rc.bottom-1); 
		dc.LineTo(rc.left,  rc.bottom-1); 
		dc.LineTo(rc.left,  rc.top); 
	} else { 
		dc.MoveTo(rc.left, rc.top); 
	} 
	dc.LineTo(rcCurTab.TopLeft()); 
	dc.SelectObject(pOldPen); 
} 
 
////////////////// 
// Handle mouse click: select new tab, if any. Notify parent, of course 
// 
void CFolderTabCtrl::OnLButtonDown(UINT nFlags, CPoint pt) 
{ 
	int iTab = HitTest(pt); 
	if (iTab>=0 && iTab!=m_iCurItem) { 
		SelectItem(iTab); 
		NMFOLDERTAB nm; 
		nm.hwndFrom = m_hWnd; 
		nm.idFrom = GetDlgCtrlID(); 
		nm.code = FTN_TABCHANGED; 
		nm.iItem = iTab; 
		nm.pItem = GetTab(iTab); 
		CWnd* pParent = GetParent(); 
		pParent->SendMessage(WM_NOTIFY, nm.idFrom, (LPARAM)&nm); 
	} 
} 
 
////////////////// 
// Find which tab is under mouse, -1 if none 
// 
int CFolderTabCtrl::HitTest(CPoint pt) 
{ 
	CRect rc; 
	GetClientRect(&rc); 
	if (rc.PtInRect(pt)) { 
		int n = GetItemCount(); 
		for (int i=0; iHitTest(pt)) 
				return i; 
		} 
	} 
	return -1; 
} 
 
////////////////// 
// Select ith tab. Returns index selected 
// 
int CFolderTabCtrl::SelectItem(int iTab) 
{ 
	if (iTab<0 || iTab>=GetItemCount()) 
		return -1;		// bad 
	if (iTab==m_iCurItem) 
		return iTab;	// already selected 
 
	InvalidateTab(m_iCurItem);		// invalidate old tab (repaint) 
	m_iCurItem = iTab;				// set new selected tab 
	InvalidateTab(m_iCurItem);		// repaint new tab 
	 
	return m_iCurItem; 
} 
 
////////////////// 
/// Invalidate a tab: invaldate its rect 
// 
void CFolderTabCtrl::InvalidateTab(int iTab, BOOL bErase) 
{ 
	InvalidateRect(GetTab(iTab)->GetRect(), bErase); 
} 
 
BOOL CFolderTabCtrl::Load(UINT nIDRes) 
{ 
	CString s; 
	if (!s.LoadString(nIDRes)) 
		return FALSE; 
 
	CString sTab; 
	for (int i=0; AfxExtractSubString(sTab, s, i); i++) { 
		AddItem(sTab); 
	} 
	RecomputeLayout(); 
	return TRUE; 
} 
 
int CFolderTabCtrl::AddItem(LPCTSTR lpszText) 
{ 
	m_lsTabs.AddTail(new CFolderTab(lpszText)); 
	return m_lsTabs.GetCount() - 1; 
} 
 
BOOL CFolderTabCtrl::RemoveItem(int iPos) 
{ 
	POSITION pos = m_lsTabs.FindIndex(iPos); 
	if (pos) { 
		CFolderTab* pTab = (CFolderTab*)m_lsTabs.GetAt(pos); 
		m_lsTabs.RemoveAt(pos); 
		delete pTab; 
	} 
	return pos!=NULL; 
} 
 
CFolderTab* CFolderTabCtrl::GetTab(int iPos) 
{ 
	POSITION pos = m_lsTabs.FindIndex(iPos); 
	return pos ? static_cast(m_lsTabs.GetAt(pos)) : NULL; 
} 
 
int CFolderTabCtrl::RecomputeLayout() 
{ 
	CClientDC dc(this); 
	CFont* pOldFont = dc.SelectObject(&m_fontNormal); 
	int x = 0; 
	int n = GetItemCount(); 
	CFolderTab* pTab; 
	for (int i=0; iComputeRgn(dc, x) - CXOFFSET; 
	} 
	dc.SelectObject(pOldFont); 
 
	if (pTab) { 
		CRect rc = pTab->GetRect(); 
		m_nDesiredWidth = rc.right; 
	} 
	return m_nDesiredWidth; 
} 
 
HBRUSH CFolderTabCtrl::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)  
{ 
	HBRUSH hbr = CWnd::OnCtlColor(pDC, pWnd, nCtlColor); 
	 
	// TODO: Change any attributes of the DC here 
	/*	pDC->SetTextColor(RGB(255,0,0)); 
		pDC->SetBkColor(RGB(160,180,255)); 
		HBRUSH b=CreateSolidBrush(RGB(160,180,220)); 
		return b;*/ 
	// TODO: Return a different brush if the default is not desired 
	return hbr; 
} 
 
BOOL CFolderTabCtrl::CreateFromRect(CRect &rc, CWnd *pParent,COLORREF clr) 
{ 
	m_RectColor=clr; 
	pParent->ScreenToClient(&rc); 
	rc.bottom = rc.top + GetDesiredHeight(); 
 
	return Create(WS_CHILD|WS_VISIBLE, rc, pParent, NULL); 
}