Click here to Skip to main content
15,885,096 members
Articles / Desktop Programming / MFC

The Ultimate Toolbox - Updates and User Contributions

Rate me:
Please Sign up or sign in to vote.
4.79/5 (26 votes)
12 Feb 2013CPOL8 min read 254.7K   23.7K   170  
Updates and User Contributions for the Ultimate Toolbox Libraries
// ==========================================================================
// 							   Class Implementation : 
//								COXTabViewContainer 
// ==========================================================================

// Version: 9.3

// This software along with its related components, documentation and files ("The Libraries")
// is � 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement").  Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office.  For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
                          
// //////////////////////////////////////////////////////////////////////////


#include "stdafx.h"
#include "OXTabView.h"


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


COXTabViewContainer* PASCAL GetParentTabViewContainer(CWnd* pWnd,
													  BOOL bOnlyActive/*=TRUE*/)
{
#if defined (_WINDLL)
#if defined (_AFXDLL)
	AFX_MANAGE_STATE(AfxGetAppModuleState());
#else
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
#endif

	ASSERT(pWnd!=NULL);
	HWND hWndParent=::GetParent(pWnd->GetSafeHwnd());
	if(hWndParent==NULL)
		return NULL;

	COXTabViewContainer* pContainer=
		(COXTabViewContainer*)CWnd::FromHandlePermanent(hWndParent);
	if(pContainer!=NULL)
	{
		ASSERT(::IsWindow(pContainer->m_hWnd));
		if(::IsWindow(pContainer->m_hWnd))
		{
			if(::GetWindowLongPtr(pContainer->m_hWnd,GWL_USERDATA)==
				ID_TABVIEWCONTAINER_SIGN)
			{
				if(!bOnlyActive || pContainer->IsActivePage(pWnd))
				{
					return pContainer;
				}
			}
		}
	}
	return NULL;
}

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// COXTabViewContainer
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
IMPLEMENT_DYNCREATE(COXTabViewContainer,CWnd)

COXTabViewContainer::COXTabViewContainer()
{
	EmptyRects();
	m_nLastTabBtnAreaWidth=-1;
	m_nTabBtnAreaOrigin=0;

	m_arrUniqueIDs.Add(1);
	m_nActivePageIndex=-1;

	m_nPressedScrlBtn=NONE;
	m_bIsScrlBtnPressed=FALSE;
	m_nScrollPageTimer=-1;

	m_bIsSplitterPressed=FALSE;

	m_nLastTabBtnAreaWidth=ID_INITABBTNAREAWIDTH;

	if((HFONT)m_fontTabBtnText==NULL)
		m_fontTabBtnText.CreatePointFont(80,_T("MS Sans Serif"));
	if((HFONT)m_fontActiveTabBtnText==NULL && (HFONT)m_fontTabBtnText!=NULL)
	{
		LOGFONT lf;
		if(m_fontTabBtnText.GetLogFont(&lf)!=0)
		{
			lf.lfWeight=FW_BOLD;
			m_fontActiveTabBtnText.CreateFontIndirect(&lf);
		}
	}

	ASSERT((HFONT)m_fontTabBtnText!=NULL && (HFONT)m_fontActiveTabBtnText!=NULL);
}

COXTabViewContainer::~COXTabViewContainer()
{
}

BEGIN_MESSAGE_MAP(COXTabViewContainer, CWnd)
	//{{AFX_MSG_MAP(COXTabViewContainer)
	ON_WM_SIZE()
	ON_WM_PAINT()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_TIMER()
	ON_WM_SETCURSOR()
	ON_WM_ERASEBKGND()
	ON_WM_CANCELMODE()
	ON_WM_DESTROY()
	ON_WM_HSCROLL()
	ON_WM_VSCROLL()
	ON_WM_SETTINGCHANGE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////

BOOL COXTabViewContainer::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, 
								 DWORD dwStyle, const RECT& rect, 
								 CWnd* pParentWnd, UINT nID,
								 CCreateContext* pContext/*=NULL*/)
{
	UNREFERENCED_PARAMETER(lpszClassName);
	UNREFERENCED_PARAMETER(lpszWindowName);
	UNREFERENCED_PARAMETER(pContext);
	return Create(pParentWnd,rect,dwStyle,nID);
}


BOOL COXTabViewContainer::Create(CWnd* pParentWnd, CRect rect/*=CRect(0,0,0,0)*/,
								 DWORD dwStyle/*=WS_CHILD|WS_VISIBLE*/, 
								 UINT nID/*=AFX_IDW_PANE_FIRST*/)
{
	ASSERT(pParentWnd != NULL);
	ASSERT(dwStyle & WS_CHILD);
	ASSERT(nID != 0);

	// the Windows scroll bar styles bits turn on the smart scrollbars
	DWORD dwCreateStyle=dwStyle&~(WS_HSCROLL|WS_VSCROLL);
	dwCreateStyle&=~WS_BORDER;

	dwCreateStyle|=WS_CHILD;

	// define our own window class 
	WNDCLASS wndClass;
	wndClass.style=CS_DBLCLKS; 
    wndClass.lpfnWndProc=AfxWndProc; 
    wndClass.cbClsExtra=0; 
    wndClass.cbWndExtra=0; 
    wndClass.hInstance=AfxGetInstanceHandle(); 
    wndClass.hIcon=0; 
    wndClass.hCursor=::LoadCursor(NULL,IDC_ARROW); 
    wndClass.hbrBackground=(HBRUSH)(COLOR_BTNFACE+1); 
    wndClass.lpszMenuName=NULL; 
	wndClass.lpszClassName=_T("TabViewContainer");
	
	if(!AfxRegisterClass(&wndClass))
		return FALSE;

	if (!CreateEx(WS_EX_CLIENTEDGE,wndClass.lpszClassName,NULL,
		dwCreateStyle,rect.left,rect.top,rect.Width(),rect.Height(),
		pParentWnd->m_hWnd,(HMENU)(INT_PTR)nID,NULL))
	{
		return FALSE;       // create invisible
	}

	// remove WS_EX_CLIENTEDGE style from parent window
	pParentWnd->ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_DRAWFRAME);

	// sign 
	::SetWindowLongPtr(GetSafeHwnd(),GWL_USERDATA,ID_TABVIEWCONTAINER_SIGN);

	SetScrollStyle(0,TRUE);

	CalcLayout();

	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// COXTabViewContainer command routing

BOOL COXTabViewContainer::OnCommand(WPARAM wParam, LPARAM lParam)
{
	if (CWnd::OnCommand(wParam,lParam))
		return TRUE;

	// route commands from the container to the parent frame window
	if(GetParentFrame()!=NULL)
		return (BOOL)GetParentFrame()->SendMessage(WM_COMMAND,wParam,lParam);
	else
		return FALSE;
}

BOOL COXTabViewContainer::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
	if (CWnd::OnNotify(wParam,lParam,pResult))
		return TRUE;

	// route commands from the container to the parent frame window

	if(GetParentFrame()!=NULL)
	{
		*pResult=GetParentFrame()->SendMessage(WM_NOTIFY,wParam,lParam);
		return TRUE;
	}
	else
	{
		*pResult=0;
		return FALSE;
	}
}

//////////////////////////////////////////////////////////////////

void COXTabViewContainer::OnDestroy() 
{
	if(m_nScrollPageTimer!=-1)
	{
		KillTimer(m_nScrollPageTimer);
		m_nScrollPageTimer=-1;
	}

	CWnd::OnDestroy();
}


void COXTabViewContainer::OnSize(UINT nType, int cx, int cy) 
{
	CWnd::OnSize(nType, cx, cy);
	
	// TODO: Add your message handler code here
	if(nType!=SIZE_MINIMIZED && cx>0 && cy>0)
	{
		CalcLayout();

		UpdateScrollSizes();

		CWnd* pWnd=GetActivePage();
		if(pWnd!=NULL)
		{
			ASSERT(::IsWindow(pWnd->m_hWnd));
			if(::IsWindow(pWnd->m_hWnd))
				pWnd->MoveWindow(m_rectPage);
		}

		RedrawContainer();
	}

}


void COXTabViewContainer::OnPaint() 
{
	CPaintDC dc(this); // device context for painting
	
	// TODO: Add your message handler code here

	CWnd* pWnd=GetActivePage();
	if(pWnd!=NULL)
	{
		CRect rect;
		pWnd->GetWindowRect(rect);
		ScreenToClient(rect);
		if(m_rectPage!=rect && !rect.IsRectEmpty())
		{
			CRgn rgnInTheory;
			CRgn rgnInReality;
			if(rgnInTheory.CreateRectRgnIndirect(m_rectPage) &&
				rgnInReality.CreateRectRgnIndirect(rect))
			{
				if(rgnInTheory.CombineRgn(&rgnInTheory,&rgnInReality,
					RGN_DIFF)!=ERROR)
				{
					CBrush* pBrush=NULL;
					CBrush brush;
					HBRUSH hBrush=(HBRUSH)(INT_PTR)::GetClassLongPtr(pWnd->m_hWnd,
						GCL_HBRBACKGROUND);
					if(hBrush==NULL)
					{
						if(brush.CreateSolidBrush(::GetSysColor(COLOR_WINDOW)))
							pBrush=&brush;
						else
							pBrush=dc.GetCurrentBrush();
					}
					else
					{
						pBrush=CBrush::FromHandle(hBrush);
					}
					
					if(pBrush!=NULL)
						dc.FillRgn(&rgnInTheory,pBrush);
				}
			}
		}
	}

	DrawScrollButtons(&dc);
	DrawSplitter(&dc);
	DrawSizeBar(&dc);
	DrawTabBtnArea(&dc);

	// Do not call CWnd::OnPaint() for painting messages
}


void COXTabViewContainer::OnMouseMove(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	
	if(m_nPressedScrlBtn!=NONE)
	{
		int hitTest=HitTest(point);
		// send corresponding messages
		if(((int)m_nPressedScrlBtn==hitTest && !m_bIsScrlBtnPressed) ||
			((int)m_nPressedScrlBtn!=hitTest && m_bIsScrlBtnPressed))
		{
			m_bIsScrlBtnPressed=!m_bIsScrlBtnPressed;
			RedrawScrollButton(m_nPressedScrlBtn);
		}
	}
	else if(m_bIsSplitterPressed)
	{
		CPoint ptMoved=point;
		if(ptMoved.x>m_rectPage.right-ID_MINSCROLLBARWIDTH+ID_SPLITTERWIDTH/2)
			ptMoved.x=m_rectPage.right-ID_MINSCROLLBARWIDTH+ID_SPLITTERWIDTH/2;
		if(ptMoved.x<m_rectScrollToEndBtn.right+ID_SPLITTERWIDTH/2)
			ptMoved.x=m_rectScrollToEndBtn.right+ID_SPLITTERWIDTH/2;

		int nOldSplitterLeft=m_rectSplitter.left;
		m_rectSplitter.left=ptMoved.x-ID_SPLITTERWIDTH/2;
		if(nOldSplitterLeft!=m_rectSplitter.left)
		{
			m_rectSplitter.right=m_rectSplitter.left+ID_SPLITTERWIDTH;
			m_rectScrollBarHorz.left=m_rectSplitter.right;

			m_nLastTabBtnAreaWidth=m_rectSplitter.left-m_rectScrollToEndBtn.right;

			m_rectTabBtnArea.right+=m_rectSplitter.left-nOldSplitterLeft;
			if(m_rectTabBtnArea.right<m_rectTabBtnArea.left)
				m_rectTabBtnArea.right=m_rectTabBtnArea.left;

			if(::IsWindow(m_scrlBarHorz.GetSafeHwnd()))
				m_scrlBarHorz.MoveWindow(m_rectScrollBarHorz);
			RedrawSplitter();

			if(nOldSplitterLeft<m_rectSplitter.left)
			{	
				CRect rect=m_rectSplitter;
				rect.left=nOldSplitterLeft;
				rect.right=m_rectSplitter.left;
				RedrawWindow(rect);

				if(m_nTabBtnAreaOrigin<0)
				{
					ASSERT(GetPageCount()==m_arrTabBtnRects.GetSize());
					rect=m_arrTabBtnRects[GetPageCount()-1];
					rect+=m_rectTabBtnArea.TopLeft();
					if(rect.right+m_nTabBtnAreaOrigin<=m_rectTabBtnArea.right)
					{
						m_nTabBtnAreaOrigin+=
							m_rectSplitter.left-nOldSplitterLeft;
						m_nTabBtnAreaOrigin=
							m_nTabBtnAreaOrigin>0 ? 0 : m_nTabBtnAreaOrigin;
						RedrawTabBtnArea();
					}
				}

			}		
		}
	}

	CWnd::OnMouseMove(nFlags, point);
}


void COXTabViewContainer::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	
	CWnd::OnLButtonDown(nFlags, point);

	int hitTest=HitTest(point);
	if(hitTest>=0 && hitTest<GetPageCount() && hitTest!=GetActivePageIndex())
		SetActivePageIndex(hitTest);
	else
		StartTracking(point);
}


void COXTabViewContainer::OnLButtonUp(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	
	StopTracking(point);

	CWnd::OnLButtonUp(nFlags,point);
}


// v9.3 - update 03 - 64-bit - using OXTPARAM here - see UTB64Bit.h
void COXTabViewContainer::OnTimer(OXTPARAM nIDEvent) 
{
	// TODO: Add your message handler code here and/or call default

	if((int)nIDEvent==m_nScrollPageTimer)
	{
		if(m_nPressedScrlBtn!=NONE && m_bIsScrlBtnPressed)
			ScrollPage(m_nPressedScrlBtn);
	}

	CWnd::OnTimer(nIDEvent);
}


BOOL COXTabViewContainer::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{
	// TODO: Add your message handler code here and/or call default

	CPoint point;
	::GetCursorPos(&point);
	ScreenToClient(&point);
	int hitTest=HitTest(point);
	if(m_bIsSplitterPressed || hitTest==SPLITTER)
	{
		HCURSOR hCursor=AfxGetApp()->
			LoadCursor(MAKEINTRESOURCE(AFX_IDC_HSPLITBAR));
		if(hCursor==NULL)
			hCursor=::LoadCursor(NULL,IDC_SIZEWE);
		SetCursor(hCursor);
		return TRUE;
	}

	return CWnd::OnSetCursor(pWnd, nHitTest, message);
}


BOOL COXTabViewContainer::OnEraseBkgnd(CDC* pDC) 
{
	// TODO: Add your message handler code here and/or call default

	if(GetPageCount()==0)
	{
		CRect rect;
		pDC->GetClipBox(rect);
		pDC->FillSolidRect(rect,::GetSysColor(COLOR_BTNFACE));
	} 
	
	return TRUE;
}


void COXTabViewContainer::OnCancelMode() 
{
	CWnd::OnCancelMode();
	
	// TODO: Add your message handler code here

	CPoint point;
	::GetCursorPos(&point);
	ScreenToClient(&point);
	StopTracking(point);
}


void COXTabViewContainer::OnHScroll(UINT nSBCode, UINT nPos, 
									CScrollBar* pScrollBar) 
{
	// TODO: Add your message handler code here and/or call default

	ASSERT(pScrollBar==GetHorzScrollBar());
	
	CWnd* pWnd=GetActivePage();
	ASSERT(pWnd!=NULL);
	if(pWnd!=NULL)
	{
		ASSERT(::IsWindow(pWnd->m_hWnd));
		pWnd->SendMessage(WM_HSCROLL,MAKEWPARAM(nSBCode,nPos),
			(LPARAM)pScrollBar->m_hWnd);
	}
}

void COXTabViewContainer::OnVScroll(UINT nSBCode, UINT nPos, 
									CScrollBar* pScrollBar) 
{
	// TODO: Add your message handler code here and/or call default

	ASSERT(pScrollBar==GetVertScrollBar());
	
	CWnd* pWnd=GetActivePage();
	ASSERT(pWnd!=NULL);
	if(pWnd!=NULL)
	{
		ASSERT(::IsWindow(pWnd->m_hWnd));
		pWnd->SendMessage(WM_VSCROLL,MAKEWPARAM(nSBCode,nPos),
			(LPARAM)pScrollBar->m_hWnd);
	}
}

void COXTabViewContainer::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
{
	UNREFERENCED_PARAMETER(uFlags);
	UNREFERENCED_PARAMETER(lpszSection);

	CalcLayout();

	UpdateScrollSizes();

	CWnd* pWnd=GetActivePage();
	if(pWnd!=NULL)
	{
		ASSERT(::IsWindow(pWnd->m_hWnd));
		if(::IsWindow(pWnd->m_hWnd))
			pWnd->MoveWindow(m_rectPage);
	}

	RedrawContainer();

	CWnd::OnSettingChange(uFlags, lpszSection);
}


//////////////////////////////////////////////////////////////////

BOOL COXTabViewContainer::SaveState(LPCTSTR lpszProfileName)
{
	CWinApp* pApp=AfxGetApp();
	if(pApp==NULL)
		return FALSE;

	BOOL bResult=TRUE;
	bResult&=pApp->WriteProfileInt(lpszProfileName,
		_T("TabButtonsAreaWidth"),m_nLastTabBtnAreaWidth);

	return bResult;
}

BOOL COXTabViewContainer::LoadState(LPCTSTR lpszProfileName)
{
	CWinApp* pApp=AfxGetApp();
	if(pApp==NULL)
		return FALSE;

	m_nLastTabBtnAreaWidth=pApp->GetProfileInt(lpszProfileName,
		_T("TabButtonsAreaWidth"),m_nLastTabBtnAreaWidth);

	CalcLayout();
	UpdateScrollSizes();
	RedrawContainer();

	return TRUE;
}


BOOL COXTabViewContainer::InsertPage(int nIndex, 
									 CRuntimeClass* pClass, 
									 CCreateContext* pContext,
									 LPCTSTR lpszTitle/*=NULL*/)
{
	ASSERT_VALID(this);
	ASSERT(nIndex>=0 && nIndex<=GetPageCount());
	ASSERT(pClass!=NULL);
	ASSERT(pClass->IsDerivedFrom(RUNTIME_CLASS(CWnd)));
	ASSERT(AfxIsValidAddress(pClass,sizeof(CRuntimeClass),FALSE));

	if(!(nIndex>=0 && nIndex<=GetPageCount()) || pClass==NULL)
		return FALSE;

	CCreateContext context;
	if(pContext==NULL)
	{
		// if no context specified, generate one from the currently active
		// view if possible
		CView* pOldView=(CView*)GetActivePage();
		if(pOldView!=NULL && pOldView->IsKindOf(RUNTIME_CLASS(CView)))
		{
			// set info about last pane
			ASSERT(context.m_pCurrentFrame==NULL);
			context.m_pLastView=pOldView;
			context.m_pCurrentDoc=pOldView->GetDocument();
			if(context.m_pCurrentDoc!=NULL)
			{
				context.m_pNewDocTemplate=context.m_pCurrentDoc->
					GetDocTemplate();
			}
		}
		pContext=&context;
	}

	CWnd* pWnd;
	TRY
	{
		pWnd=(CWnd*)pClass->CreateObject();
		if(pWnd==NULL)
			AfxThrowMemoryException();
	}
	CATCH_ALL(e)
	{
		TRACE(_T("COXTabViewContainer::InsertPage: Out of memory inserting new page\n"));
		// Note: DELETE_EXCEPTION(e) not required
		return FALSE;
	}
	END_CATCH_ALL

	ASSERT_KINDOF(CWnd,pWnd);
	ASSERT(pWnd->m_hWnd==NULL);       // not yet created

	DWORD dwStyle=AFX_WS_DEFAULT_VIEW;
#if _MFC_VER < 0x0700
	if(afxData.bWin4)
#endif
		dwStyle&=~WS_BORDER;

	DWORD dwID=GetUniqueId();

	// Create with the right size
	if(!pWnd->Create(NULL,NULL,dwStyle,m_rectPage,this,dwID,pContext))
	{
		TRACE(_T("COXTabViewContainer::InsertPage: couldn't create new page\n"));
		// pWnd will be cleaned up by PostNcDestroy
		return FALSE;
	}

	if(InsertPage(nIndex,pWnd,lpszTitle))
	{
		CWnd* pWnd=GetPage(nIndex);
		ASSERT(pWnd!=NULL);
		ASSERT(::IsWindow(pWnd->m_hWnd));
		if(pWnd->IsKindOf(RUNTIME_CLASS(CView)))
		{
			// send initial notification message
			pWnd->SendMessage(WM_INITIALUPDATE);
		}
		return TRUE;
	}

	return FALSE;
}

BOOL COXTabViewContainer::InsertPage(int nIndex, CWnd* pWnd, 
									 LPCTSTR lpszTitle/*=NULL*/)
{
	ASSERT_VALID(this);
	ASSERT(nIndex>=0 && nIndex<=GetPageCount());
	ASSERT(pWnd!=NULL);
	ASSERT(::IsWindow(pWnd->m_hWnd));

	if(!(nIndex>=0 && nIndex<=GetPageCount()) 
		|| pWnd==NULL || !::IsWindow(pWnd->m_hWnd))
	{
		return FALSE;
	}

	// set this container as parent window of the specified page
	pWnd->SetParent(this);

	PAGEINFO pi;
	pi.pWnd=pWnd;
	if(lpszTitle==NULL)
	{
		pWnd->GetWindowText(pi.sTitle);
		if(pi.sTitle.IsEmpty())
		{
			pi.sTitle.Format(_T("%d"),nIndex);
		}
	}
	else
	{
		pi.sTitle=lpszTitle;
	}

	m_arrPages.InsertAt(nIndex,pi);
	
	CalcTabBtnRects();

	SetActivePageIndex(nIndex);

	RedrawContainer();
	
	return TRUE;
}


BOOL COXTabViewContainer::DeletePage(CWnd* pWnd, 
									 BOOL bDestroy/*=TRUE*/) 
{
	ASSERT(pWnd!=NULL);
	ASSERT(IsPage(pWnd));
	int nIndex=-1;
	if(FindPage(pWnd,nIndex))
	{
		return DeletePage(nIndex,bDestroy);
	}
	return FALSE;
}


BOOL COXTabViewContainer::DeletePage(int nIndex, 
									 BOOL bDestroy/*=TRUE*/)
{
	ASSERT_VALID(this);
	ASSERT(nIndex>=0 && nIndex<GetPageCount());

	if(nIndex>=0 && nIndex<GetPageCount())
	{
		// if active page is being deleted - activate next
		int nActivePage=GetActivePageIndex();
		if(nActivePage==nIndex)
		{
			if(nActivePage==GetPageCount()-1)
				SetActivePageIndex(0);
			else
				SetActivePageIndex(GetPageCount()>1 ? nActivePage+1 : 0);
		}
		CWnd* pWnd=GetPage(nIndex);
		ASSERT(pWnd!=NULL);
		m_arrUniqueIDs.Add(pWnd->GetDlgCtrlID());
		if(bDestroy && ::IsWindow(pWnd->m_hWnd))
			VERIFY(pWnd->DestroyWindow());

		m_arrPages.RemoveAt(nIndex);

		nActivePage=GetActivePageIndex();
		if(nActivePage>=nIndex)
			m_nActivePageIndex--;

		CalcTabBtnRects();

		RedrawContainer();
	
		return TRUE;
	}

	return FALSE;
}


BOOL COXTabViewContainer::SetPageTitle(int nIndex, LPCTSTR lpszTitle) 
{
	ASSERT(nIndex>=0 && nIndex<GetPageCount());

	if(nIndex>=0 && nIndex<GetPageCount())
	{
		PAGEINFO& pi=m_arrPages[nIndex];
		if(pi.sTitle.Compare(lpszTitle)!=0)
		{
			pi.sTitle=lpszTitle;
			CalcTabBtnRects();
			CRect rect=m_arrTabBtnRects[nIndex];
			if(rect.left<m_rectTabBtnArea.right &&
				rect.right>m_rectTabBtnArea.right)
			{
				RedrawTabBtnArea();
			}
		}
		return TRUE;
	}
	
	return FALSE;
}


BOOL COXTabViewContainer::SetActivePageIndex(int nIndex)
{
	if(nIndex==m_nActivePageIndex)
	{
		return TRUE;
	}

	CWnd* pWndOld=GetActivePage();
	if(pWndOld!=NULL)
	{
		ASSERT(::IsWindow(pWndOld->m_hWnd));
		if(::IsWindow(pWndOld->m_hWnd))
		{
			UpdateScrollInfo();

			pWndOld->ShowWindow(SW_HIDE);


			PAGEINFO pi=m_arrPages[GetActivePageIndex()];
			m_nActivePageIndex=-1;
			UINT nBar=(pi.bHasScrollHorz&pi.bHasScrollVert ? SB_BOTH : 
				(pi.bHasScrollHorz ? SB_HORZ : 
				(pi.bHasScrollVert ? SB_VERT : 0)));
			if(nBar!=0)
			{
				pWndOld->ShowScrollBar(nBar,TRUE);
			}
		}
	}

	m_nActivePageIndex=nIndex;
	if(m_nActivePageIndex>=0 && m_nActivePageIndex<GetPageCount())
	{
		EnsureTabBtnVisible(m_nActivePageIndex);

		CWnd* pWnd=GetPage(m_nActivePageIndex);
		if(pWnd!=NULL)
		{
			ASSERT(::IsWindow(pWnd->m_hWnd));
			if(::IsWindow(pWnd->m_hWnd))
			{
				IniScrollInfo();

				pWnd->ShowWindow(SW_SHOW);
				CRect rect;
				pWnd->GetWindowRect(rect);
				if(rect.Width()!=m_rectPage.Width() || 
					rect.Height()!=m_rectPage.Height())
				{
					pWnd->MoveWindow(m_rectPage);
				}
				else
				{
					pWnd->SendMessage(WM_SIZE,SIZE_RESTORED,
						MAKELPARAM(rect.Width(),rect.Height()));
				}

				// set the focus to the page
				CFrameWnd* pFrameWnd=(CFrameWnd*)GetParent();
				ASSERT(pFrameWnd!=NULL);
				if(pFrameWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd)))
				{
					if(pWnd->IsKindOf(RUNTIME_CLASS(CView)))
					{
						pFrameWnd->SetActiveView((CView*)pWnd);
					}
					else
					{
						if(pWndOld!=NULL && 
							pWndOld->IsKindOf(RUNTIME_CLASS(CView)))
						{
							pFrameWnd->SetActiveView(NULL);
						}
						pWnd->SetFocus();
					}
				}
				else
				{
					pWnd->SetFocus();
				}

				RedrawTabBtnArea();
		
			}
		}
	}
	else
		return FALSE;

	return TRUE;
}

void COXTabViewContainer::SetScrollStyle(DWORD dwScrollStyle, 
										 BOOL bForceToRebuild/*=FALSE*/) 
{
	ASSERT(::IsWindow(GetSafeHwnd()));

	if(!bForceToRebuild && 
		m_dwScrollStyle==(dwScrollStyle & (WS_VSCROLL | WS_HSCROLL)))
	{
		return; 
	}

	m_dwScrollStyle=(dwScrollStyle & (WS_VSCROLL | WS_HSCROLL));

	if(::IsWindow(m_scrlBarHorz.GetSafeHwnd()))
	{
		m_scrlBarHorz.DestroyWindow();
	}
	if(::IsWindow(m_scrlBarVert.GetSafeHwnd()))
	{
		m_scrlBarVert.DestroyWindow();
	}

	VERIFY(m_scrlBarHorz.Create(SBS_HORZ|WS_CHILD|WS_VISIBLE|
		((dwScrollStyle & WS_HSCROLL) ? 0 : WS_DISABLED),
		m_rectScrollBarHorz,this,AFX_IDW_HSCROLL_FIRST));
	VERIFY(m_scrlBarVert.Create(SBS_VERT|WS_CHILD|WS_VISIBLE|
		((dwScrollStyle & WS_VSCROLL) ? 0 : WS_DISABLED),
		m_rectScrollBarVert,this,AFX_IDW_VSCROLL_FIRST));
}

void COXTabViewContainer::CalcLayout()
{
	ASSERT(::IsWindow(GetSafeHwnd()));

	EmptyRects();

	CRect rect;
	GetClientRect(rect);

	if(rect.IsRectEmpty())
		return;

	CSize szScrollBtn=CSize(::GetSystemMetrics(SM_CXHSCROLL), 
		::GetSystemMetrics(SM_CYHSCROLL));

	// scroll buttons
	//
	m_rectScrollToStartBtn=CRect(rect.left,rect.bottom-szScrollBtn.cy,
		rect.left+szScrollBtn.cx,rect.bottom);

	m_rectScrollBackwardBtn=m_rectScrollToStartBtn;
	m_rectScrollBackwardBtn.OffsetRect(m_rectScrollToStartBtn.Width(),0);

	m_rectScrollForwardBtn=m_rectScrollBackwardBtn;
	m_rectScrollForwardBtn.OffsetRect(m_rectScrollBackwardBtn.Width(),0);

	m_rectScrollToEndBtn=m_rectScrollForwardBtn;
	m_rectScrollToEndBtn.OffsetRect(m_rectScrollForwardBtn.Width(),0);
	//
	/////////////////////////////////////////////

	// page rect
	m_rectPage=rect;
	m_rectPage.right-=szScrollBtn.cx;
	m_rectPage.bottom-=szScrollBtn.cy;

	int nTabBtnAreaWidth=m_nLastTabBtnAreaWidth;
	if(m_rectPage.right<m_rectScrollToEndBtn.right+
		nTabBtnAreaWidth+ID_MINSCROLLBARWIDTH)
	{
		nTabBtnAreaWidth=m_rectPage.right-
			m_rectScrollToEndBtn.right-ID_MINSCROLLBARWIDTH;
	}

	// tab buttons area
	m_rectTabBtnArea=m_rectScrollToStartBtn;
	m_rectTabBtnArea.left=m_rectScrollToEndBtn.right;
	m_rectTabBtnArea.right=m_rectTabBtnArea.left+nTabBtnAreaWidth;
	if(m_rectTabBtnArea.right<m_rectTabBtnArea.left)
		m_rectTabBtnArea.right=m_rectTabBtnArea.left;

	// splitter rect
	m_rectSplitter=m_rectScrollToStartBtn;
	m_rectSplitter.left=m_rectTabBtnArea.right;
	m_rectSplitter.right=m_rectSplitter.left+ID_SPLITTERWIDTH;

	// horz scroll bar
	m_rectScrollBarHorz=m_rectScrollToStartBtn;
	m_rectScrollBarHorz.left=m_rectSplitter.right;
	m_rectScrollBarHorz.right=m_rectPage.right;

	// vert scroll bar
	m_rectScrollBarVert=m_rectPage;
	m_rectScrollBarVert.left=m_rectPage.right;
	m_rectScrollBarVert.right=rect.right;

	// size bar
	m_rectSizeBar=rect;
	m_rectSizeBar.top=m_rectScrollBarVert.bottom;
	m_rectSizeBar.left=m_rectScrollBarHorz.right;

}

void COXTabViewContainer::CalcTabBtnRects()
{
	m_arrTabBtnRects.RemoveAll();

	CString sTitle;
	int nLeftSide=0;
	CRect rect;

	CClientDC dc(this);
	ASSERT((HFONT)m_fontActiveTabBtnText!=NULL);
	if((HFONT)m_fontActiveTabBtnText!=NULL)
		dc.SelectObject(&m_fontActiveTabBtnText);
	
	for(int nIndex=0; nIndex<GetPageCount(); nIndex++)
	{
		rect=m_rectTabBtnArea;
		rect-=rect.TopLeft();
		rect.right=0xffff;

		sTitle=GetPageTitle(nIndex);

		dc.DrawText(sTitle,rect,DT_CALCRECT|DT_LEFT|DT_SINGLELINE|DT_VCENTER);
		rect.OffsetRect(nLeftSide,0);
		rect.right+=2*ID_TABBTNOVERLAPSIZE+2*ID_TABBTNGAPSIZE;
		
		m_arrTabBtnRects.Add(rect);

		nLeftSide=rect.right-ID_TABBTNOVERLAPSIZE;
	}
}

void COXTabViewContainer::EmptyRects()
{
	m_rectScrollForwardBtn.SetRectEmpty();
	m_rectScrollBackwardBtn.SetRectEmpty();
	m_rectScrollToStartBtn.SetRectEmpty();
	m_rectScrollToEndBtn.SetRectEmpty();

	m_rectTabBtnArea.SetRectEmpty();

	m_rectScrollBarHorz.SetRectEmpty();
	m_rectScrollBarVert.SetRectEmpty();

	m_rectSplitter.SetRectEmpty();

	m_rectSizeBar.SetRectEmpty();

	m_rectPage.SetRectEmpty();
}

void COXTabViewContainer::UpdateScrollSizes()
{
	if(::IsWindow(m_scrlBarHorz.GetSafeHwnd()))
	{
		m_scrlBarHorz.MoveWindow(m_rectScrollBarHorz);
	}
	if(::IsWindow(m_scrlBarVert.GetSafeHwnd()))
	{
		m_scrlBarVert.MoveWindow(m_rectScrollBarVert);
	}
}


void COXTabViewContainer::IniScrollInfo()
{
	CWnd* pWnd=GetActivePage();
	if(pWnd==NULL)
	{
		return;
	}

	PAGEINFO& pi=m_arrPages[GetActivePageIndex()];

	pi.bHasScrollHorz=((pWnd->GetStyle()&WS_HSCROLL)==WS_HSCROLL);
	pi.bHasScrollVert=((pWnd->GetStyle()&WS_VSCROLL)==WS_VSCROLL);

	if(pi.bHasScrollHorz)
	{
		pi.GetScrollInfo(pWnd,TRUE);
	}
	if(pi.bHasScrollVert)
	{
		pi.GetScrollInfo(pWnd,FALSE);
	}

	DWORD dwStyle=(pi.bHasScrollHorz ? WS_HSCROLL : 0) | 
		(pi.bHasScrollVert ? WS_VSCROLL : 0);
	SetScrollStyle(dwStyle,TRUE);
	pWnd->ShowScrollBar(SB_BOTH,FALSE);
	pWnd->RedrawWindow();

	if(pi.bHasScrollHorz || 
		(pi.scrlInfoHorz.nMax>0 && pi.scrlInfoHorz.nPage>0 && 
		pi.scrlInfoHorz.nPos>0 && pi.scrlInfoHorz.nPos<=pi.scrlInfoHorz.nMax))
	{
		ASSERT(::IsWindow(m_scrlBarHorz.GetSafeHwnd()));
		m_scrlBarHorz.SetScrollInfo(&pi.scrlInfoHorz);
	}
	if(pi.bHasScrollVert || 
		(pi.scrlInfoVert.nMax>0 && pi.scrlInfoVert.nPage>0 && 
		pi.scrlInfoVert.nPos>0 && pi.scrlInfoVert.nPos<=pi.scrlInfoVert.nMax))
	{
		ASSERT(::IsWindow(m_scrlBarVert.GetSafeHwnd()));
		m_scrlBarVert.SetScrollInfo(&pi.scrlInfoVert);
	}

}


static BOOL g_bUpdatingScrollInfo=FALSE;
static BOOL g_bUpdatingScrollState=FALSE;

void COXTabViewContainer::UpdateScrollInfo()
{
	if(g_bUpdatingScrollInfo || g_bUpdatingScrollState)
	{
		return;
	}

	g_bUpdatingScrollInfo=TRUE;

	CWnd* pWnd=GetActivePage();
	if(pWnd==NULL || !pWnd->IsWindowVisible())
	{
		g_bUpdatingScrollInfo=FALSE;
		return;
	}

	PAGEINFO& pi=m_arrPages[GetActivePageIndex()];

	if(pi.bHasScrollHorz) 
	{
		pWnd->ModifyStyle(WS_VISIBLE,NULL);
		pWnd->ShowScrollBar(SB_HORZ,TRUE);
		pWnd->ModifyStyle(NULL,WS_HSCROLL,SWP_DRAWFRAME);

		pi.GetScrollInfo(pWnd,TRUE);

		ASSERT(::IsWindow(m_scrlBarHorz.GetSafeHwnd()));
		m_scrlBarHorz.SetScrollInfo(&pi.scrlInfoHorz);

		pWnd->ShowScrollBar(SB_HORZ,FALSE);
		pWnd->ModifyStyle(WS_HSCROLL,NULL,SWP_DRAWFRAME);
		pWnd->ModifyStyle(NULL,WS_VISIBLE);
	}
	else
	{
		pi.scrlInfoHorz.cbSize=sizeof(SCROLLINFO);
		pWnd->GetScrollInfo(SB_HORZ,&pi.scrlInfoHorz);
	}

	if(pi.bHasScrollVert)
	{
		pWnd->ModifyStyle(WS_VISIBLE,NULL);
		pWnd->ShowScrollBar(SB_VERT,TRUE);
		pWnd->ModifyStyle(NULL,WS_VSCROLL,SWP_DRAWFRAME);

		pi.GetScrollInfo(pWnd,FALSE);

		ASSERT(::IsWindow(m_scrlBarVert.GetSafeHwnd()));
		m_scrlBarVert.SetScrollInfo(&pi.scrlInfoVert);

		pWnd->ShowScrollBar(SB_VERT,FALSE);
		pWnd->ModifyStyle(WS_VSCROLL,NULL,SWP_DRAWFRAME);
		pWnd->ModifyStyle(NULL,WS_VISIBLE);
	}
	else
	{
		pi.scrlInfoVert.cbSize=sizeof(SCROLLINFO);
		pWnd->GetScrollInfo(SB_VERT,&pi.scrlInfoVert);
	}

	g_bUpdatingScrollInfo=FALSE;
}


void COXTabViewContainer::UpdateScrollState()
{
	if(g_bUpdatingScrollState || g_bUpdatingScrollInfo)
	{
		return;
	}

	g_bUpdatingScrollState=TRUE;

	CWnd* pWnd=GetActivePage();
	if(pWnd==NULL || !pWnd->IsWindowVisible())
	{
		g_bUpdatingScrollState=FALSE;
		return;
	}

	PAGEINFO& pi=m_arrPages[GetActivePageIndex()];

	BOOL bHasScrollHorz=((pWnd->GetStyle() & WS_HSCROLL)==WS_HSCROLL);
	BOOL bHasScrollVert=((pWnd->GetStyle() & WS_VSCROLL)==WS_VSCROLL);

	if(bHasScrollHorz || bHasScrollVert)
	{
		if(bHasScrollHorz)
		{
			pi.bHasScrollHorz=bHasScrollHorz;
			pWnd->ModifyStyle(WS_HSCROLL,NULL,SWP_DRAWFRAME);
			pWnd->ShowScrollBar(SB_HORZ,FALSE);
		}
		if(bHasScrollVert)
		{
			pi.bHasScrollVert=bHasScrollVert;
			pWnd->ModifyStyle(WS_VSCROLL,NULL,SWP_DRAWFRAME);
			pWnd->ShowScrollBar(SB_VERT,FALSE);
		}
	}

	pWnd->ModifyStyle(WS_VISIBLE,NULL);
	pWnd->ShowScrollBar(SB_HORZ,TRUE);
	pWnd->ModifyStyle(NULL,WS_HSCROLL,SWP_DRAWFRAME);
	pWnd->ShowScrollBar(SB_VERT,TRUE);
	pWnd->ModifyStyle(NULL,WS_VSCROLL,SWP_DRAWFRAME);

	pi.GetScrollInfo(pWnd,TRUE);
	if(pi.bHasScrollHorz)
	{
		if(pi.scrlInfoHorz.nMax==0 || pi.scrlInfoHorz.nPage==0 || 
			pi.scrlInfoHorz.nMax<(int)pi.scrlInfoHorz.nPage)
		{
			pi.bHasScrollHorz=FALSE;
		}
	}
	else
	{
		if(pi.scrlInfoHorz.nMax>0 && pi.scrlInfoHorz.nPage>0 &&
			pi.scrlInfoHorz.nMax>=(int)pi.scrlInfoHorz.nPage)
		{
			pi.bHasScrollHorz=TRUE;
		}
	}

	pi.GetScrollInfo(pWnd,FALSE);
	if(pi.bHasScrollVert)
	{
		if(pi.scrlInfoVert.nMax==0 || pi.scrlInfoVert.nPage==0 || 
			pi.scrlInfoVert.nMax<(int)pi.scrlInfoVert.nPage)
		{
			pi.bHasScrollVert=FALSE;
		}
	}
	else
	{
		if(pi.scrlInfoVert.nMax>0 && pi.scrlInfoVert.nPage>0 &&
			pi.scrlInfoVert.nMax>=(int)pi.scrlInfoVert.nPage)
		{
			pi.bHasScrollVert=TRUE;
		}
	}

	pWnd->ShowScrollBar(SB_HORZ,FALSE);
	pWnd->ModifyStyle(WS_HSCROLL,NULL,SWP_DRAWFRAME);
	pWnd->ShowScrollBar(SB_VERT,FALSE);
	pWnd->ModifyStyle(WS_VSCROLL,NULL,SWP_DRAWFRAME);
	pWnd->ModifyStyle(NULL,WS_VISIBLE);

	DWORD dwStyle=GetScrollStyle();
	dwStyle=(pi.bHasScrollHorz ? WS_HSCROLL : 0) | 
		(pi.bHasScrollVert ? WS_VSCROLL : 0);
	SetScrollStyle(dwStyle,FALSE);

	g_bUpdatingScrollState=FALSE;
}


void COXTabViewContainer::DrawScrollButtons(CDC* pDC)
{
	ASSERT_VALID(pDC);

	// scroll to start button
	DrawButton(pDC,m_rectScrollToStartBtn,SCRLSTARTBTN);
	// scroll backward button
	DrawButton(pDC,m_rectScrollBackwardBtn,SCRLBACKWARDBTN);
	// scroll forward button
	DrawButton(pDC,m_rectScrollForwardBtn,SCRLFORWARDBTN);
	// scroll to end button
	DrawButton(pDC,m_rectScrollToEndBtn,SCRLENDBTN);
}

void COXTabViewContainer::DrawTabBtnArea(CDC* pDC)
{
	ASSERT(m_arrTabBtnRects.GetSize()==GetPageCount());

	pDC->IntersectClipRect(m_rectTabBtnArea);

	pDC->FillSolidRect(m_rectTabBtnArea,::GetSysColor(COLOR_BTNFACE));

	CPen pen(PS_SOLID,1,::GetSysColor(COLOR_WINDOWTEXT));
	CPen* pOldPen=pDC->SelectObject(&pen);
	pDC->MoveTo(m_rectTabBtnArea.left,m_rectTabBtnArea.top);
	pDC->LineTo(m_rectTabBtnArea.right,m_rectTabBtnArea.top);

	CBrush brush;
	brush.CreateSolidBrush(::GetSysColor(COLOR_BTNFACE));
	CBrush* pOldBrush=pDC->SelectObject(&brush);

	CFont* pOldFont=NULL;
	ASSERT((HFONT)m_fontTabBtnText!=NULL);
	if((HFONT)m_fontTabBtnText!=NULL)
		pOldFont=pDC->SelectObject(&m_fontTabBtnText);

	for(int nIndex=0; nIndex<GetPageCount(); nIndex++)
	{
		if(nIndex!=GetActivePageIndex())
			DrawTabButton(pDC,nIndex);
	}


	if(GetActivePageIndex()!=-1)
	{
		CBrush brushActive;
		brushActive.CreateSolidBrush(::GetSysColor(COLOR_WINDOW));
		pDC->SelectObject(&brushActive);

		ASSERT((HFONT)m_fontActiveTabBtnText!=NULL);
		if((HFONT)m_fontActiveTabBtnText!=NULL)
			pDC->SelectObject(&m_fontActiveTabBtnText);

		DrawTabButton(pDC,GetActivePageIndex());
	}

	if(pOldFont!=NULL)
		pDC->SelectObject(pOldFont);

	if(pOldBrush!=NULL)
		pDC->SelectObject(pOldBrush);

	if(pOldPen!=NULL)
		pDC->SelectObject(pOldPen);
}

void COXTabViewContainer::DrawSplitter(CDC* pDC)
{
	ASSERT_VALID(pDC);

	CRect rect=m_rectSplitter;

	pDC->Draw3dRect(rect,::GetSysColor(COLOR_BTNFACE),
		::GetSysColor(COLOR_WINDOWFRAME));
	rect.DeflateRect(1,1);
	pDC->Draw3dRect(rect,::GetSysColor(COLOR_BTNHILIGHT),
		::GetSysColor(COLOR_BTNSHADOW));
	rect.DeflateRect(1,1);

	// fill the middle
	COLORREF clr=::GetSysColor(COLOR_BTNFACE);
#ifdef _MAC
	// just use white for interior if less than 16 colors
	if(pDC->GetDeviceCaps(NUMCOLORS)<16)
		clr=RGB(0xFF,0xFF,0xFF);
#endif
	pDC->FillSolidRect(rect,clr);
}

void COXTabViewContainer::DrawSizeBar(CDC* pDC)
{
	ASSERT_VALID(pDC);

	CWnd* pParentWnd=GetParent();
	ASSERT(pParentWnd!=NULL);
	ASSERT(::IsWindow(pParentWnd->m_hWnd));
	if(pParentWnd->IsKindOf(RUNTIME_CLASS(CMDIChildWnd)) && 
		(pParentWnd->GetStyle()&WS_THICKFRAME)==WS_THICKFRAME &&
		(pParentWnd->GetStyle()&WS_MAXIMIZE)!=WS_MAXIMIZE)
		pDC->DrawFrameControl(m_rectSizeBar,DFC_SCROLL,DFCS_SCROLLSIZEGRIP);
	else
		pDC->FillSolidRect(m_rectSizeBar,::GetSysColor(COLOR_BTNFACE));
}

void COXTabViewContainer::DrawButton(CDC* pDC, CRect rect, 
									 HITTEST nButtonType) const
{
	ASSERT_VALID(pDC);

	pDC->FillSolidRect(rect,::GetSysColor(COLOR_BTNFACE));

	COLORREF clrTopLeft;
	COLORREF clrBottomRight;
	if(m_nPressedScrlBtn==nButtonType)
	{
		CPoint point;
		::GetCursorPos(&point);
		ScreenToClient(&point);
		if(rect.PtInRect(point))
		{
			clrBottomRight=::GetSysColor(COLOR_BTNFACE);
			clrTopLeft=::GetSysColor(COLOR_BTNFACE);
			rect.OffsetRect(1,1);
		}
		else
		{
			clrTopLeft=::GetSysColor(COLOR_BTNHILIGHT);
			clrBottomRight=::GetSysColor(COLOR_BTNSHADOW);
		}
	}
	else
	{
		clrTopLeft=::GetSysColor(COLOR_BTNHILIGHT);
		clrBottomRight=::GetSysColor(COLOR_BTNSHADOW);
	}

	pDC->Draw3dRect(rect,clrTopLeft,clrBottomRight);


	// draw arrows
	//
	rect.DeflateRect(2,2);
	CRect rectCopy=rect;

	POINT arrPoints[3];
	CRgn rgn;
	CBrush brush;
	brush.CreateSolidBrush(::GetSysColor(COLOR_WINDOWTEXT));

	switch(nButtonType)
	{
	// start
	case SCRLSTARTBTN:
		rectCopy.DeflateRect(rectCopy.Width()/4,rectCopy.Height()/6);
		rectCopy.left+=rectCopy.Width()/4-2;
		if(rectCopy.Height()%2==0)
			rectCopy.bottom+=1;
		rectCopy.right=rectCopy.left+2;
		pDC->FillSolidRect(rectCopy,::GetSysColor(COLOR_WINDOWTEXT));
	// backward
	case SCRLBACKWARDBTN:
		rectCopy=rect;
		rectCopy.DeflateRect(rectCopy.Width()/4,rectCopy.Height()/6);
		if(nButtonType==SCRLSTARTBTN)
			rectCopy.left+=rectCopy.Width()/4;
		else
			rectCopy.right-=rectCopy.Width()/4;
		if(rectCopy.Height()%2==0)
			rectCopy.bottom++;
		arrPoints[0].x=rectCopy.right;
		arrPoints[0].y=rectCopy.top-1;
		arrPoints[1].x=rectCopy.right;
		arrPoints[1].y=rectCopy.bottom;
		arrPoints[2].x=rectCopy.left;
		arrPoints[2].y=rectCopy.top+rectCopy.Height()/2;
		rgn.CreatePolygonRgn(arrPoints,3,WINDING);
		pDC->FillRgn(&rgn,&brush);
		break;
	// end
	case SCRLENDBTN:
		rectCopy.DeflateRect(rectCopy.Width()/4,rectCopy.Height()/6);
		rectCopy.right-=rectCopy.Width()/4-2;
		if(rectCopy.Height()%2==0)
			rectCopy.bottom+=1;
		rectCopy.left=rectCopy.right-2;
		pDC->FillSolidRect(rectCopy,::GetSysColor(COLOR_WINDOWTEXT));
	// forward
	case SCRLFORWARDBTN:
		rectCopy=rect;
		rectCopy.DeflateRect(rectCopy.Width()/4,rectCopy.Height()/6);
		if(nButtonType==SCRLENDBTN)
			rectCopy.right-=rectCopy.Width()/4;
		else
			rectCopy.left+=rectCopy.Width()/4;
		if(rectCopy.Height()%2==0)
			rectCopy.bottom++;
		arrPoints[0].x=rectCopy.right;
		arrPoints[0].y=rectCopy.top+rectCopy.Height()/2;
		arrPoints[1].x=rectCopy.left;
		arrPoints[1].y=rectCopy.top-1;
		arrPoints[2].x=rectCopy.left;
		arrPoints[2].y=rectCopy.bottom;
		rgn.CreatePolygonRgn(arrPoints,3,WINDING);
		pDC->FillRgn(&rgn,&brush);
		break;
	default:
		ASSERT(FALSE);
	}
}

void COXTabViewContainer::DrawTabButton(CDC* pDC, int nIndex) const
{
	ASSERT(nIndex>=0 && nIndex<GetPageCount());

	CRect rect=m_arrTabBtnRects[nIndex];
	rect+=m_rectTabBtnArea.TopLeft();
	rect.OffsetRect(m_nTabBtnAreaOrigin,0);
	if(rect.right>m_rectTabBtnArea.left && rect.left<m_rectTabBtnArea.right)
	{
		rect.bottom=m_rectTabBtnArea.bottom;
		
		POINT arrPoints[4];
		arrPoints[0].x=rect.left;
		arrPoints[0].y=rect.top;
		arrPoints[1].x=rect.left+ID_TABBTNOVERLAPSIZE;
		arrPoints[1].y=rect.bottom;
		arrPoints[2].x=rect.right-ID_TABBTNOVERLAPSIZE;
		arrPoints[2].y=rect.bottom;
		arrPoints[3].x=rect.right;
		arrPoints[3].y=rect.top;

		CPen penTop(PS_SOLID,1,::GetSysColor(COLOR_BTNHILIGHT));
		CPen penBottom(PS_SOLID,1,::GetSysColor(COLOR_BTNSHADOW));
		CPen* pOldPen=NULL;
		if(nIndex==GetActivePageIndex())
		{
			pDC->Polygon(arrPoints,4);

			pOldPen=pDC->SelectObject(&penTop);
			arrPoints[0].x++;
			pDC->MoveTo(arrPoints[0]);
			pDC->LineTo(arrPoints[3]);

			pDC->SelectObject(&penBottom);
			arrPoints[1].y--;
			arrPoints[2].y--;
			pDC->MoveTo(arrPoints[1]);
			pDC->LineTo(arrPoints[2]);
		}
		else
		{
			pDC->Polygon(arrPoints,4);

			pOldPen=pDC->SelectObject(&penBottom);
			arrPoints[1].y--;
			arrPoints[2].y--;
			pDC->MoveTo(arrPoints[1]);
			pDC->LineTo(arrPoints[2]);
			arrPoints[2].x--;
			arrPoints[3].x--;
			pDC->MoveTo(arrPoints[2]);
			pDC->LineTo(arrPoints[3]);

			pDC->SelectObject(&penTop);
			arrPoints[0].x+=2;
			arrPoints[0].y++;
			arrPoints[1].x++;
			pDC->MoveTo(arrPoints[0]);
			pDC->LineTo(arrPoints[1]);
		}
		if(pOldPen!=NULL)
			pDC->SelectObject(pOldPen);

		CString sTitle=GetPageTitle(nIndex);
		if(!sTitle.IsEmpty())
		{
			COLORREF oldColor=pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
			int nOldBkMode=pDC->SetBkMode(TRANSPARENT);
			pDC->DrawText(sTitle,rect,DT_CENTER|DT_SINGLELINE|DT_VCENTER);
			pDC->SetBkMode(nOldBkMode);
			pDC->SetTextColor(oldColor);
		}
	}
}

int COXTabViewContainer::HitTest(const CPoint& point) const
{
	if(m_rectSplitter.PtInRect(point))
		return SPLITTER;
	else if(m_rectScrollBarHorz.PtInRect(point))
		return SCROLLBARHORZ;
	else if(m_rectScrollBarVert.PtInRect(point))
		return SCROLLBARVERT;
	else if(m_rectScrollToStartBtn.PtInRect(point))
		return SCRLSTARTBTN;
	else if(m_rectScrollBackwardBtn.PtInRect(point))
		return SCRLBACKWARDBTN;
	else if(m_rectScrollForwardBtn.PtInRect(point))
		return SCRLFORWARDBTN;
	else if(m_rectScrollToEndBtn.PtInRect(point))
		return SCRLENDBTN;
	else if(m_rectTabBtnArea.PtInRect(point))
	{
		// test here for tab buttons
		for(int nIndex=0; nIndex<GetPageCount(); nIndex++)
		{
			CRect rect=m_arrTabBtnRects[nIndex];
			rect+=m_rectTabBtnArea.TopLeft();
			rect.OffsetRect(m_nTabBtnAreaOrigin,0);
			rect.bottom=m_rectTabBtnArea.bottom;
			if(rect.PtInRect(point))
				return nIndex;
		}

		return TABBTNAREA;
	}
	else if(m_rectSizeBar.PtInRect(point))
		return SIZEBAR;
	else if(m_rectPage.PtInRect(point))
		return PAGE;

	return NONE;
}

void COXTabViewContainer::RedrawScrollButton(HITTEST hitTest)
{
	switch(hitTest)
	{
	case SCRLSTARTBTN:
		RedrawWindow(m_rectScrollToStartBtn);
		break;
	case SCRLBACKWARDBTN:
		RedrawWindow(m_rectScrollBackwardBtn);
		break;
	case SCRLFORWARDBTN:
		RedrawWindow(m_rectScrollForwardBtn);
		break;
	case SCRLENDBTN:
		RedrawWindow(m_rectScrollToEndBtn);
		break;
	}
}

void COXTabViewContainer::ScrollPage(HITTEST nScrlBtn)
{
	BOOL m_bHasScrolled=FALSE;

	switch(nScrlBtn)
	{
	case SCRLSTARTBTN:
		if(CanScrollToStart())
		{
			m_nTabBtnAreaOrigin=0;
			m_bHasScrolled=TRUE;
		}
		break;
	case SCRLBACKWARDBTN:
		if(CanScrollBackward())
		{
			m_nTabBtnAreaOrigin+=ID_SCROLLTABBTNAREASTEP;
			m_nTabBtnAreaOrigin=
				m_nTabBtnAreaOrigin>0 ? 0 : m_nTabBtnAreaOrigin;
			m_bHasScrolled=TRUE;
		}
		break;
	case SCRLFORWARDBTN:
		if(CanScrollForward())
		{
			m_nTabBtnAreaOrigin-=ID_SCROLLTABBTNAREASTEP;
			CRect rect=m_arrTabBtnRects[GetPageCount()-1];
			rect+=m_rectTabBtnArea.TopLeft();
			if(rect.right+m_nTabBtnAreaOrigin<m_rectTabBtnArea.right)
				m_nTabBtnAreaOrigin=m_rectTabBtnArea.right-rect.right;
			m_bHasScrolled=TRUE;
		}
		break;
	case SCRLENDBTN:
		if(CanScrollToEnd())
		{
			CRect rect=m_arrTabBtnRects[GetPageCount()-1];
			rect+=m_rectTabBtnArea.TopLeft();
			m_nTabBtnAreaOrigin=m_rectTabBtnArea.right-rect.right;
			m_bHasScrolled=TRUE;
		}
		break;
	default:
		ASSERT(FALSE);
	}

	if(m_bHasScrolled)
		RedrawTabBtnArea();
}


void COXTabViewContainer::StartTracking(const CPoint& point)
{
	ASSERT(m_nPressedScrlBtn==NONE);
	m_nPressedScrlBtn=NONE;

	int hitTest=HitTest(point);
	if(hitTest==SCRLSTARTBTN || hitTest==SCRLBACKWARDBTN || 
		hitTest==SCRLFORWARDBTN || hitTest==SCRLENDBTN)
	{
		SetCapture();
		m_nPressedScrlBtn=(HITTEST)hitTest;
		m_bIsScrlBtnPressed=TRUE;
		RedrawScrollButton(m_nPressedScrlBtn);
		ScrollPage(m_nPressedScrlBtn);
		m_nScrollPageTimer=SetTimer(IDT_SCROLLPAGE_TIMER,
			ID_SCROLLPAGE_DELAY,NULL);
	}
	else if(hitTest==SPLITTER)
	{
		SetCapture();
		m_bIsSplitterPressed=TRUE;
	}
}

	
void COXTabViewContainer::StopTracking(const CPoint& point)
{
	if(::GetCapture()==GetSafeHwnd())
	{
		::ReleaseCapture();
	}

	if(m_nPressedScrlBtn!=NONE)
	{
		int hitTest=HitTest(point);
		if((int)m_nPressedScrlBtn==hitTest && m_nScrollPageTimer!=-1)
		{
			KillTimer(m_nScrollPageTimer);
			m_nScrollPageTimer=-1;
		}

		HITTEST nOldPressedScrlBtn=m_nPressedScrlBtn;
		m_nPressedScrlBtn=NONE;
		m_bIsScrlBtnPressed=FALSE;
		RedrawScrollButton(nOldPressedScrlBtn);
	}
	else if(m_bIsSplitterPressed)
	{
		m_bIsSplitterPressed=FALSE;
	}
}


DWORD COXTabViewContainer::GetUniqueId() 
{ 
	int nCount=PtrToInt(m_arrUniqueIDs.GetSize());
	ASSERT(nCount>0);
	DWORD dwUniqueID=m_arrUniqueIDs[nCount-1]; 
	if(nCount==1)
	{
		m_arrUniqueIDs.SetAt(nCount-1,dwUniqueID+1);
	}
	return dwUniqueID; 
}


void COXTabViewContainer::EnsureTabBtnVisible(int nIndex, 
											  BOOL bPartialOk/*=FALSE*/)
{
	ASSERT(nIndex>=0 && nIndex<GetPageCount());

	if(m_rectTabBtnArea.Width()>0 && nIndex>=0 && nIndex<GetPageCount())
	{
		CRect rect=m_arrTabBtnRects[nIndex];
		rect+=m_rectTabBtnArea.TopLeft();
		rect.OffsetRect(m_nTabBtnAreaOrigin,0);
		if(rect.left>m_rectTabBtnArea.right || 
			rect.right<m_rectTabBtnArea.left ||
			(rect.left<m_rectTabBtnArea.left && 
			rect.right<m_rectTabBtnArea.right && !bPartialOk) ||
			(rect.left>m_rectTabBtnArea.left && 
			rect.right>m_rectTabBtnArea.right && !bPartialOk))
		{
			if(rect.left<m_rectTabBtnArea.left)
			{
				m_nTabBtnAreaOrigin+=m_rectTabBtnArea.left-rect.left;
				if(nIndex>0)
					m_nTabBtnAreaOrigin+=m_rectTabBtnArea.Width()/2<20 ?
						m_rectTabBtnArea.Width()/2 : 20;
			}
			else
			{
				m_nTabBtnAreaOrigin+=m_rectTabBtnArea.right-rect.right;
				if(nIndex<GetPageCount()-1)
					m_nTabBtnAreaOrigin-=m_rectTabBtnArea.Width()/2<20 ?
						m_rectTabBtnArea.Width()/2 : 20;
			}
			RedrawTabBtnArea();
		}
	}
}

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

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

License

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


Written By
Web Developer
Canada Canada
In January 2005, David Cunningham and Chris Maunder created TheUltimateToolbox.com, a new group dedicated to the continued development, support and growth of Dundas Software’s award winning line of MFC, C++ and ActiveX control products.

Ultimate Grid for MFC, Ultimate Toolbox for MFC, and Ultimate TCP/IP have been stalwarts of C++/MFC development for a decade. Thousands of developers have used these products to speed their time to market, improve the quality of their finished products, and enhance the reliability and flexibility of their software.
This is a Organisation

476 members

Comments and Discussions