// ==========================================================================
// 							   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);
}


void COXTabViewContainer::OnTimer(UINT 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();
		}
	}
}
