Click here to Skip to main content
Click here to Skip to main content
 
Add your own
alternative version
Go to top

Create a Universal Document Template which supports Dynamic Frame Window Layout

, 14 Dec 2002
Introduce a programming technology to design a very complex, rich document type.
visualframework_src.zip
sample
ObjManager
res
ObjManager.manifest
VisualFrameWork
application.rgs
AtlForm.rgs
Board.rgs
DocumentCtrl.rgs
Documents.rgs
menuimg.bmp
Prairie Wind.bmp
res
Toolbar.bmp
VisualFrameWork.ico
VisualFrameWork.manifest
VisualFrameWorkDoc.ico
VisualFrameWork.rgs
VisualFrameWork.tlb
VisualFrameWork1
VisualFrameWork12
VisualFrameWork13
Demo1
Demo2
MfcViewLib
MfcViewLib.rgs
cnn.rgs
MfcViewLib.def
MfcViewLibps.def
AtlView2.rgs
AtlView3.rgs
vc70.tlb
VbComLib
VbComLib.vbproj.user
VCSharpComLib
VCSharpComLib.csproj.user
DemoDoc
Demo1
Demo2
Demo3
DocumentPropertyPage.rgs
New Document 2
New Document333
New Document 2dd
VBDoc
UD.dob
prjVBDoc.vbp
MSSCCPRJ.SCC
prjVBDoc.vbw
CNN
MSSCCPRJ.SCC
PrjCnn.vbw
PrjCnn.dll
PrjCnn.lib
PrjCnn.exp
cls.cls
PrjCnn.vbp
//*******************************************************************************
// COPYRIGHT NOTES
// ---------------
// This source code is a part of Tangram library.
// You may use, compile or redistribute it as part of your application 
// for free. You cannot redistribute it as a part of a software development 
// library without the agreement of the author. If the sources are 
// distributed along with the application, you should leave the original 
// copyright notes in the source code without any changes.
// This code can be used WITHOUT ANY WARRANTIES on your own risk.
// 
// For the latest updates to this library, check site:
// http://www.tangramdev.com
// 
// sunhui
//*******************************************************************************

#include "stdafx.h"
#include "tangramDoc.h"
#include "tabwnd.h"
#include "globals.h"
#include <afxpriv.h>        // Needed for WM_SIZEPARENT

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

UINT BCGM_CHANGE_ACTIVE_TAB = ::RegisterWindowMessage (_T("BCGM_ONCHANGE_ACTIVE_TAB"));
UINT BCGM_ON_HSCROLL		= ::RegisterWindowMessage (_T("BCGM_ON_HSCROLL"));
/////////////////////////////////////////////////////////////////////////////
// CTabWndInfo

class CTabWndInfo : public CObject
{
	friend class CTabWnd;

	CTabWndInfo(const CString&	strText,
				const UINT		uiIcon,
				CWnd*			pWnd) :
		m_pWnd (pWnd),
		m_uiIcon (uiIcon)
	{
		m_strText = m_strDisplayedText = strText;
		m_rect.SetRectEmpty ();
		m_nFullWidth = 0;
		m_bDestoryed = false;
	}
	~CTabWndInfo()
	{
		TRACE("Delete CTabWndInfo %x TabText %s \n",this, m_strText);
	}

	CString		m_strText;
	CString		m_strDisplayedText;
	const UINT	m_uiIcon;
	CRect		m_rect;
	CWnd*		m_pWnd;
	int			m_nFullWidth;
	BOOL		m_bDestoryed;
};

/////////////////////////////////////////////////////////////////////////////
// CTabWnd

#define TAB_BORDER_SIZE	3
#define TEXT_MARGIN		4
#define IMAGE_MARGIN	4
#define MIN_SROLL_WIDTH (::GetSystemMetrics (SM_CXHSCROLL) * 2)
#define SPLITTER_WIDTH	5
#define TABS_FONT		_T("Arial")

IMPLEMENT_DYNCREATE(CTabWnd, CTangramContainerWnd)

CTabWnd::CTabWnd()
{
	m_iTabsNum = 0;
	m_pOleItem = NULL;
	m_bLockFlag = FALSE;
	m_iActiveTab = -1;
	m_sizeImage = CSize (0, 0);
	m_hImageList = NULL;
	m_bFlat = FALSE;
	m_bSharedScroll = FALSE;
	m_rectTabsArea.SetRectEmpty ();
	m_rectWndArea.SetRectEmpty ();
	m_nTabsHorzOffset = 0;
	m_nTabsHorzOffsetMax = 0;
	m_nTabsTotalWidth = 0;
	m_nHorzScrollWidth = 0;
	m_nScrollBarRight = 0;
	m_rectTabSplitter.SetRectEmpty ();
	m_bTrackSplitter = FALSE;
	m_location = LOCATION_BOTTOM;
	m_bFlatFrame = TRUE;
	m_pBoard=NULL;

	m_clrActiveTabBk = (COLORREF) -1;
	m_clrActiveTabFg = (COLORREF) -1;
	
	m_nTabsHeight = 0;
}
//***************************************************************************************
CTabWnd::~CTabWnd()
{
}

BEGIN_MESSAGE_MAP(CTabWnd, CTangramContainerWnd)
	//{{AFX_MSG_MAP(CTabWnd)
	ON_WM_DESTROY()
	ON_WM_PAINT()
	ON_WM_SIZE()
	ON_WM_LBUTTONDOWN()
	ON_WM_CREATE()
	ON_WM_ERASEBKGND()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_HSCROLL()
	ON_WM_SETCURSOR()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_CANCELMODE()
	ON_WM_SYSCOLORCHANGE()
	ON_WM_SETTINGCHANGE()
	ON_MESSAGE(WM_SIZEPARENT, OnSizeParent)
	ON_WM_CLOSE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

BOOL CTabWnd::Create (Style style, const RECT& rect, CWnd* pParentWnd, UINT nID, Location location /* = LOCATION_BOTTOM*/)
{
	m_bFlat = (style == STYLE_FLAT) || (style == STYLE_FLAT_SHARED_HORZ_SCROLL);
	m_bSharedScroll = style == STYLE_FLAT_SHARED_HORZ_SCROLL;
	m_location = location;

	if (m_bFlat && m_location != LOCATION_BOTTOM)
	{
		ASSERT (FALSE);
		m_location = LOCATION_BOTTOM;
	}

	if (!m_bFlat && m_bSharedScroll)
	{
		//--------------------------------------
		// Only flat tab has a shared scrollbar!
		//--------------------------------------
		ASSERT (FALSE);
		m_bSharedScroll = FALSE;
	}

	return CWnd::Create (NULL, _T(""), WS_CHILD | WS_VISIBLE, rect,
		pParentWnd, nID);
}

/////////////////////////////////////////////////////////////////////////////
// CTabWnd message handlers

void CTabWnd::OnDestroy() 
{
	if(m_pOleItem)m_pOleItem->Delete();
	CTangramContainerWnd::OnDestroy();
	TRACE("TabWnd : %x-CTangramContainerWnd::OnDestroy()\n",this);
	if(m_iTabsNum)
	{
		for (int i = 0; i < m_iTabsNum; i ++)
		{
			CTabWndInfo* pTab = (CTabWndInfo*) m_arTabs [i];
			//ASSERT_VALID (pTab);
			if(::IsWindow(pTab->m_pWnd->m_hWnd))
				if(!pTab->m_pWnd->IsKindOf(RUNTIME_CLASS(CView)))
				{
					pTab->m_pWnd->DestroyWindow ();
					//delete pTab;
				}

			//else		//pTab->m_pWnd = NULL;
			delete pTab;
		}
	}
	m_iTabsNum=0;
	m_arTabs.RemoveAll();
}
//***************************************************************************************
void CTabWnd::OnPaint() 
{
	CPaintDC dc(this); // device context for painting

	CRect rectClient;
	GetClientRect (&rectClient);

	CBrush* pOldBrush = dc.SelectObject (&globalData.brBtnFace);
	ASSERT (pOldBrush != NULL);

	CPen penDrak (PS_SOLID, 1, globalData.clrBtnDkShadow);
	CPen* pOldPen = (CPen*) dc.SelectObject (&penDrak);
	ASSERT(pOldPen != NULL);

	CRect rectTabs = rectClient;

	if (m_location == LOCATION_BOTTOM)
	{
		rectTabs.top = m_rectTabsArea.top - TAB_BORDER_SIZE;
	}
	else
	{
		rectTabs.bottom = m_rectTabsArea.bottom + TAB_BORDER_SIZE;
	}

	dc.FillRect (rectTabs, &globalData.brBtnFace);

	CRect rectFrame = rectClient;

	if (m_bFlat)
	{
		//---------------------------
		// Draw line bellow the tabs:
		//---------------------------
		dc.MoveTo (rectFrame.left, m_rectTabsArea.top);
		dc.LineTo (rectFrame.right, m_rectTabsArea.top);
	}
	else
	{
		if (m_location == LOCATION_BOTTOM)
		{
			rectFrame.bottom = m_rectTabsArea.top;
		}
		else
		{
			rectFrame.top = m_rectTabsArea.bottom;
		}
	}

	//-----------------------------------------------------
	// Draw wide 3-dimensional frame around the Tabs area:
	//-----------------------------------------------------
	if (m_bFlatFrame)
	{
		// Lutisan
		CRect rectBorder (rectFrame);
		if (m_bFlat)
		{
			rectBorder.bottom = m_rectTabsArea.top + 1;
		}

		rectFrame.DeflateRect (1, 1);

		if (rectFrame.Width () > 0 && rectFrame.Height () > 0)
		{
			dc.PatBlt (rectFrame.left, rectFrame.top, TAB_BORDER_SIZE, rectFrame.Height (), PATCOPY);
			dc.PatBlt (rectFrame.left, rectFrame.top, rectFrame.Width (), TAB_BORDER_SIZE, PATCOPY);
			dc.PatBlt (rectFrame.right - TAB_BORDER_SIZE - 1, rectFrame.top, TAB_BORDER_SIZE + 1, rectFrame.Height (), PATCOPY);
			dc.PatBlt (rectFrame.left, rectFrame.bottom - TAB_BORDER_SIZE, rectFrame.Width (), TAB_BORDER_SIZE, PATCOPY);
		}

		dc.Draw3dRect (&rectBorder, globalData.clrBtnHilite, globalData.clrBtnDkShadow);
	}
	else
	{
		dc.Draw3dRect (&rectFrame, globalData.clrBtnHilite, globalData.clrBtnDkShadow);
		
		rectFrame.DeflateRect (1, 1);
		dc.Draw3dRect (&rectFrame,
					globalData.clrBtnLight, globalData.clrBtnShadow);

		rectFrame.DeflateRect (1, 1);

		if (rectFrame.Width () > 0 && rectFrame.Height () > 0)
		{
			dc.PatBlt (rectFrame.left, rectFrame.top, TAB_BORDER_SIZE, rectFrame.Height (), PATCOPY);
			dc.PatBlt (rectFrame.left, rectFrame.top, rectFrame.Width (), TAB_BORDER_SIZE, PATCOPY);
			dc.PatBlt (rectFrame.right - TAB_BORDER_SIZE, rectFrame.top, TAB_BORDER_SIZE, rectFrame.Height (), PATCOPY);
			dc.PatBlt (rectFrame.left, rectFrame.bottom - TAB_BORDER_SIZE, rectFrame.Width (), TAB_BORDER_SIZE, PATCOPY);

			rectFrame.DeflateRect (TAB_BORDER_SIZE - 2, TAB_BORDER_SIZE - 2);

			if (rectFrame.Width () > 0 && rectFrame.Height () > 0)
			{
				dc.Draw3dRect (&rectFrame,
							globalData.clrBtnDkShadow, globalData.clrBtnHilite);
			}
		}
	}

	CFont* pOldFont = dc.SelectObject (m_bFlat ?	&m_fntTabs : 
													&globalData.fontRegular);
	ASSERT(pOldFont != NULL);

	dc.SetBkMode (TRANSPARENT);
	dc.SetTextColor (globalData.clrBtnText);

	if (m_rectTabsArea.Width () > 2 * TAB_BORDER_SIZE + 1 &&
		m_rectTabsArea.Height () > 2 * TAB_BORDER_SIZE + 1)
	{
		//-----------
		// Draw tabs:
		//-----------
		if (m_bFlat)
		{
			CRgn rgn;
			rgn.CreateRectRgnIndirect (m_rectTabsArea);

			dc.SelectClipRgn (&rgn);
		}

		for (int i = 0; i < m_iTabsNum; i ++)
		{
			CTabWndInfo* pTab = (CTabWndInfo*) m_arTabs [i];
			ASSERT_VALID (pTab);

			if (m_bFlat)
			{
				if (i != m_iActiveTab)	// Draw active button last
				{
					DrawFlatTab (&dc, pTab, FALSE);
				}
			}
			else
			{
				Draw3DTab (&dc, pTab, i == m_iActiveTab);
			}
		}

		if (m_bFlat && m_iActiveTab >= 0)
		{
			//-----------------
			// Draw active tab:
			//-----------------
			CTabWndInfo* pTabActive = (CTabWndInfo*) m_arTabs [m_iActiveTab];
			ASSERT_VALID (pTabActive);

			dc.SelectObject (&m_brActiveTab);
			dc.SelectObject (&m_fntTabsBold);
			dc.SetTextColor (GetActiveTabTextColor ());

			DrawFlatTab (&dc, pTabActive, TRUE);

			//---------------------------------
			// Draw line bellow the active tab:
			//---------------------------------
			CPen penLight (PS_SOLID, 1, GetActiveTabColor ());
			dc.SelectObject (&penLight);

			dc.MoveTo (pTabActive->m_rect.left + 1, pTabActive->m_rect.top);
			dc.LineTo (pTabActive->m_rect.right, pTabActive->m_rect.top);
		}

		if (m_bFlat)
		{
			dc.SelectClipRgn (NULL);
		}
	}

	if (!m_rectTabSplitter.IsRectEmpty ())
	{
		dc.FillRect (m_rectTabSplitter, &globalData.brBtnFace);

		CRect rectTabSplitter = m_rectTabSplitter;

		dc.Draw3dRect (rectTabSplitter,
			globalData.clrBtnDkShadow, globalData.clrBtnShadow);
		rectTabSplitter.DeflateRect (1, 1);
		dc.Draw3dRect (rectTabSplitter,
			globalData.clrBtnHilite, globalData.clrBtnShadow);
	}
	
	dc.SelectObject (pOldFont);
	dc.SelectObject (pOldBrush);
	dc.SelectObject (pOldPen);
}
//***************************************************************************************
// Handle resize
LRESULT CTabWnd::OnSizeParent(WPARAM, LPARAM lParam)
{
	if (m_bLockFlag)
		return 0;
	ResizeTab();
	return 0;
}

void CTabWnd::OnSize(UINT nType, int cx, int cy) 
{
	CTangramContainerWnd::OnSize(nType, cx, cy);

	int nTabsAreaWidth = cx - 4 * ::GetSystemMetrics (SM_CXVSCROLL) 
							- 2 * TAB_BORDER_SIZE;

	if (nTabsAreaWidth <= MIN_SROLL_WIDTH)
	{
		m_nHorzScrollWidth = 0;
	}
	else if (nTabsAreaWidth / 2 > MIN_SROLL_WIDTH)
	{
		m_nHorzScrollWidth = nTabsAreaWidth / 2;
	}
	else
	{
		m_nHorzScrollWidth = nTabsAreaWidth; 
	}

	RecalcLayout ();
	SynchronizeScrollBar ();
}
//***************************************************************************************
void CTabWnd::AddTab (CWnd* pNewWnd, LPCTSTR lpszName, UINT uiImageId)
{
	ASSERT_VALID (this);
	ASSERT_VALID (pNewWnd);
	ASSERT (pNewWnd->GetParent()->GetSafeHwnd () == GetSafeHwnd ());
	ASSERT (lpszName != NULL);

	if (m_bFlat)
	{
		ASSERT (uiImageId == -1);
		uiImageId = (UINT) -1;
	}
	else if (m_sizeImage != CSize (0, 0))
	{
		ASSERT (uiImageId != (UINT) -1);
	}

	m_arTabs.SetAtGrow (m_iTabsNum ++, 
		new CTabWndInfo (lpszName, uiImageId, pNewWnd));

	if (!m_bFlat)
	{
		CRect rectEmpty (0, 0, 0, 0);
		m_ToolTip.AddTool (this, lpszName, &rectEmpty, m_iTabsNum);
	}

	RecalcLayout ();

	if (m_iTabsNum == 1)
	{
		//----------------------------------------
		// First tab automatically becames active:
		//----------------------------------------
		SetActiveTab (0);
	}

	if (GetActiveWnd () != pNewWnd)
	{
		pNewWnd->ShowWindow (SW_HIDE);
	}
}
//***************************************************************************************
void CTabWnd::AddTab2(CWnd* pTabWnd, UINT uiResTabLabel, UINT uiImageId)
{
	CString strLabel;
	strLabel.LoadString (uiResTabLabel);

	AddTab (pTabWnd, strLabel, uiImageId);
}
//***************************************************************************************
BOOL CTabWnd::RemoveTab (int iTab)
{
	if (iTab < 0 || iTab >= m_iTabsNum)
	{
		TRACE(_T("RemoveTab: illegal tab number %d\n"), iTab);
		return FALSE;
	}

	if (m_iTabsNum == 1)
	{
		RemoveAllTabs ();
		return TRUE;
	}

	CTabWndInfo* pTab = (CTabWndInfo*) m_arTabs [iTab];
	ASSERT_VALID (pTab);

	//----------------------------
	// Detach tab from collection:
	//----------------------------
	m_arTabs.RemoveAt (iTab);
	m_iTabsNum --;

	//-----------------------------------
	// Destroy tab window and delete tab:
	//-----------------------------------
	ASSERT_VALID (pTab->m_pWnd);
	pTab->m_pWnd->DestroyWindow ();
	delete pTab;

	int iActiveTab = -1;
	if (m_iActiveTab >= iTab)
	{
		iActiveTab = max (0, min (m_iTabsNum, m_iActiveTab - 1));
		m_iActiveTab = -1;
	}

	RecalcLayout ();
	SetActiveTab (iActiveTab);
	GetParent ()->SendMessage (BCGM_CHANGE_ACTIVE_TAB, m_iActiveTab);

	return TRUE;
}
//***************************************************************************************
void CTabWnd::RemoveAllTabs ()
{
	for (int i = 0; i < m_iTabsNum; i ++)
	{
		CTabWndInfo* pTab = (CTabWndInfo*) m_arTabs [i];
		ASSERT_VALID (pTab);

		pTab->m_pWnd->DestroyWindow ();
		delete pTab;
	}

	m_arTabs.RemoveAll ();

	m_iTabsNum = 0;
	m_iActiveTab = -1;

	RecalcLayout ();
}
//***************************************************************************************
BOOL CTabWnd::SetActiveTab (int iTab)
{
	if (iTab < 0 || iTab >= m_iTabsNum)
	{
		TRACE(_T("SetActiveTab: illegal tab number %d\n"), iTab);
		return FALSE;
	}

	if (m_iActiveTab == iTab)	// Already active, do nothing
	{
		return TRUE;
	}

	if (m_iActiveTab != -1)
	{
		//--------------------
		// Hide active window:
		//--------------------
		GetActiveWnd()->ShowWindow (SW_HIDE);
	}

	m_iActiveTab = iTab;
	
	//------------------------
	// Show new active window:
	//------------------------
	HideActiveWindowHorzScrollBar ();

	CWnd* pWndActive = GetActiveWnd ();
	ASSERT_VALID (pWndActive);

	pWndActive->ShowWindow (SW_SHOW);

	//----------------------------------------------------------------------
	// Small trick: to adjust active window scroll sizes, I should change an
	// active window size twice (+1 pixel and -1 pixel):
	//----------------------------------------------------------------------
	pWndActive->SetWindowPos (NULL,
			-1, -1,
			m_rectWndArea.Width (), m_rectWndArea.Height (),
			SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
	pWndActive->SetWindowPos (NULL,
			-1, -1,
			m_rectWndArea.Width () - 1, m_rectWndArea.Height (),
			SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);

	EnsureVisible (m_iActiveTab);

	if (m_bFlat)
	{
		SynchronizeScrollBar ();
	}

	//-------------
	// Redraw tabs:
	//-------------
	Invalidate ();
	UpdateWindow ();

	///// By Uladzimir Liashkevich
	CView* pActiveView = DYNAMIC_DOWNCAST (CView, pWndActive);
	if (pActiveView != NULL)
	{
		CFrameWnd* pFrame = pActiveView->GetParentFrame ();
		ASSERT_VALID (pFrame);

		pFrame->SetActiveView (pActiveView);
	}
	else
	{
		pWndActive->SetFocus ();
	}
	////

	return TRUE;
}
//***************************************************************************************
void CTabWnd::AdjustTabs ()
{
	m_nTabsTotalWidth = 0;

	if (m_iTabsNum == 0)
	{
		return;
	}

	//-------------------------
	// Define tab's full width:
	//-------------------------
	CClientDC dc (this);

	CFont* pOldFont = dc.SelectObject (m_bFlat ? 
		&m_fntTabsBold : &globalData.fontRegular);
	ASSERT(pOldFont != NULL);

	CRect rectClient;
	GetClientRect (&rectClient);

	//----------------------------------------------
	// First, try set all tabs in its original size:
	//----------------------------------------------
	int x = m_rectTabsArea.left - m_nTabsHorzOffset;
	for (int i = 0; i < m_iTabsNum; i ++)
	{
		CTabWndInfo* pTab = (CTabWndInfo*) m_arTabs [i];
		ASSERT_VALID (pTab);

		pTab->m_nFullWidth = m_sizeImage.cx + IMAGE_MARGIN +
				dc.GetTextExtent (pTab->m_strText).cx + 2 * TEXT_MARGIN;

		pTab->m_rect = CRect (CPoint (x, m_rectTabsArea.top),
						CSize (pTab->m_nFullWidth, m_rectTabsArea.Height () - 2));

		if (m_location == LOCATION_TOP)
		{
			pTab->m_rect.OffsetRect (0, 2);
		}

		if (!m_bFlat)
		{
			m_ToolTip.SetToolRect (this, i + 1, CRect (0, 0, 0, 0));
		}

		x += pTab->m_rect.Width () + 1;
		m_nTabsTotalWidth += pTab->m_rect.Width () + 1;

		if (m_bFlat)
		{
			//--------------------------------------------
			// In the flat mode tab is overlapped by next:
			//--------------------------------------------
			pTab->m_rect.right += m_nTabsHeight / 2;
		}

		pTab->m_strDisplayedText = pTab->m_strText;
	}

	if (m_bFlat || x < m_rectTabsArea.right)
	{
		m_nTabsTotalWidth += m_nTabsHeight / 2;
		dc.SelectObject(pOldFont);
		return;
	}

	//-----------------------------------------
	// Not enouth space to show the whole text.
	//-----------------------------------------
	int nTabWidth = m_rectTabsArea.Width () / m_iTabsNum;

	//------------------------------------
	// May be it's too wide for some tabs?
	//------------------------------------
	int nRest = 0;
	int nCuttedTabsNum = m_iTabsNum;

	for (i = 0; i < m_iTabsNum; i ++)
	{
		CTabWndInfo* pTab = (CTabWndInfo*) m_arTabs [i];
		ASSERT_VALID (pTab);

		if (pTab->m_nFullWidth < nTabWidth)
		{
			nRest += nTabWidth - pTab->m_nFullWidth;
			nCuttedTabsNum --;
		}
	}

	if (nCuttedTabsNum <= 0)
	{
		//ASSERT (FALSE);	// Somethin wrong
		dc.SelectObject(pOldFont);
		return;
	}

	nTabWidth += nRest / nCuttedTabsNum;

	//----------------------------------
	// Last pass: set actual rectangles:
	//----------------------------------
	x = m_rectTabsArea.left;
	for (i = 0; i < m_iTabsNum; i ++)
	{
		CTabWndInfo* pTab = (CTabWndInfo*) m_arTabs [i];
		ASSERT_VALID (pTab);

		BOOL bIsTrucncated = pTab->m_nFullWidth > nTabWidth;
		int nCurrTabWidth = (bIsTrucncated) ? nTabWidth : pTab->m_nFullWidth;

		if (bIsTrucncated)
		{
			TruncateText (&dc, pTab->m_strDisplayedText, 
				nCurrTabWidth - TEXT_MARGIN - IMAGE_MARGIN - m_sizeImage.cx);
		}

		if (nTabWidth < m_sizeImage.cx + IMAGE_MARGIN)
		{
			// Too narrow!
			nCurrTabWidth = (m_rectTabsArea.Width () + TAB_BORDER_SIZE * 2) / m_iTabsNum;
			pTab->m_strDisplayedText.Empty ();
		}
		else
		{
			if (pTab->m_strDisplayedText.IsEmpty ())
			{
				nCurrTabWidth = m_sizeImage.cx + 2 * TEXT_MARGIN;
			}
		}

		pTab->m_rect = CRect (CPoint (x, m_rectTabsArea.top),
						CSize (nCurrTabWidth, m_rectTabsArea.Height ()));

		if (!m_bFlat)
		{
			pTab->m_rect.bottom -= 2;
			m_ToolTip.SetToolRect (this, i + 1, 
				bIsTrucncated ? pTab->m_rect : CRect (0, 0, 0, 0));
		}

		x += nCurrTabWidth + 1;
	}

	dc.SelectObject(pOldFont);
}
//***************************************************************************************
void CTabWnd::Draw3DTab (CDC* pDC, CTabWndInfo* pTab, BOOL bActive)
{
	ASSERT_VALID (pTab);
	ASSERT_VALID (pDC);

	const int iVertOffset = 2;
	const int iHorzOffset = 2;

	CRect rectTab = pTab->m_rect;
	
	if (!bActive)
	{
		if (m_location == LOCATION_BOTTOM)
		{
			rectTab.bottom -= iVertOffset;
		}
		else
		{
			rectTab.top += iVertOffset;
		}
	}

	CPen penLight (PS_SOLID, 1, ::GetSysColor (COLOR_3DHILIGHT));
	CPen penDkGray (PS_SOLID, 1, ::GetSysColor (COLOR_3DSHADOW));

	CPen* pOldPen = NULL;

	if (m_location == LOCATION_BOTTOM)
	{
		pOldPen = (CPen*) pDC->SelectObject (&penLight);
		ASSERT(pOldPen != NULL);

		pDC->MoveTo (rectTab.left, rectTab.top);
		pDC->LineTo (rectTab.left, rectTab.bottom - iVertOffset);

		pDC->SelectStockObject (BLACK_PEN);

		pDC->LineTo (rectTab.left + iHorzOffset, rectTab.bottom);
		pDC->LineTo (rectTab.right - iHorzOffset, rectTab.bottom);
		pDC->LineTo (rectTab.right, rectTab.bottom - iVertOffset);
		pDC->LineTo (rectTab.right, rectTab.top - 1);

		pDC->SelectObject(&penDkGray);

		pDC->MoveTo (rectTab.left + iHorzOffset + 1, rectTab.bottom - 1);
		pDC->LineTo (rectTab.right - iHorzOffset, rectTab.bottom - 1);
		pDC->LineTo (rectTab.right - 1, rectTab.bottom - iVertOffset);
		pDC->LineTo (rectTab.right - 1, rectTab.top - 1);
	}
	else
	{
		// By Brian Palmer:
		pOldPen = (CPen*) pDC->SelectStockObject (BLACK_PEN);
		ASSERT(pOldPen != NULL);

		rectTab.bottom += iVertOffset;
		
		pDC->MoveTo (rectTab.right, bActive ? rectTab.bottom : rectTab.bottom - 1);
		pDC->LineTo (rectTab.right, rectTab.top + iVertOffset);
		pDC->LineTo (rectTab.right - iHorzOffset, rectTab.top);
		
		pDC->SelectObject (&penLight);
		
		pDC->LineTo (rectTab.left + iHorzOffset, rectTab.top);
		
		pDC->LineTo (rectTab.left, rectTab.top + iVertOffset);
		pDC->LineTo (rectTab.left, rectTab.bottom);
		
		pDC->SelectObject (&penDkGray);
		
		pDC->MoveTo (rectTab.right - 1, bActive ? rectTab.bottom : rectTab.bottom - 1);
		pDC->LineTo (rectTab.right - 1, rectTab.top + iVertOffset - 1);
 	}

	if (bActive)
	{
		const int iBarHeight = 3;
		const int y = (m_location == LOCATION_BOTTOM) ? 
			(rectTab.top - iBarHeight) : (rectTab.bottom);

		pDC->FillRect (CRect (CPoint (rectTab.left, y), 
							CSize (rectTab.Width () - 1, iBarHeight)), &globalData.brBtnFace);
	}

	if (m_sizeImage.cx + IMAGE_MARGIN <= rectTab.Width ())
	{
		if (m_Images.GetSafeHandle () != NULL || m_hImageList != NULL)
		{
			//----------------------
			// Draw the tab's image:
			//----------------------
			CRect rectImage = rectTab;

			rectImage.top += (rectTab.Height () - m_sizeImage.cy) / 2;
			rectImage.bottom = rectImage.top + m_sizeImage.cy;

			rectImage.left += IMAGE_MARGIN;
			rectImage.right = rectImage.left + m_sizeImage.cx;

			CImageList* pImageList = 
				(m_Images.GetSafeHandle () != NULL) ?
					&m_Images : CImageList::FromHandle (m_hImageList);
			ASSERT_VALID (pImageList);

			pImageList->Draw (pDC, pTab->m_uiIcon, rectImage.TopLeft (), ILD_TRANSPARENT);
		}

		//------------------------------
		// Finally, draw the tab's text:
		//------------------------------
		CRect rcText = rectTab;
		rcText.left += m_sizeImage.cx + 2 * TEXT_MARGIN;

		pDC->DrawText (pTab->m_strDisplayedText, 
							rcText, DT_SINGLELINE | DT_LEFT | DT_VCENTER);
	}

	pDC->SelectObject (pOldPen);
}
//***************************************************************************************
void CTabWnd::DrawFlatTab (CDC* pDC, CTabWndInfo* pTab, BOOL /*bActive*/)
{
	ASSERT_VALID (pTab);
	ASSERT_VALID (pDC);

	CRect rectTab = pTab->m_rect;
	rectTab.bottom --;

	//----------------
	// Draw tab edges:
	//----------------
	#define POINTS_NUM	4
	POINT pts [POINTS_NUM];

	pts [0].x = rectTab.left;
	pts [0].y = rectTab.top;

	pts [1].x = rectTab.left + m_nTabsHeight / 2;
	pts [1].y = rectTab.bottom;

	pts [2].x = rectTab.right - m_nTabsHeight / 2;
	pts [2].y = rectTab.bottom;

	pts [3].x = rectTab.right;
	pts [3].y = rectTab.top;

	pDC->Polygon (pts, POINTS_NUM);

	//---------------
	// Draw tab text:
	//---------------
	pDC->DrawText (pTab->m_strDisplayedText, 
					rectTab, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}
//***************************************************************************************
void CTabWnd::OnLButtonDown(UINT nFlags, CPoint point) 
{
	CTangramContainerWnd::OnLButtonDown(nFlags, point);

	if (m_rectTabSplitter.PtInRect (point))
	{
		m_bTrackSplitter = TRUE;
		SetCapture ();
		return;
	}

	CWnd* pWndParent = GetParent ();
	ASSERT_VALID (pWndParent);

	for (int i = 0; i < m_iTabsNum; i ++)
	{
		CTabWndInfo* pTab = (CTabWndInfo*) m_arTabs [i];
		ASSERT_VALID (pTab);

		if (pTab->m_rect.PtInRect (point))
		{
			if (i != m_iActiveTab && m_rectTabsArea.PtInRect (point))
			{
				SetActiveTab (i);
				pWndParent->SendMessage (BCGM_CHANGE_ACTIVE_TAB, i);
			}

			return;
		}
	}

	CWnd* pWndTarget = FindTargetWnd (point);
	if (pWndTarget == NULL)
	{
		return;
	}

	ASSERT_VALID (pWndTarget);

	MapWindowPoints (pWndTarget, &point, 1);
	pWndTarget->SendMessage (WM_LBUTTONDOWN, nFlags, 
							MAKELPARAM (point.x, point.y));
}
//***************************************************************************************
void CTabWnd::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
	CTangramContainerWnd::OnLButtonDblClk(nFlags, point);

	CWnd* pWndTarget = FindTargetWnd (point);
	if (pWndTarget == NULL)
	{
		return;
	}

	ASSERT_VALID (pWndTarget);
	if(m_pBoard)
	{
		m_pBoard->ShowPropertyPage();
		MapWindowPoints (pWndTarget, &point, 1);
		pWndTarget->SendMessage (WM_LBUTTONDBLCLK, nFlags, 
								MAKELPARAM (point.x, point.y));
	}
}
//****************************************************************************************
int CTabWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CTangramContainerWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	if (m_bFlat)
	{
		//-------------------
		// Create scrollbars:
		//-------------------
		CRect rectDummy (0, 0, 0, 0);
		m_wndScrollTabs.Create (WS_CHILD | WS_VISIBLE | SBS_HORZ, rectDummy,
			this, (UINT) -1);

		if (m_bSharedScroll)
		{
			m_wndScrollWnd.Create (WS_CHILD | WS_VISIBLE | SBS_HORZ, rectDummy,
				this, (UINT) -1);
		}

		//---------------------
		// Create active brush:
		//---------------------
		m_brActiveTab.CreateSolidBrush (GetActiveTabColor ());
	}
	else
	{
		if(m_pBoard)
		{
			HBITMAP hBmp = m_pBoard->GetBitmap();
			if(hBmp)
				SetImageList(hBmp);
		}
		m_ToolTip.Create (this, TTS_ALWAYSTIP);
		m_ToolTip.Activate (TRUE);
		m_ToolTip.BringWindowToTop();
	}

	SetTabsHeight ();
	return 0;
}
//***************************************************************************************
BOOL CTabWnd::SetImageList (UINT uiID, int cx, COLORREF clrTransp)
{
	if (m_bFlat)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	CBitmap bmp;
	if (!bmp.LoadBitmap (uiID))
	{
		TRACE(_T("CTabWnd::SetImageList Can't load bitmap: %x\n"), uiID);
		return FALSE;
	}

	BITMAP bmpObj;
	bmp.GetBitmap (&bmpObj);

	UINT nFlags = (clrTransp == (COLORREF) -1) ? 0 : ILC_MASK;

	switch (bmpObj.bmBitsPixel)
	{
	case 4:
	default:
		nFlags |= ILC_COLOR4;
		break;

	case 8:
		nFlags |= ILC_COLOR8;
		break;

	case 16:
		nFlags |= ILC_COLOR16;
		break;

	case 24:
		nFlags |= ILC_COLOR24;
		break;

	case 32:
		nFlags |= ILC_COLOR32;
		break;
	}

	m_Images.Create (cx, bmpObj.bmHeight, nFlags, 0, 0);
	m_Images.Add (&bmp, clrTransp);

	m_sizeImage = CSize (cx, bmpObj.bmHeight);

	SetTabsHeight ();
	return TRUE;
}

BOOL CTabWnd::SetImageList(HBITMAP hBitmap, int cx, COLORREF clrTransp)
{
	if (m_bFlat)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	CBitmap bmp ;//= *(CBitmap::FromHandle(hBitmap));
	bmp.Attach(hBitmap);
	//if (!bmp.LoadBitmap (uiID))
	//{
	//	TRACE(_T("CTabWnd::SetImageList Can't load bitmap: %x\n"), uiID);
	//	return FALSE;
	//}

	BITMAP bmpObj;
	bmp.GetBitmap (&bmpObj);

	UINT nFlags = (clrTransp == (COLORREF) -1) ? 0 : ILC_MASK;

	switch (bmpObj.bmBitsPixel)
	{
	case 4:
	default:
		nFlags |= ILC_COLOR4;
		break;

	case 8:
		nFlags |= ILC_COLOR8;
		break;

	case 16:
		nFlags |= ILC_COLOR16;
		break;

	case 24:
		nFlags |= ILC_COLOR24;
		break;

	case 32:
		nFlags |= ILC_COLOR32;
		break;
	}

	m_Images.Create (cx, bmpObj.bmHeight, nFlags, 0, 0);
	m_Images.Add (&bmp, clrTransp);

	m_sizeImage = CSize (cx, bmpObj.bmHeight);

	SetTabsHeight ();
	return TRUE;
}
//***************************************************************************************
BOOL CTabWnd::SetImageList (HIMAGELIST hImageList)
{
	if (m_bFlat)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	ASSERT (hImageList != NULL);
	ASSERT (m_Images.GetSafeHandle () == NULL);

	CImageList* pImageList = CImageList::FromHandle (hImageList);
	if (pImageList == NULL)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	IMAGEINFO info;
	pImageList->GetImageInfo (0, &info);

	CRect rectImage = info.rcImage;
	m_sizeImage = rectImage.Size ();

	m_hImageList = hImageList;

	SetTabsHeight ();
	return TRUE;
}
//***************************************************************************************
BOOL CTabWnd::OnEraseBkgnd(CDC* pDC)
{
	if (m_iTabsNum == 0)
	{
		CRect rectClient;
		GetClientRect (rectClient);
		pDC->FillRect (rectClient, &globalData.brBtnFace);
	}

	return TRUE;
}
//****************************************************************************************
BOOL CTabWnd::PreTranslateMessage(MSG* pMsg) 
{
   	switch (pMsg->message)
	{
	case WM_KEYDOWN:
		if (m_iActiveTab != -1 &&
			::GetAsyncKeyState (VK_CONTROL) & 0x8000)	// Ctrl is pressed
		{
			switch (pMsg->wParam)
			{
			case VK_NEXT:
				SetActiveTab (m_iActiveTab == m_iTabsNum - 1 ? 0 : m_iActiveTab + 1);
				GetActiveWnd ()->SetFocus ();
				GetParent ()->SendMessage (BCGM_CHANGE_ACTIVE_TAB, m_iActiveTab);
				break;

			case VK_PRIOR:
				SetActiveTab (m_iActiveTab == 0 ? m_iTabsNum - 1 : m_iActiveTab - 1);
				GetActiveWnd ()->SetFocus ();
				GetParent ()->SendMessage (BCGM_CHANGE_ACTIVE_TAB, m_iActiveTab);
				break;
			}
		}
		// Continue....

	case WM_SYSKEYDOWN:
	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
	case WM_MBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_RBUTTONUP:
	case WM_MBUTTONUP:
	case WM_NCLBUTTONDOWN:
	case WM_NCRBUTTONDOWN:
	case WM_NCMBUTTONDOWN:
	case WM_NCLBUTTONUP:
	case WM_NCRBUTTONUP:
	case WM_NCMBUTTONUP:
	case WM_MOUSEMOVE:
		if (!m_bFlat)
		{
			m_ToolTip.RelayEvent(pMsg);
		}
		break;
	}

	return CTangramContainerWnd::PreTranslateMessage(pMsg);
}
//****************************************************************************************
CWnd* CTabWnd::GetTabWnd (int iTab) const
{
	if (iTab >= 0 && iTab < m_iTabsNum)
	{
		CTabWndInfo* pTab = (CTabWndInfo*) m_arTabs [iTab];
		ASSERT_VALID (pTab);

		return pTab->m_pWnd;
	}
	else
	{
		return NULL;
	}
}

BOOL CTabWnd::SetTabWnd (int iTab,CWnd* m_pNewWnd) 
{
	if (iTab >= 0 && iTab < m_iTabsNum)
	{
		CWnd* m_pOldWnd = NULL;
		CTabWndInfo* pTab = (CTabWndInfo*) m_arTabs [iTab];
		ASSERT_VALID (pTab);

		m_pOldWnd = pTab->m_pWnd;
		//m_pOldWnd -> DestroyWindow();
		pTab->m_pWnd = m_pNewWnd;
		
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}
//******************************************************************************************
CWnd* CTabWnd::GetActiveWnd () const
{
	return m_iActiveTab == -1 ? 
		NULL : 
		((CTabWndInfo*) m_arTabs [m_iActiveTab])->m_pWnd;
}
//******************************************************************************************
void CTabWnd::TruncateText (CDC* pDC, CString& strText, int nMaxWidth)
//--------------
// By Alan Fritz
//--------------
{
	ASSERT_VALID (pDC);

	if (strText.IsEmpty ())
	{
		return;
	}

	CString strFirstChar = strText.Left (2);
	if (pDC->GetTextExtent (strFirstChar).cx > nMaxWidth)
	{
		// Even first char can't be displayed!
		strText.Empty ();
		return;
	}

	const CString strEllipses (_T("..."));
	CString strNewText (strText);

	while (strText.GetLength () > 2 &&
		pDC->GetTextExtent (strNewText).cx > nMaxWidth)
	{
		strText = strText.Left (strText.GetLength () - 2);
		strNewText = strText + strEllipses;
	}

	if (strText.GetLength () == 2)
	{
		// Start remove dots from ellipses...
		while (!strNewText.IsEmpty () &&
			pDC->GetTextExtent (strNewText).cx > nMaxWidth)
		{
			strNewText = strNewText.Left (strNewText.GetLength () - 2);
			if(strNewText.GetLength() < 2)
				strNewText = _T("");
		}
	}

	strText = strNewText;
}
//******************************************************************************************
void CTabWnd::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	if (!m_bFlat)
	{
		CTangramContainerWnd::OnHScroll (nSBCode, nPos, pScrollBar);
		return;
	}

	if (pScrollBar->GetSafeHwnd () == m_wndScrollWnd.GetSafeHwnd ())
	{
		static BOOL bInsideScroll = FALSE;

		if (m_iActiveTab != -1 && !bInsideScroll)
		{
			CWnd* pWndActive = GetActiveWnd ();
			ASSERT_VALID (pWndActive);

			WPARAM wParam = MAKEWPARAM (nSBCode, nPos);

			//----------------------------------
			// Pass scroll to the active window:
			//----------------------------------
			bInsideScroll = TRUE;
			pWndActive->SendMessage (WM_HSCROLL, wParam, 0);
			bInsideScroll = FALSE;

			m_wndScrollWnd.SetScrollPos (pWndActive->GetScrollPos (SB_HORZ));

			HideActiveWindowHorzScrollBar ();
			GetParent ()->SendMessage (BCGM_ON_HSCROLL, wParam);
		}

		return;
	}

	if (pScrollBar->GetSafeHwnd () != m_wndScrollTabs.GetSafeHwnd ())
	{
		CTangramContainerWnd::OnHScroll (nSBCode, nPos, pScrollBar);
		return;
	}

	int nPrevOffset = m_nTabsHorzOffset;
	const int nScrollOffset = 20;

	switch (nSBCode)
	{
	case SB_LINELEFT:
		m_nTabsHorzOffset -= nScrollOffset;
		break;

	case SB_LINERIGHT:
		m_nTabsHorzOffset += nScrollOffset;
		break;

	default:
		AdjustTabsScroll ();
		return;
	}

	m_nTabsHorzOffset = min (max (0, m_nTabsHorzOffset), m_nTabsHorzOffsetMax);
	if (nPrevOffset != m_nTabsHorzOffset)
	{
		AdjustTabs ();
		InvalidateRect (m_rectTabsArea);
		UpdateWindow ();

		if (m_nTabsHorzOffset == 0)
		{
			m_wndScrollTabs.EnableScrollBar ();
			m_wndScrollTabs.EnableScrollBar (ESB_DISABLE_LTUP);
		}
		else if (m_nTabsHorzOffset == m_nTabsHorzOffsetMax)
		{
			m_wndScrollTabs.EnableScrollBar ();
			m_wndScrollTabs.EnableScrollBar (ESB_DISABLE_RTDN);
		}
	}
}
//******************************************************************************************
CWnd* CTabWnd::FindTargetWnd (const CPoint& point)
{
	if (point.y < m_nTabsHeight)
	{
		return NULL;
	}

	for (int i = 0; i < m_iTabsNum; i ++)
	{
		CTabWndInfo* pTab = (CTabWndInfo*) m_arTabs [i];
		ASSERT_VALID (pTab);

		if (pTab->m_rect.PtInRect (point))
		{
			return NULL;
		}
	}

	CWnd* pWndParent = GetParent ();
	ASSERT_VALID (pWndParent);

	return pWndParent;
}
//************************************************************************************
void CTabWnd::AdjustTabsScroll ()
{
	ASSERT_VALID (this);

	if (!m_bFlat)
	{
		m_nTabsHorzOffset = 0;
		return;
	}

	if (m_iTabsNum == 0)
	{
		m_wndScrollTabs.EnableScrollBar (ESB_DISABLE_BOTH);
		m_nTabsHorzOffsetMax = 0;
		m_nTabsHorzOffset = 0;
		return;
	}

	int nPrevHorzOffset = m_nTabsHorzOffset;

	m_nTabsHorzOffsetMax = max (0, m_nTabsTotalWidth - m_rectTabsArea.Width ());
	m_nTabsHorzOffset = max (0, min (m_nTabsHorzOffset, m_nTabsHorzOffsetMax));

	m_wndScrollTabs.EnableScrollBar ();
	if (m_nTabsHorzOffset <= 0)
	{
		m_nTabsHorzOffset = 0;
		m_wndScrollTabs.EnableScrollBar (ESB_DISABLE_LTUP);
	}

	if (m_nTabsHorzOffset >= m_nTabsHorzOffsetMax)
	{
		m_nTabsHorzOffset = m_nTabsHorzOffsetMax;
		m_wndScrollTabs.EnableScrollBar (ESB_DISABLE_RTDN);
	}

	if (nPrevHorzOffset != m_nTabsHorzOffset)
	{
		AdjustTabs ();
		InvalidateRect (m_rectTabsArea);
		UpdateWindow ();
	}
}
//*************************************************************************************
void CTabWnd::RecalcLayout ()
{
	if (GetSafeHwnd () == NULL)
	{
		return;
	}

	ASSERT_VALID (this);

	CRect rectClient;
	GetClientRect (rectClient);

	m_rectTabsArea = rectClient;
	m_rectTabsArea.DeflateRect (TAB_BORDER_SIZE, 0);

	if (m_bFlat)
	{
		m_rectTabsArea.bottom -= TAB_BORDER_SIZE - 1;
		m_rectTabsArea.top = m_rectTabsArea.bottom - m_nTabsHeight;

		int nScrollBarWidth = ::GetSystemMetrics (SM_CXHSCROLL) * 2;
		int nScrollBarHeight = m_rectTabsArea.Height () - 3;

		m_rectTabsArea.left += nScrollBarWidth + 1;
		m_rectTabsArea.right -= TAB_BORDER_SIZE;

		if (m_rectTabsArea.right < m_rectTabsArea.left)
		{
			nScrollBarWidth = 0;
			m_rectTabsArea.left = rectClient.left + TAB_BORDER_SIZE + 1;
			m_rectTabsArea.right = rectClient.right - TAB_BORDER_SIZE - 1;
		}

		if (m_rectTabsArea.Height () + TAB_BORDER_SIZE > rectClient.Height ())
		{
			nScrollBarHeight = 0;
			m_rectTabsArea.left = 0;
			m_rectTabsArea.right = 0;
		}

		ASSERT (m_wndScrollTabs.GetSafeHwnd () != NULL);

		m_wndScrollTabs.SetWindowPos (NULL,
			rectClient.left + TAB_BORDER_SIZE + 1, m_rectTabsArea.top + 1,
			nScrollBarWidth, nScrollBarHeight,
			SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW);
	}
	else
	{
		if (m_location == LOCATION_BOTTOM)
		{
			m_rectTabsArea.top = m_rectTabsArea.bottom - m_nTabsHeight;
		}
		else
		{
			m_rectTabsArea.bottom = m_rectTabsArea.top + m_nTabsHeight;
		}

		m_rectTabsArea.right -= 2 * TAB_BORDER_SIZE;
	}

	m_nScrollBarRight = m_rectTabsArea.right - ::GetSystemMetrics (SM_CXVSCROLL);

	m_rectWndArea = rectClient;
	m_rectWndArea.DeflateRect (TAB_BORDER_SIZE + 1, TAB_BORDER_SIZE + 1);
	
	if (m_bFlat)
	{
		m_rectWndArea.bottom = m_rectTabsArea.top;
	}
	else
	{
		if (m_location == LOCATION_BOTTOM)
		{
			m_rectWndArea.bottom = m_rectTabsArea.top - 2 * TAB_BORDER_SIZE + 1;
			if (m_bFlatFrame)
			{
				m_rectWndArea.bottom ++;
			}
		}
		else
		{
			m_rectWndArea.top = m_rectTabsArea.bottom + 2 * TAB_BORDER_SIZE - 1;
			if (m_bFlatFrame)
			{
				m_rectWndArea.top --;
			}
		}
	}

	for (int i = 0; i < m_iTabsNum; i ++)
	{
		CTabWndInfo* pTab = (CTabWndInfo*) m_arTabs [i];
		ASSERT_VALID (pTab);

		pTab->m_pWnd->SetWindowPos (NULL,
			m_rectWndArea.left, m_rectWndArea.top,
			m_rectWndArea.Width () - 1, m_rectWndArea.Height (),
			SWP_NOACTIVATE | SWP_NOZORDER);
	}

	AdjustWndScroll ();
	AdjustTabs ();
	AdjustTabsScroll ();

	Invalidate ();
	UpdateWindow ();
}
//*************************************************************************************
void CTabWnd::AdjustWndScroll ()
{
	if (!m_bSharedScroll)
	{
		return;
	}

	ASSERT_VALID (this);

	CRect rectScroll = m_rectTabsArea;

	if (m_nHorzScrollWidth >= MIN_SROLL_WIDTH)
	{
		rectScroll.right = m_nScrollBarRight;
		rectScroll.left = rectScroll.right - m_nHorzScrollWidth;
		rectScroll.bottom -= 2;

		m_rectTabSplitter = rectScroll;
		m_rectTabSplitter.top ++;
		m_rectTabSplitter.right = rectScroll.left;
		m_rectTabSplitter.left = m_rectTabSplitter.right - SPLITTER_WIDTH;

		m_rectTabsArea.right = m_rectTabSplitter.left;
		
		ASSERT (!m_rectTabSplitter.IsRectEmpty ());
	}
	else
	{
		rectScroll.SetRectEmpty ();
		m_rectTabSplitter.SetRectEmpty ();
	}

	m_wndScrollWnd.SetWindowPos (NULL,
		rectScroll.left, rectScroll.top,
		rectScroll.Width (), rectScroll.Height (),
		SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS);
}
//***************************************************************************************
BOOL CTabWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{
	if (m_bFlat && !m_rectTabSplitter.IsRectEmpty ())
	{
		CPoint ptCursor;
		::GetCursorPos (&ptCursor);
		ScreenToClient (&ptCursor);

		if (m_rectTabSplitter.PtInRect (ptCursor))
		{
			::SetCursor (globalData.m_hcurStretch);
			return TRUE;
		}
	}
	
	return CTangramContainerWnd::OnSetCursor(pWnd, nHitTest, message);
}
//***************************************************************************************
void CTabWnd::OnLButtonUp(UINT nFlags, CPoint point) 
{
	if (m_bTrackSplitter)
	{
		m_bTrackSplitter = FALSE;
		ReleaseCapture ();
	}
	
	CTangramContainerWnd::OnLButtonUp(nFlags, point);
}
//***************************************************************************************
void CTabWnd::OnMouseMove(UINT nFlags, CPoint point) 
{
	if (m_bTrackSplitter)
	{
		int nSplitterLeftPrev = m_rectTabSplitter.left;

		m_nHorzScrollWidth = min (
			m_nScrollBarRight - m_rectTabsArea.left - SPLITTER_WIDTH, 
			m_nScrollBarRight - point.x);

		m_nHorzScrollWidth = max (MIN_SROLL_WIDTH, m_nHorzScrollWidth);
		AdjustWndScroll ();

		if (m_rectTabSplitter.left > nSplitterLeftPrev)
		{
			CRect rect = m_rectTabSplitter;
			rect.left = nSplitterLeftPrev - 20;
			rect.right = m_rectTabSplitter.left;
			rect.InflateRect (0, TAB_BORDER_SIZE);

			InvalidateRect (rect);
		}

		CRect rectTabSplitter = m_rectTabSplitter;
		rectTabSplitter.InflateRect (0, TAB_BORDER_SIZE);

		InvalidateRect (rectTabSplitter);
		UpdateWindow ();
		AdjustTabsScroll ();
	}
	
	CTangramContainerWnd::OnMouseMove(nFlags, point);
}
//***************************************************************************************
void CTabWnd::OnCancelMode() 
{
	CTangramContainerWnd::OnCancelMode();
	
	if (m_bTrackSplitter)
	{
		m_bTrackSplitter = FALSE;
		ReleaseCapture ();
	}
}
//***********************************************************************************
void CTabWnd::SetActiveTabTextColor (COLORREF clr)
{
	ASSERT (m_bFlat);
	m_clrActiveTabFg = clr;
}
//***********************************************************************************
void CTabWnd::SetActiveTabColor (COLORREF clr)
{
	ASSERT (m_bFlat);

	m_clrActiveTabBk = clr;

	if (m_brActiveTab.GetSafeHandle () != NULL)
	{
		m_brActiveTab.DeleteObject ();
	}

	m_brActiveTab.CreateSolidBrush (GetActiveTabColor ());
}
//**********************************************************************************
void CTabWnd::OnSysColorChange() 
{
	CTangramContainerWnd::OnSysColorChange();

	if (m_bFlat && m_clrActiveTabFg == (COLORREF) -1)
	{
		if (m_brActiveTab.GetSafeHandle () != NULL)
		{
			m_brActiveTab.DeleteObject ();
		}

		m_brActiveTab.CreateSolidBrush (GetActiveTabColor ());

		Invalidate ();
		UpdateWindow ();
	}
}
//***********************************************************************************
BOOL CTabWnd::SynchronizeScrollBar (SCROLLINFO* pScrollInfo/* = NULL*/)
{
	if (!m_bSharedScroll)
	{
		return FALSE;
	}

	ASSERT_VALID (this);

	SCROLLINFO scrollInfo;
	memset (&scrollInfo, 0, sizeof (SCROLLINFO));

	scrollInfo.cbSize = sizeof (SCROLLINFO);
	scrollInfo.fMask = SIF_ALL;

	CWnd* pWndActive = GetActiveWnd ();

	if (pScrollInfo != NULL)
	{
		scrollInfo = *pScrollInfo;
	}
	else if (pWndActive != NULL)
	{
		if (!pWndActive->GetScrollInfo (SB_HORZ, &scrollInfo) ||
			scrollInfo.nMin + (int) scrollInfo.nPage >= scrollInfo.nMax)
		{
			m_wndScrollWnd.EnableScrollBar (ESB_DISABLE_BOTH);
			return TRUE;
		}
	}

	m_wndScrollWnd.EnableScrollBar (ESB_ENABLE_BOTH);
	m_wndScrollWnd.SetScrollInfo (&scrollInfo);

	HideActiveWindowHorzScrollBar ();
	return TRUE;
}
//*************************************************************************************
void CTabWnd::HideActiveWindowHorzScrollBar ()
{
	CWnd* pWnd = GetActiveWnd ();
	if (pWnd == NULL)
	{
		return;
	}

	ASSERT_VALID (pWnd);

	pWnd->ShowScrollBar (SB_HORZ, FALSE);
	pWnd->ModifyStyle (WS_HSCROLL, 0, SWP_DRAWFRAME);
}
//************************************************************************************
void CTabWnd::SetTabsHeight ()
{
	if (m_bFlat)
	{
		m_nTabsHeight = ::GetSystemMetrics (SM_CYHSCROLL) + TEXT_MARGIN / 2;

		CFont* pDefaultFont = 
			CFont::FromHandle ((HFONT) ::GetStockObject (DEFAULT_GUI_FONT));
		ASSERT_VALID (pDefaultFont);

		if (pDefaultFont != NULL)	// Just to be relaxed....
		{
			LOGFONT lfDefault;
			pDefaultFont->GetLogFont (&lfDefault);

			LOGFONT lf;
			memset (&lf, 0, sizeof (LOGFONT));

			lf.lfCharSet = lfDefault.lfCharSet;
			lf.lfHeight = lfDefault.lfHeight;
			_tcscpy (lf.lfFaceName, TABS_FONT);

			CClientDC dc (this);

			TEXTMETRIC tm;

			do
			{
				m_fntTabs.DeleteObject ();
				m_fntTabs.CreateFontIndirect (&lf);

				CFont* pFont = dc.SelectObject (&m_fntTabs);
				ASSERT (pFont != NULL);

				dc.GetTextMetrics (&tm);
				dc.SelectObject (pFont);

				if (tm.tmHeight + TEXT_MARGIN / 2 <= m_nTabsHeight)
				{
					break;
				}

				//------------------
				// Try smaller font:
				//------------------
				if (lf.lfHeight < 0)
				{
					lf.lfHeight ++;
				}
				else
				{
					lf.lfHeight --;
				}
			}
			while (lf.lfHeight != 0);

			//------------------
			// Create bold font:
			//------------------
			lf.lfWeight = FW_BOLD;
			m_fntTabsBold.DeleteObject ();
			m_fntTabsBold.CreateFontIndirect (&lf);
		}
	}
	else
	{
		m_nTabsHeight = (max (m_sizeImage.cy + 2 * IMAGE_MARGIN,
			globalData.GetTextHeight () + TEXT_MARGIN / 2));
	}
}
//*************************************************************************************
void CTabWnd::OnSettingChange(UINT uFlags, LPCTSTR lpszSection) 
{
	CTangramContainerWnd::OnSettingChange(uFlags, lpszSection);
	
	//-----------------------------------------------------------------
	// In the flat modetabs height should be same as scroll bar height
	//-----------------------------------------------------------------
	if (m_bFlat)
	{
		SetTabsHeight ();
		RecalcLayout ();
		SynchronizeScrollBar ();
	}
}
//*************************************************************************************
BOOL CTabWnd::EnsureVisible (int iTab)
{
	if (iTab < 0 || iTab >= m_iTabsNum)
	{
		TRACE(_T("EnsureVisible: illegal tab number %d\n"), iTab);
		return FALSE;
	}

	//---------------------------------------------------------
	// Be sure, that active tab is visible (not out of scroll):
	//---------------------------------------------------------
	if (m_bFlat && m_rectTabsArea.Width () > 0)
	{
		CRect rectActiveTab = ((CTabWndInfo*) m_arTabs [m_iActiveTab])->m_rect;
		BOOL bAdjustTabs = FALSE;

		if (rectActiveTab.left < m_rectTabsArea.left)
		{
			m_nTabsHorzOffset -= (m_rectTabsArea.left - rectActiveTab.left);
			bAdjustTabs = TRUE;
		}
		else if (rectActiveTab.right > m_rectTabsArea.right)
		{
			m_nTabsHorzOffset += (rectActiveTab.right - m_rectTabsArea.right);
			bAdjustTabs = TRUE;
		}

		if (bAdjustTabs)
		{
			AdjustTabs ();
			AdjustTabsScroll ();
		}
	}

	return TRUE;
}
//**********************************************************************************
BOOL CTabWnd::OnNotify (WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
	BOOL bRes = CTangramContainerWnd::OnNotify (wParam, lParam, pResult);

	NMHDR* pNMHDR = (NMHDR*)lParam;
	ASSERT (pNMHDR != NULL);

	if (pNMHDR->code == TTN_SHOW && !m_bFlat)
	{
		m_ToolTip.SetWindowPos (&wndTop, -1, -1, -1, -1,
			SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOSIZE);
	}

	return bRes;
}
//*********************************************************************************
BOOL CTabWnd::GetTabRect (int iTab, CRect& rect) const
{
	if (iTab < 0 || iTab >= m_iTabsNum)
	{
		return FALSE;
	}

	CTabWndInfo* pTab = (CTabWndInfo*) m_arTabs [iTab];
	ASSERT_VALID (pTab);

	rect = pTab->m_rect;
	return TRUE;
}
//********************************************************************************
UINT CTabWnd::GetTabIcon (int iTab) const
{
	if (iTab < 0 || iTab >= m_iTabsNum)
	{
		return (UINT) -1;
	}

	CTabWndInfo* pTab = (CTabWndInfo*) m_arTabs [iTab];
	ASSERT_VALID (pTab);

	return pTab->m_uiIcon;
}
//*********************************************************************************
BOOL CTabWnd::GetTabLabel (int iTab, CString& strLabel) const
// By Richard L. Melton
{
	if (iTab < 0 || iTab >= m_iTabsNum)
	{
		return FALSE;
	};
	
	CTabWndInfo* pTab = (CTabWndInfo*) m_arTabs [iTab];
	ASSERT_VALID (pTab);
	
	strLabel = pTab->m_strText;
	return TRUE;
};
//*********************************************************************************
BOOL CTabWnd::SetTabLabel (int iTab, const CString& strLabel)
// By Richard L. Melton
{
	if (iTab < 0 || iTab >= m_iTabsNum || strLabel.IsEmpty ())
	{
		return FALSE;
	};
	
	CTabWndInfo* pTab = (CTabWndInfo*) m_arTabs [iTab];
	ASSERT_VALID(pTab);
	
	pTab->m_strText = pTab->m_strDisplayedText = strLabel;
	
	RecalcLayout();
	return TRUE;
}

void CTabWnd::ResizeTab(int cx, int cy)
{
	CRect rect;
	CWnd *pParent = (CWnd*)GetParent();
	ASSERT(pParent);
	pParent->GetClientRect(&rect); 
	
	if (pParent->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
	{
		CSplitterWnd *splitter = (CSplitterWnd*)pParent;
		ASSERT(pParent);
		int row, col;
		splitter->IsChildPane(this, &row, &col);
		splitter->RecalcLayout();
	}
	else if (pParent->IsKindOf(RUNTIME_CLASS(CFrameWnd)))
	{
		m_bLockFlag = TRUE;
		pParent->RepositionBars(0, 0xFFFF, AFX_IDW_PANE_FIRST, CWnd::reposQuery, &rect);
		MoveWindow(rect.left, rect.top, rect.Width(), rect.Height());
		m_bLockFlag = FALSE;
	}
	
	m_bLockFlag = FALSE;
}

void CTabWnd::OnClose() 
{
	// TODO: Add your message handler code here and/or call default
	TRACE("TabWnd : %x-CTangramContainerWnd::OnClose()\n",this);
	
	CTangramContainerWnd::OnClose();
}

void CTabWnd::DeleteTabs()
{
	for (int i = 0; i < m_iTabsNum; i ++)
	{
		CTabWndInfo* pTab = (CTabWndInfo*) m_arTabs [i];
		ASSERT_VALID (pTab);
		if(::IsWindow(pTab->m_pWnd->m_hWnd))
			if(!pTab->m_pWnd->IsKindOf(RUNTIME_CLASS(CView)))
			{
				pTab->m_pWnd->DestroyWindow ();
				//delete pTab;
			}

		//else		//pTab->m_pWnd = NULL;
		delete pTab;
	}
	m_arTabs.RemoveAll();
}

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

dean170
Web Developer
China China
No Biography provided

| Advertise | Privacy | Mobile
Web01 | 2.8.140922.1 | Last Updated 15 Dec 2002
Article Copyright 2002 by dean170
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid