// XTPTabManager.cpp: implementation of the CXTPTabManager class.
//
// This file is a part of the XTREME TOOLKIT PRO MFC class library.
// (c)1998-2012 Codejock Software, All Rights Reserved.
//
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
// CONSENT OF CODEJOCK SOFTWARE.
//
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
// IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
// SINGLE COMPUTER.
//
// CONTACT INFORMATION:
// support@codejock.com
// http://www.codejock.com
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Resource.h"

#include "Common/XTPDrawHelpers.h"
#include "Common/XTPToolTipContext.h"
#include "Common/XTPResourceManager.h"
#include "Common/XTPImageManager.h"
#include "Common/XTPMarkupRender.h"
#include "Common/XTPColorManager.h"
#include "Common/XTPWinThemeWrapper.h"

#include "XTPTabManager.h"
#include "XTPTabPaintManager.h"

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

IMPLEMENT_DYNAMIC(CXTPTabManagerItem, CCmdTarget)

//////////////////////////////////////////////////////////////////////
// CXTPTabManagerItem

CXTPTabManagerItem::CXTPTabManagerItem()
{
	EnableAutomation();


	m_bVisible = TRUE;
	m_bEnabled = TRUE;


	m_hIcon = 0;
	m_hWnd = 0;
	m_clrItem = 0;
	m_nIndex = -1;
	m_nImage = -1;
	m_dwData = 0;
	m_rcItem.SetRectEmpty();
	m_nItemRow = 0;
	m_nButtonLength = m_nContentLength = 0;
	m_bClosable = TRUE;
	m_pTabManager = NULL;
	m_bFound = FALSE;

	m_pMarkupUIElement = 0;
}

void CXTPTabManagerItem::OnRemoved()
{
	m_arrNavigateButtons.RemoveAll();
}


CXTPTabManagerItem::~CXTPTabManagerItem()
{
	m_arrNavigateButtons.RemoveAll();

	XTPMarkupReleaseElement(m_pMarkupUIElement);
}



void CXTPTabManagerItem::Reposition()
{
	if (m_pTabManager) m_pTabManager->Reposition();
}

void CXTPTabManagerItem::SetCaption(LPCTSTR lpszCaption)
{
	CString strCaption(lpszCaption);

	if (m_strCaption != strCaption)
	{
		m_strCaption = strCaption;

		XTPMarkupReleaseElement(m_pMarkupUIElement);
		if (m_pTabManager && m_pTabManager->GetMarkupContext())
		{
			m_pMarkupUIElement = XTPMarkupParseText(m_pTabManager->GetMarkupContext(), lpszCaption);
		}

		Reposition();
	}
}

void CXTPTabManagerItem::SetColor(COLORREF clr)
{
	if (m_clrItem != clr)
	{
		m_clrItem = clr;
		m_pTabManager->RedrawControl(GetRect(), FALSE);
	}
}

CString CXTPTabManagerItem::GetCaption() const
{
	return m_pTabManager->GetItemCaption(this);
}

CString CXTPTabManagerItem::GetTooltip() const
{
	return m_pTabManager->GetItemTooltip(this);
}

COLORREF CXTPTabManagerItem::GetColor()
{
	return m_pTabManager->GetItemColor(this);
}

void CXTPTabManagerItem::SetHandle(HWND hWnd)
{
	m_hWnd = hWnd;
}

void CXTPTabManagerItem::Remove()
{
	if (m_pTabManager) m_pTabManager->DeleteItem(m_nIndex);
}

void CXTPTabManagerItem::Select()
{
	if (m_pTabManager) m_pTabManager->OnItemClick(this);
}

HWND CXTPTabManagerItem::GetHandle() const
{
	return m_hWnd;
}

BOOL CXTPTabManagerItem::IsVisible() const
{
	return m_bVisible;
}

void CXTPTabManagerItem::SetVisible(BOOL bVisible)
{
	if (m_bVisible != bVisible)
	{
		m_bVisible = bVisible;
		Reposition();
	}
}

BOOL CXTPTabManagerItem::IsEnabled() const
{
	return m_bEnabled;
}

void CXTPTabManagerItem::SetEnabled(BOOL bEnabled)
{
	if (m_bEnabled != bEnabled)
	{
		m_bEnabled = bEnabled;
		Reposition();
	}
}

void CXTPTabManagerItem::Move(int nIndex)
{
	if (GetIndex() != nIndex)
	{
		m_pTabManager->MoveItem(this, nIndex);
	}
}

BOOL CXTPTabManagerItem::DrawRotatedImage(CDC* pDC, CRect rcItem, CXTPImageManagerIcon* pImage)
{
	CXTPImageManagerIconHandle& imageHandle = !IsEnabled() ? pImage->GetDisabledIcon(): IsHighlighted() ? pImage->GetHotIcon() :
		IsSelected() ? pImage->GetCheckedIcon() : pImage->GetIcon();

	const int cx = rcItem.Width();
	const int cy = rcItem.Height();
	ASSERT(cx == cy);
	if (cx != cy)
		return FALSE;

	UINT* pSrcBits = NULL, *pDestBits = NULL;
	HBITMAP hbmSrc = CXTPImageManager::Create32BPPDIBSection(NULL, cx, cy, (LPBYTE*)&pSrcBits);
	if (!pSrcBits)
		return FALSE;

	HBITMAP hbmDest = CXTPImageManager::Create32BPPDIBSection(NULL, cx, cy, (LPBYTE*)&pDestBits);
	if (!pDestBits)
		return FALSE;

	CDC dc;
	dc.CreateCompatibleDC(NULL);
	HGDIOBJ hbmpOld = ::SelectObject(dc, hbmSrc);

	CRect rcDraw(0, 0, cx, cy);

	dc.BitBlt(0, 0, cx, cy, pDC, rcItem.left, rcItem.top, SRCCOPY);
	::SelectObject(dc, hbmpOld);

	UINT* pSrcInv = pDestBits;
	UINT* pDestInv = pSrcBits;

	UINT* pDest = &pDestBits[cx];
	int i;

	for (i = 0; i < cy; i++)
	{
		pDest -= 1;
		pDestBits = pDest;
		for (int j = 0; j < cx; j++)
		{
			*pDestBits = *pSrcBits;
			pSrcBits += 1;
			pDestBits += cy;
		}
	}


	hbmpOld = ::SelectObject(dc, hbmDest);
	pImage->Draw(&dc, rcDraw.TopLeft(), imageHandle, rcDraw.Size());
	::SelectObject(dc, hbmpOld);

	pDest = &pDestInv[cx * (cy - 1)];

	for (i = 0; i < cy; i++)
	{
		pDestInv = pDest;
		for (int j = 0; j < cx; j++)
		{
			*pDestInv = *pSrcInv;
			pSrcInv += 1;
			pDestInv -= cy;
		}
		pDest += 1;
	}

	pDC->DrawState(rcItem.TopLeft(), rcItem.Size(), hbmSrc, DST_BITMAP);

	DeleteObject(hbmSrc);
	DeleteObject(hbmDest);

	return TRUE;
}

void CXTPTabManagerItem::DrawImage(CDC* pDC, CRect rcIcon, CXTPImageManagerIcon* pImage)
{
	if (rcIcon.Width() == rcIcon.Height() && GetTabManager()->GetPaintManager()->m_bRotateImageOnVerticalDraw
		&& (GetTabManager()->GetPosition() == xtpTabPositionLeft || GetTabManager()->GetPosition() == xtpTabPositionRight)
		&& DrawRotatedImage(pDC, rcIcon, pImage))
	{

	}
	else
	{
		CXTPImageManagerIconHandle& imageHandle = !IsEnabled() ? pImage->GetDisabledIcon(): IsHighlighted() ? pImage->GetHotIcon() :
			IsSelected() ? pImage->GetCheckedIcon() : pImage->GetIcon();

		pImage->Draw(pDC, rcIcon.TopLeft(), imageHandle, rcIcon.Size());
	}
}

void CXTPTabManagerItem::SetRect(CRect rcItem)
{
	m_rcItem = rcItem;

	for (int i = (int)m_arrNavigateButtons.GetSize() - 1; i >= 0; i--)
	{
		CXTPTabManagerNavigateButton* pButton = m_arrNavigateButtons.GetAt(i);
		pButton->SetRect(CRect(0, 0, 0, 0));
	}
}

//////////////////////////////////////////////////////////////////////////
// CXTPTabManagerNavigateButtons

CXTPTabManagerNavigateButtons::CXTPTabManagerNavigateButtons()
{

}

CXTPTabManagerNavigateButtons::~CXTPTabManagerNavigateButtons()
{
	RemoveAll();
}

void CXTPTabManagerNavigateButtons::RemoveAll()
{
	for (int i = 0; i < m_arrButtons.GetSize(); i++)
	{
		delete m_arrButtons[i];
	}
	m_arrButtons.RemoveAll();
}

void CXTPTabManagerNavigateButtons::RemoveAt(int nIndex)
{
	delete m_arrButtons[nIndex];
	m_arrButtons.RemoveAt(nIndex);
}

//////////////////////////////////////////////////////////////////////////
// CXTPTabManagerNavigateButton

CXTPTabManagerNavigateButton::CXTPTabManagerNavigateButton(CXTPTabManager* pManager, UINT nID, XTPTabNavigateButtonFlags dwFlags)
{
	m_pManager = pManager;
	m_nID = nID;
	m_dwFlags = dwFlags;

	m_rcButton.SetRectEmpty();

	m_bEnabled = TRUE;
	m_bPressed = FALSE;
	m_pItem = NULL;
}

CXTPTabManagerNavigateButton::~CXTPTabManagerNavigateButton()
{
	if (m_pManager->m_pHighlightedNavigateButton == this)
		m_pManager->m_pHighlightedNavigateButton = NULL;
}

CSize CXTPTabManagerNavigateButton::GetSize() const
{
	return m_pManager->GetPaintManager()->m_szNavigateButton;
}

void CXTPTabManagerNavigateButton::AdjustWidth(int& nWidth)
{
	if (m_pManager->IsNavigateButtonVisible(this))
	{
		if ((m_pManager->GetPosition() == xtpTabPositionTop) || (m_pManager->GetPosition() == xtpTabPositionBottom))
		{
			nWidth -= GetSize().cx;
		}
		else
		{
			nWidth -= GetSize().cy;
		}
	}
}

void CXTPTabManagerNavigateButton::SetRect(CRect rcButton)
{
	m_rcButton = rcButton;
}

void CXTPTabManagerNavigateButton::Reposition(CRect& rcNavigateButtons)
{
	if (m_pManager->IsNavigateButtonVisible(this))
	{
		CSize szButton = GetSize();

		if (m_pManager->IsHorizontalPosition())
		{
			m_rcButton.SetRect(rcNavigateButtons.right - szButton.cx, rcNavigateButtons.CenterPoint().y + szButton.cy / 2 - szButton.cy, rcNavigateButtons.right, rcNavigateButtons.CenterPoint().y + szButton.cy / 2);
			rcNavigateButtons.right -= szButton.cx;
		}
		else
		{
			m_rcButton.SetRect(rcNavigateButtons.CenterPoint().x - szButton.cx / 2, rcNavigateButtons.bottom - szButton.cy, rcNavigateButtons.CenterPoint().x - szButton.cx / 2 + szButton.cx, rcNavigateButtons.bottom);
			rcNavigateButtons.bottom -= szButton.cy;
		}
	}
	else
		m_rcButton.SetRectEmpty();

}

void CXTPTabManagerNavigateButton::Draw(CDC* pDC)
{
	if (!m_rcButton.IsRectEmpty())
	{
		CRect rc(m_rcButton);
		m_pManager->GetPaintManager()->DrawNavigateButton(pDC, this, rc);
	}
}

void CXTPTabManagerNavigateButton::PerformClick(HWND hWnd, CPoint pt)
{
	if ((::GetCapture() != NULL) || !m_bEnabled)
		return;

	::SetCapture(hWnd);

	BOOL  bClick = FALSE;

	DWORD dwStart = GetTickCount();
	for (;;)
	{
		if (m_bEnabled && GetTickCount() - dwStart > 20)
		{
			dwStart = GetTickCount();
			OnExecute(TRUE);
		}

		BOOL bPressed = m_rcButton.PtInRect(pt);

		if (bPressed != m_bPressed)
		{
			m_bPressed = bPressed;
			m_pManager->RedrawControl(m_rcButton, TRUE);
		}
		MSG msg;

		if (!::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE))
			continue;

		VERIFY(::GetMessage(&msg, NULL, 0, 0));

		if (::GetCapture() != hWnd)
		{
			DispatchMessage (&msg);
			goto ExitLoop;
		}
		switch (msg.message)
		{
			case WM_MOUSEMOVE:
				pt = CPoint((short signed)LOWORD(msg.lParam), (short signed)HIWORD(msg.lParam));
				break;

			case WM_LBUTTONUP:
				bClick = m_bPressed;
				goto ExitLoop;

			case WM_KEYDOWN:
				if (msg.wParam != VK_ESCAPE)
					break;

			case WM_CANCELMODE:
			case WM_RBUTTONDOWN:
				goto ExitLoop;

			default:
				DispatchMessage (&msg);
				break;
		}
	}

ExitLoop:
	ReleaseCapture();
	m_bPressed = FALSE;
	m_pManager->PerformMouseMove(hWnd, pt);
	m_pManager->RedrawControl(NULL, FALSE);

	if (bClick)
	{
		OnExecute(FALSE);
	}
}

void CXTPTabManagerNavigateButton::OnExecute(BOOL bTick)
{
	if (!bTick)
	{
		m_pManager->OnNavigateButtonClick(this);
	}
}

//////////////////////////////////////////////////////////////////////////
// CXTPTabManager::CRowIndexer


CXTPTabManager::CRowIndexer::CRowIndexer(CXTPTabManager* pManager)
{
	m_nRowCount = 0;
	m_pRowItems = 0;
	m_pManager = pManager;

	CreateIndexer(1);
}

CXTPTabManager::CRowIndexer::~CRowIndexer()
{
	SAFE_DELETE_AR(m_pRowItems);
}

CXTPTabManager::ROW_ITEMS* CXTPTabManager::CRowIndexer::CreateIndexer(int nRowCount)
{
	if (m_nRowCount != nRowCount)
	{
		SAFE_DELETE_AR(m_pRowItems);
		m_pRowItems = new ROW_ITEMS[nRowCount];
		m_nRowCount = nRowCount;
	}

	if (nRowCount > 0)
	{
		m_pRowItems[0].nFirstItem = 0;
		m_pRowItems[0].nLastItem = m_pManager->GetItemCount() - 1;
	}
	return m_pRowItems;
}

//////////////////////////////////////////////////////////////////////////
// CXTPTabManager::CNavigateButtonArrow

class CXTPTabManager::CNavigateButtonArrow : public CXTPTabManagerNavigateButton
{
protected:
	CNavigateButtonArrow(CXTPTabManager* pManager, XTPTabNavigateButton nID, XTPTabNavigateButtonFlags dwFlags)
		: CXTPTabManagerNavigateButton(pManager, nID, dwFlags)
	{
	}
	virtual void Reposition(CRect& rcNavigateButtons);
	virtual void AdjustWidth(int& nWidth);

};

class CXTPTabManager::CNavigateButtonArrowLeft : public CNavigateButtonArrow
{
public:
	CNavigateButtonArrowLeft(CXTPTabManager* pManager, XTPTabNavigateButtonFlags dwFlags)
		: CNavigateButtonArrow(pManager, xtpTabNavigateButtonLeft, dwFlags)
	{
	}
protected:
	virtual void DrawEntry(CDC* pDC, CRect rc);
	virtual void Reposition(CRect& rcNavigateButtons);
	virtual void OnExecute(BOOL bTick);
};

class CXTPTabManager::CNavigateButtonArrowRight : public CNavigateButtonArrow
{
public:
	CNavigateButtonArrowRight(CXTPTabManager* pManager, XTPTabNavigateButtonFlags dwFlags)
		: CNavigateButtonArrow(pManager, xtpTabNavigateButtonRight, dwFlags)
	{
	}
protected:
	virtual void DrawEntry(CDC* pDC, CRect rc);
	virtual void Reposition(CRect& rcNavigateButtons);
	virtual void OnExecute(BOOL bTick);
};

class CXTPTabManager::CNavigateButtonClose : public CXTPTabManagerNavigateButton
{
public:
	CNavigateButtonClose(CXTPTabManager* pManager, XTPTabNavigateButtonFlags dwFlags)
		: CXTPTabManagerNavigateButton(pManager, xtpTabNavigateButtonClose, dwFlags)
	{
		CXTPResourceManager::AssertValid(XTPResourceManager()->LoadString(&m_strToolTip, XTP_IDS_TABNAVIGATEBUTTON_CLOSE));
	}
protected:
	virtual void DrawEntry(CDC* pDC, CRect rc);
};

class CXTPTabManager::CNavigateButtonTabClose : public CXTPTabManager::CNavigateButtonClose
{
public:
	CNavigateButtonTabClose(CXTPTabManagerItem* pItem, XTPTabNavigateButtonFlags dwFlags)
		: CXTPTabManager::CNavigateButtonClose(pItem->GetTabManager(), dwFlags)
	{
		m_pItem = pItem;
	}
};


void CXTPTabManager::CNavigateButtonClose::DrawEntry(CDC* pDC, CRect rc)
{
	CPoint pt = rc.CenterPoint();

	pDC->MoveTo(pt.x - 4, pt.y - 3);
	pDC->LineTo(pt.x + 3, pt.y + 4);
	pDC->MoveTo(pt.x - 3, pt.y - 3);
	pDC->LineTo(pt.x + 4, pt.y + 4);

	pDC->MoveTo(pt.x - 4, pt.y + 3);
	pDC->LineTo(pt.x + 3, pt.y - 4);
	pDC->MoveTo(pt.x - 3, pt.y + 3);
	pDC->LineTo(pt.x + 4, pt.y - 4);
}

void CXTPTabManager::CNavigateButtonArrowRight::DrawEntry(CDC* pDC, CRect rc)
{
	CPoint pt = rc.CenterPoint();

	if (m_pManager->IsHorizontalPosition())
		CXTPDrawHelpers::Triangle(pDC, CPoint(pt.x - 2, pt.y - 5), CPoint(pt.x + 2, pt.y - 1), CPoint(pt.x - 2, pt.y + 3));
	else
		CXTPDrawHelpers::Triangle(pDC, CPoint(pt.x - 5, pt.y - 2), CPoint(pt.x - 1, pt.y + 2), CPoint(pt.x + 3, pt.y - 2));
}

void CXTPTabManager::CNavigateButtonArrow::AdjustWidth(int& nWidth)
{
	if (m_dwFlags == xtpTabNavigateButtonAlways)
	{
		if ((m_pManager->GetPosition() == xtpTabPositionTop) || (m_pManager->GetPosition() == xtpTabPositionBottom))
		{
			nWidth -= GetSize().cx;
		}
		else
		{
			nWidth -= GetSize().cy;
		}
	}
}
void CXTPTabManager::CNavigateButtonArrow::Reposition(CRect& rcNavigateButtons)
{
	if (m_pManager->GetLayout() == xtpTabLayoutMultiRow)
	{
		m_rcButton.SetRectEmpty();
		return;
	}

	if (m_dwFlags == xtpTabNavigateButtonAutomatic)
	{
		if (m_pManager->GetLayout() == xtpTabLayoutSizeToFit)
		{
			m_rcButton.SetRectEmpty();
			return;
		}

		CRect rc = m_pManager->GetAppearanceSet()->GetHeaderMargin();

		if (!(m_pManager->GetItemsLength() + m_pManager->GetHeaderOffset() - rc.left - 1 > m_pManager->GetRectLength(rcNavigateButtons) - (rc.left + rc.right)
			|| m_pManager->GetHeaderOffset() < 0))
		{
			m_rcButton.SetRectEmpty();
			return;
		}
	}

	CXTPTabManagerNavigateButton::Reposition(rcNavigateButtons);

}
void CXTPTabManager::CNavigateButtonArrowRight::Reposition(CRect& rcNavigateButtons)
{
	XTPResourceManager()->LoadString(&m_strToolTip, m_pManager->IsHorizontalPosition() ? XTP_IDS_TABNAVIGATEBUTTON_RIGHT : XTP_IDS_TABNAVIGATEBUTTON_DOWN);

	CRect rcHeaderMargin = m_pManager->GetPaintManager()->GetAppearanceSet()->GetHeaderMargin();

	m_bEnabled = m_pManager->GetItemsLength() + m_pManager->GetHeaderOffset() >
		m_pManager->GetRectLength(rcNavigateButtons) - (rcHeaderMargin.left + rcHeaderMargin.right) - 28;

	CNavigateButtonArrow::Reposition(rcNavigateButtons);
}

void CXTPTabManager::CNavigateButtonArrowRight::OnExecute(BOOL bTick)
{
	if (bTick)
	{
		m_pManager->OnScrollHeader(TRUE);
	}
}

void CXTPTabManager::OnScrollHeader(BOOL bRight)
{
	if (bRight)
	{
		SetHeaderOffset(GetHeaderOffset() - m_nScrollDelta);
	}
	else
	{
		SetHeaderOffset(GetHeaderOffset() + m_nScrollDelta);
	}
}

void CXTPTabManager::CNavigateButtonArrowLeft::DrawEntry(CDC* pDC, CRect rc)
{
	CPoint pt = rc.CenterPoint();

	if (m_pManager->IsHorizontalPosition())
		CXTPDrawHelpers::Triangle(pDC, CPoint(pt.x + 2, pt.y - 5), CPoint(pt.x - 2, pt.y - 1), CPoint(pt.x + 2, pt.y + 3));
	else
		CXTPDrawHelpers::Triangle(pDC, CPoint(pt.x - 5, pt.y + 2), CPoint(pt.x - 1, pt.y - 2), CPoint(pt.x + 3, pt.y + 2));
}

void CXTPTabManager::CNavigateButtonArrowLeft::Reposition(CRect& rcNavigateButtons)
{
	XTPResourceManager()->LoadString(&m_strToolTip, m_pManager->IsHorizontalPosition() ? XTP_IDS_TABNAVIGATEBUTTON_LEFT : XTP_IDS_TABNAVIGATEBUTTON_UP);

	m_bEnabled = m_pManager->GetHeaderOffset() < 0;

	CNavigateButtonArrow::Reposition(rcNavigateButtons);
}

void CXTPTabManager::CNavigateButtonArrowLeft::OnExecute(BOOL bTick)
{
	if (bTick)
	{
		m_pManager->OnScrollHeader(FALSE);
	}
}



//////////////////////////////////////////////////////////////////////
// CXTPTabManager

CXTPTabManager::CXTPTabManager()
{

	m_pSelected = 0;
	m_pHighlighted = 0;
	m_pPressed = 0;

	m_nHeaderOffset = 0;

	m_bAllowReorder = TRUE;

	m_bActive = TRUE;
	m_nScrollDelta = 20;

	m_rcHeaderRect.SetRectEmpty();
	m_rcControl.SetRectEmpty();
	m_rcClient.SetRectEmpty();

	m_bCloseItemButton = xtpTabNavigateButtonNone;

	m_pHighlightedNavigateButton = NULL;

	m_pRowIndexer = new CRowIndexer(this);

	m_arrNavigateButtons.Add(new CNavigateButtonArrowLeft(this, xtpTabNavigateButtonAutomatic));
	m_arrNavigateButtons.Add(new CNavigateButtonArrowRight(this, xtpTabNavigateButtonAutomatic));
	m_arrNavigateButtons.Add(new CNavigateButtonClose(this, xtpTabNavigateButtonNone));

	m_pMarkupContext = NULL;
}

CXTPTabManager::~CXTPTabManager()
{
	for (int i = 0; i < m_arrItems.GetSize(); i++)
	{
		CXTPTabManagerItem* pItem = m_arrItems[i];
		pItem->OnRemoved();
		pItem->InternalRelease();
	}

	m_arrNavigateButtons.RemoveAll();

	delete m_pRowIndexer;

	XTPMarkupReleaseContext(m_pMarkupContext);
}

void CXTPTabManager::EnableMarkup(BOOL bEnable)
{
	BOOL bMarkupContext = m_pMarkupContext != NULL;
	if (bMarkupContext == bEnable)
		return;

	for (int i = 0; i < m_arrItems.GetSize(); i++)
	{
		XTPMarkupReleaseElement(m_arrItems[i]->m_pMarkupUIElement);
	}

	XTPMarkupReleaseContext(m_pMarkupContext);

	if (bEnable)
	{
		m_pMarkupContext = XTPMarkupCreateContext();
	}
}


void CXTPTabManager::SetActive(BOOL bActive)
{
	if (m_bActive != bActive)
	{
		m_bActive = bActive;
		Reposition();
	}
}

BOOL CXTPTabManager::IsNavigateButtonVisible(CXTPTabManagerNavigateButton* pButton)
{
	if (pButton->GetFlags() == xtpTabNavigateButtonAutomatic)
		return IsNavigateButtonAutomaticVisible(pButton);

	if (pButton->GetFlags() == xtpTabNavigateButtonAlways)
	{
		if (pButton->GetID() == xtpTabNavigateButtonClose && pButton->GetItem())
		{
			return pButton->GetItem()->IsClosable();
		}
		return TRUE;
	}

	return FALSE;
}

BOOL CXTPTabManager::IsNavigateButtonAutomaticVisible(CXTPTabManagerNavigateButton* pButton)
{
	if (pButton->GetID() == xtpTabNavigateButtonClose)
	{
		if (pButton->GetItem())
		{
			return pButton->GetItem()->IsSelected() ?  pButton->GetItem()->IsClosable() : FALSE;
		}
		return m_pSelected ? m_pSelected->IsClosable() : FALSE;
	}

	return TRUE;
}

void CXTPTabManager::EnableTabThemeTexture(HWND hWnd, BOOL bEnable)
{
	CXTPWinThemeWrapper themeTabControl;  // Internal helper for drawing XP interface parts.
	themeTabControl.OpenTheme(hWnd, L"TAB");
	themeTabControl.EnableThemeDialogTexture(hWnd, bEnable ? ETDT_ENABLETAB : ETDT_DISABLE | ETDT_USETABTEXTURE);
}

CXTPTabManagerNavigateButton* CXTPTabManager::FindNavigateButton(UINT nID) const
{
	for (int i = 0; i < m_arrNavigateButtons.GetSize(); i++)
	{
		if (m_arrNavigateButtons[i]->m_nID == nID)
			return m_arrNavigateButtons[i];
	}

	return NULL;
}

void CXTPTabManager::SetHeaderOffset(int nOffset)
{
	if (nOffset > 0) nOffset = 0;

	if (nOffset != m_nHeaderOffset)
	{
		m_nHeaderOffset = nOffset;
		Reposition();
	}
}

int CXTPTabManager::GetItemsLength() const
{
	int nLength = 0;

	if (GetLayout() == xtpTabLayoutRotated)
	{
		nLength = GetPaintManager()->GetAppearanceSet()->GetButtonHeight(this)
			* GetItemCount();
	}
	else
	{
		for (int i = 0; i < GetItemCount(); i++)
			nLength += GetItem(i)->GetButtonLength();
	}

	return nLength;
}


void CXTPTabManager::DeleteAllItems()
{
	for (int i = 0; i < m_arrItems.GetSize(); i++)
	{
		CXTPTabManagerItem* pItem = m_arrItems[i];
		pItem->OnRemoved();
		pItem->InternalRelease();
	}

	m_arrItems.RemoveAll();

	m_pHighlighted = m_pSelected = m_pPressed = NULL;

	OnItemsChanged();
}

BOOL CXTPTabManager::DeleteItem(int nItem)
{
	if (nItem < 0 || nItem >= GetItemCount())
		return FALSE;

	CXTPTabManagerItem* pItem = m_arrItems[nItem];

	BOOL bSelected = (m_pSelected == pItem);

	if (m_pHighlighted == pItem)
		m_pHighlighted = NULL;

	m_arrItems.RemoveAt(nItem);

	pItem->OnRemoved();
	pItem->InternalRelease();

	if (bSelected)
	{
		SetCurSel(nItem);
	}


	OnItemsChanged();

	return TRUE;
}

CXTPTabManagerItem* CXTPTabManager::AddItem(int nItem, CXTPTabManagerItem* pItem /*= NULL*/)
{
	if (!pItem)
		pItem = new CXTPTabManagerItem();

	pItem->m_pTabManager = this;

	if (nItem < 0 || nItem > GetItemCount())
		nItem = GetItemCount();

	m_arrItems.InsertAt(nItem, pItem);
	pItem->m_clrItem = xtpTabColorBlue + (GetPaintManager()->m_nItemColor++ % 8);

	if (m_bCloseItemButton != xtpTabNavigateButtonNone)
	{
		pItem->m_arrNavigateButtons.Add(new CNavigateButtonTabClose(pItem, m_bCloseItemButton));
	}

	OnItemsChanged();

	return pItem;
}

void CXTPTabManager::OnItemsChanged()
{
	for (int i = 0; i < GetItemCount(); i++)
	{
		GetItem(i)->m_nIndex = i;
	}
	Reposition();
}

CString CXTPTabManager::GetItemCaption(const CXTPTabManagerItem* pItem) const
{
	return pItem->m_strCaption;
}

HICON CXTPTabManager::GetItemIcon(const CXTPTabManagerItem* pItem) const
{
	return pItem->m_hIcon;
}

void CXTPTabManager::ShowIcons(BOOL bShowIcons)
{
	GetPaintManager()->m_bShowIcons = bShowIcons;
	Reposition();
}

COLORREF CXTPTabManager::GetItemColor(const CXTPTabManagerItem* pItem) const
{
	COLORREF nColor = pItem->m_clrItem;

	if (nColor >= xtpTabColorBlue && nColor <= xtpTabColorMagenta)
		return CXTPTabPaintManager::GetOneNoteColor((XTPTabOneNoteColor)nColor);

	return nColor;
}

void CXTPTabManager::SetCurSel(int nIndex)
{
	if (GetItemCount() != 0)
	{
		nIndex = nIndex < 0 ? 0: nIndex >= GetItemCount() ? GetItemCount() - 1 : nIndex;

		SetSelectedItem(GetItem(nIndex));
	}
	else
	{
		SetSelectedItem(NULL);
	}
}

int CXTPTabManager::GetCurSel() const
{
	if (m_pSelected)
	{
		return m_pSelected->GetIndex();
	}
	return -1;
}

void CXTPTabManager::SetSelectedItem(CXTPTabManagerItem* pItem)
{
	if (m_pSelected != pItem)
	{
		m_pSelected = pItem;
		Reposition();
		EnsureVisible(pItem);
	}
}

void CXTPTabManager::EnsureVisible(CXTPTabManagerItem* pItem)
{
	if (!pItem)
		return;

	GetPaintManager()->EnsureVisible(this, pItem);
}

void CXTPTabManager::SetPosition(XTPTabPosition tabPosition)
{
	GetPaintManager()->m_tabPosition = tabPosition;
	Reposition();
}

void CXTPTabManager::SetLayoutStyle(XTPTabLayoutStyle tabLayout)
{
	GetPaintManager()->m_tabLayout = tabLayout;
	Reposition();
}


CXTPTabManagerItem* CXTPTabManager::HitTest(CPoint point) const
{
	if (!m_rcControl.PtInRect(point))
		return NULL;

	if (!m_rcHeaderRect.IsRectEmpty() && !m_rcHeaderRect.PtInRect(point))
		return NULL;

	for (int i = 0; i < GetItemCount(); i++)
	{
		CXTPTabManagerItem* pItem = GetItem(i);

		if (pItem->GetRect().PtInRect(point) && pItem->IsEnabled() && pItem->IsVisible())
		{
			return pItem;
		}
	}
	return NULL;
}

XTPTabPosition CXTPTabManager::GetPosition() const
{
	return GetPaintManager()->m_tabPosition;
}

XTPTabLayoutStyle CXTPTabManager::GetLayout() const
{
	return GetPaintManager()->m_tabLayout;
}

void CXTPTabManager::MoveItem(CXTPTabManagerItem* pItem, int nIndex)
{
	ASSERT(pItem && pItem->GetTabManager() == this);

	if (!pItem || pItem->GetTabManager() != this)
		return;

	int nOldIndex = pItem->GetIndex();
	if (nOldIndex == nIndex)
		return;

	ASSERT(nOldIndex >= 0);
	ASSERT(nIndex >= 0 && nIndex < GetItemCount());

	if (nIndex < 0 || nIndex >= GetItemCount())
		nIndex = GetItemCount() - 1;

	m_arrItems.RemoveAt(nOldIndex);
	m_arrItems.InsertAt(nIndex, pItem);

	OnItemsChanged();
}

void CXTPTabManager::TrackClick(HWND hWnd, CPoint pt, CXTPTabManagerItem* pItem)
{
	if (GetPaintManager()->m_bHotTracking)
		m_pHighlighted = pItem;

	m_pPressed = pItem;
	BOOL bHighlighted = TRUE;

	Reposition();
	BOOL bAccept = FALSE;

	::SetCapture(hWnd);

	while (::GetCapture() == hWnd)
	{
		MSG msg;
		VERIFY(::GetMessage(&msg, NULL, 0, 0));

		if (::GetCapture() != hWnd)
		{
			DispatchMessage (&msg);
			break;
		}
		switch (msg.message)
		{
			case WM_MOUSEMOVE:
				{
					pt = CPoint((short signed)LOWORD(msg.lParam), (short signed)HIWORD(msg.lParam));

					bHighlighted = pItem->GetRect().PtInRect(pt);

					if (GetPaintManager()->m_bHotTracking)
					{
						CXTPTabManagerItem* pHighlighted = bHighlighted ? pItem : NULL;
						if (pHighlighted != m_pHighlighted)
						{
							m_pHighlighted = pHighlighted;
							RedrawControl(pItem->GetRect(), TRUE);
						}
					}

				}

				break;

			case WM_KEYDOWN:
				if (msg.wParam != VK_ESCAPE)
					break;
			case WM_CANCELMODE:
			case WM_RBUTTONDOWN:
				goto ExitLoop;

			case WM_LBUTTONUP:
				bAccept = TRUE;
				goto ExitLoop;

			default:
				DispatchMessage (&msg);
				break;
		}
	}

ExitLoop:
	ReleaseCapture();

	m_pPressed = FALSE;

	PerformMouseMove(hWnd, pt);

	RedrawControl(NULL, FALSE);

	if (bAccept && bHighlighted)
	{
		OnItemClick(pItem);
	}
}

void CXTPTabManager::ReOrder(HWND hWnd, CPoint pt, CXTPTabManagerItem* pItem)
{
	CXTPTabManagerItem* pSelected = m_pSelected;
	m_pSelected = pItem;

	if (GetPaintManager()->m_bHotTracking)
		m_pHighlighted = pItem;

	Reposition();

	CArray<CRect, CRect&> arrRects;

	for (int j = 0; j < GetItemCount(); j++)
	{
		CRect rc = GetItem(j)->GetRect();
		arrRects.Add(rc);
	}

	int nIndex = pItem->GetIndex();

	::SetCapture(hWnd);

	while (::GetCapture() == hWnd)
	{
		MSG msg;
		VERIFY(::GetMessage(&msg, NULL, 0, 0));

		if (::GetCapture() != hWnd)
		{
			DispatchMessage (&msg);
			break;
		}
		switch (msg.message)
		{
			case WM_MOUSEMOVE:
				{
					pt = CPoint((short signed)LOWORD(msg.lParam), (short signed)HIWORD(msg.lParam));

					for (int i = 0; i < arrRects.GetSize(); i++)
					{
						if (i != pItem->GetIndex() && arrRects[i].PtInRect(pt))
						{

							while (i != pItem->GetIndex())
							{
								int j = i < pItem->GetIndex() ? pItem->GetIndex() - 1 : pItem->GetIndex() + 1;

								CXTPTabManagerItem* p1 = pItem;
								CXTPTabManagerItem* p2 = m_arrItems[pItem->GetIndex()] = m_arrItems[j];
								m_arrItems[j] = p1;

								OnItemsChanged();

								OnSwitchItem(p1, p2);
							}
							break;
						}
					}
				}

				break;

			case WM_KEYDOWN:
				if (msg.wParam != VK_ESCAPE)
					break;
			case WM_CANCELMODE:
			case WM_LBUTTONUP:
			case WM_RBUTTONDOWN:
				goto ExitLoop;

			default:
				DispatchMessage (&msg);
				break;
		}
	}

ExitLoop:
	ReleaseCapture();
	PerformMouseMove(hWnd, pt);

	m_pSelected = pSelected;
	OnItemClick(pItem);

	if (nIndex != pItem->GetIndex())
	{
		OnItemOrderChanged(pItem, nIndex, pItem->GetIndex());
	}
}

void CXTPTabManager::OnItemClick(CXTPTabManagerItem* pItem)
{
	SetSelectedItem(pItem);
}

void CXTPTabManager::SetFocusedItem(CXTPTabManagerItem* pItem)
{
	if (!OnBeforeItemClick(pItem))
		return;

	if (pItem)
	{
		OnItemClick(pItem);
	}
}

CXTPTabManagerItem* CXTPTabManager::FindNextFocusable(int nIndex, int nDirection) const
{
	CXTPTabManagerItem* pItem = NULL;

	do
	{
		nIndex += nDirection;

		pItem = GetItem(nIndex);
		if (!pItem)
			return NULL;

	}
	while (!(pItem->IsVisible() && pItem->IsEnabled()));

	return pItem;
}

BOOL CXTPTabManager::PerformKeyDown(HWND hWnd, UINT nChar)
{
	const int nCount = GetItemCount();
	if (nCount < 1)
		return FALSE;

	if (nChar == VK_LEFT && (DWORD)GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL)
		nChar = VK_RIGHT;
	else if (nChar == VK_RIGHT && (DWORD)GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL)
		nChar = VK_LEFT;

	switch (nChar)
	{
	case VK_HOME:
		SetFocusedItem(FindNextFocusable(-1, +1));
		return TRUE;

	case VK_END:
		SetFocusedItem(FindNextFocusable(nCount, -1));
		return TRUE;

	case VK_LEFT:
		if (IsHorizontalPosition() && m_pSelected && m_pSelected->GetIndex() > 0)
			SetFocusedItem(FindNextFocusable(m_pSelected->GetIndex(), -1));
		return TRUE;

	case VK_UP:
		if (!IsHorizontalPosition() && m_pSelected && m_pSelected->GetIndex() > 0)
			SetFocusedItem(FindNextFocusable(m_pSelected->GetIndex(), -1));
		return TRUE;

	case VK_RIGHT:
		if (IsHorizontalPosition() && m_pSelected && m_pSelected->GetIndex() < nCount - 1)
			SetFocusedItem(FindNextFocusable(m_pSelected->GetIndex(), + 1));
		return TRUE;

	case VK_DOWN:
		if (!IsHorizontalPosition() && m_pSelected && m_pSelected->GetIndex() < nCount - 1)
			SetFocusedItem(FindNextFocusable(m_pSelected->GetIndex(), + 1));
		return TRUE;
	}
	return FALSE;
}

BOOL CXTPTabManager::OnBeforeItemClick(CXTPTabManagerItem* /*pItem*/)
{
	return TRUE;
}


CXTPTabManagerNavigateButton* CXTPTabManager::HitTestNavigateButton(CPoint point, BOOL bHeaderOnly, int* pnIndex) const
{
	int i;
	for (i = 0; i < (int)m_arrNavigateButtons.GetSize(); i++)
	{
		CXTPTabManagerNavigateButton* pButton = m_arrNavigateButtons[i];
		if (pButton->m_rcButton.PtInRect(point))
		{
			if (!pButton->IsEnabled())
				return NULL;

			if (pnIndex)
			{
				*pnIndex = i;
			}
			return pButton;
		}
	}

	if (bHeaderOnly)
		return NULL;

	CXTPTabManagerItem* pItem = HitTest(point);
	if (!pItem)
		return NULL;

	for (i = 0; i < (int)pItem->GetNavigateButtons()->GetSize(); i++)
	{
		CXTPTabManagerNavigateButton* pButton = pItem->GetNavigateButtons()->GetAt(i);
		if (pButton->m_rcButton.PtInRect(point))
		{
			if (!pButton->IsEnabled())
				return NULL;

			if (pnIndex)
			{
				*pnIndex = i;
			}
			return pButton;
		}
	}

	return NULL;
}

BOOL CXTPTabManager::PerformClick(HWND hWnd, CPoint pt, BOOL bNoItemClick)
{
	CXTPTabManagerNavigateButton* pNavigateButton = HitTestNavigateButton(pt, FALSE);
	if (pNavigateButton)
	{
		pNavigateButton->PerformClick(hWnd, pt);
		return TRUE;
	}

	if (bNoItemClick)
		return FALSE;

	CXTPTabManagerItem* pItem = HitTest(pt);

	if (pItem)
	{
		if (!OnBeforeItemClick(pItem))
			return FALSE;

		if (IsAllowReorder())
		{
			ReOrder(hWnd, pt, pItem);
		}
		else if (GetPaintManager()->m_bSelectOnButtonDown)
		{
			OnItemClick(pItem);
		}
		else
		{
			TrackClick(hWnd, pt, pItem);
		}
		return TRUE;
	}

	return FALSE;
}


void CXTPTabManager::PerformMouseMove(HWND hWnd, CPoint pt)
{
	CXTPTabPaintManagerAppearanceSet* pAppearance = GetPaintManager()->GetAppearanceSet();

	if (!CXTPDrawHelpers::IsTopParentActive(hWnd) || IsMouseLocked())
	{
		if (m_pHighlighted)
		{
			CRect rcRedraw(pAppearance->GetButtonDrawRect(m_pHighlighted));
			m_pHighlighted = NULL;

			RedrawControl(rcRedraw, TRUE);
		}
		return;
	}

	if (GetPaintManager()->m_bHotTracking)
	{
		CXTPTabManagerItem* pItem = HitTest(pt);

		if (pItem != m_pHighlighted)
		{
			if (m_pHighlighted)
			{
				CRect rcRedraw(pAppearance->GetButtonDrawRect(m_pHighlighted));
				m_pHighlighted = NULL;

				RedrawControl(rcRedraw, TRUE);
			}

			m_pHighlighted = pItem;

			if (m_pHighlighted)
			{
				RedrawControl(pAppearance->GetButtonDrawRect(m_pHighlighted), FALSE);
			}

			if (pItem)
			{
				TRACKMOUSEEVENT tme =
				{
					sizeof(TRACKMOUSEEVENT), TME_LEAVE, hWnd
				};
				_TrackMouseEvent(&tme);
			}
		}
	}

	CXTPTabManagerNavigateButton* pNavigateButton = HitTestNavigateButton(pt, FALSE);

	if (pNavigateButton != m_pHighlightedNavigateButton)
	{
		if (m_pHighlightedNavigateButton)
		{
			RedrawControl(m_pHighlightedNavigateButton->GetRect(), TRUE);
		}

		m_pHighlightedNavigateButton = pNavigateButton;

		if (m_pHighlightedNavigateButton)
		{
			RedrawControl(m_pHighlightedNavigateButton->GetRect(), FALSE);
		}

		if (pNavigateButton)
		{
			TRACKMOUSEEVENT tme =
			{
				sizeof(TRACKMOUSEEVENT), TME_LEAVE, hWnd
			};
			_TrackMouseEvent(&tme);
		}
	}
}

CString CXTPTabManager::GetItemTooltip(const CXTPTabManagerItem* pItem) const
{
	return pItem->m_strToolTip;
}

INT_PTR CXTPTabManager::PerformToolHitTest(HWND hWnd, CPoint point, TOOLINFO* pTI) const
{
	if (IsMouseLocked())
		return -1;

	int nIndex = -1;
	CXTPTabManagerNavigateButton* pNavigateButton = HitTestNavigateButton(point, FALSE, &nIndex);
	if (pNavigateButton)
	{
		ASSERT(nIndex != -1);
		CString strTip = pNavigateButton->GetTooltip();
		if (strTip.IsEmpty())
			return -1;

		CXTPToolTipContext::FillInToolInfo(pTI, hWnd, pNavigateButton->GetRect(), nIndex, strTip);

		return nIndex;
	}

	CXTPTabManagerItem* pItem = HitTest(point);

	if (pItem)
	{
		if (GetPaintManager()->m_toolBehaviour == xtpTabToolTipNever)
			return -1;

		if (GetPaintManager()->m_toolBehaviour == xtpTabToolTipShrinkedOnly && !pItem->IsItemShrinked())
			return -1;

		CString strTip = GetItemTooltip(pItem);
		if (strTip.IsEmpty())
			return -1;

		CXTPToolTipContext::FillInToolInfo(pTI, hWnd, pItem->GetRect(), pItem->GetIndex(), strTip, pItem->GetCaption(), strTip);

		return pItem->GetIndex();
	}
	return -1;
}

void CXTPTabManager::GetItemMetrics(CSize* lpszNormal, CSize* lpszMin /*= NULL*/, CSize* lpszMax /*= NULL*/) const
{
	CXTPTabPaintManager* pPaintManager = GetPaintManager();
	if (lpszNormal) *lpszNormal = CSize(pPaintManager->m_nFixedTabWidth, 0);
	if (lpszMin) *lpszMin = CSize(pPaintManager->m_nMinTabWidth, 0);
	if (lpszMax) *lpszMax = CSize(pPaintManager->m_nMaxTabWidth, 0);
}

void CXTPTabManager::SetItemMetrics(CSize szNormal, CSize szMin /*= CSize(0, 0)*/, CSize szMax /*= CSize(0, 0)*/)
{
	CXTPTabPaintManager* pPaintManager = GetPaintManager();
	pPaintManager->m_nFixedTabWidth = szNormal.cx;
	pPaintManager->m_nMinTabWidth = szMin.cx;
	pPaintManager->m_nMaxTabWidth = szMax.cx;

	Reposition();
}

BOOL CXTPTabManager::IsDrawStaticFrame() const
{
	return GetPaintManager()->m_bStaticFrame;
}


CXTPTabPaintManagerColorSet* CXTPTabManager::SetColor(XTPTabColorStyle tabColor)
{
	CXTPTabPaintManagerColorSet* pColorSet = GetPaintManager()->SetColor(tabColor);
	Reposition();
	return pColorSet;
}

CXTPTabPaintManagerColorSet* CXTPTabManager::SetColorSet(CXTPTabPaintManagerColorSet* pColorSet)
{
	GetPaintManager()->SetColorSet(pColorSet);
	Reposition();
	return pColorSet;
}
CXTPTabPaintManagerAppearanceSet* CXTPTabManager::SetAppearance(XTPTabAppearanceStyle tabAppearance)
{
	CXTPTabPaintManagerAppearanceSet* pAppearanceSet = GetPaintManager()->SetAppearance(tabAppearance);
	Reposition();
	return pAppearanceSet;
}
CXTPTabPaintManagerAppearanceSet* CXTPTabManager::SetAppearanceSet(CXTPTabPaintManagerAppearanceSet* pAppearanceSet)
{
	GetPaintManager()->SetAppearanceSet(pAppearanceSet);
	Reposition();
	return pAppearanceSet;
}

CXTPTabPaintManagerAppearanceSet* CXTPTabManager::GetAppearanceSet() const
{
	return GetPaintManager()->GetAppearanceSet();
}
CXTPTabPaintManagerColorSet* CXTPTabManager::GetColorSet() const

{
	return GetPaintManager()->GetColorSet();
}

XTPTabAppearanceStyle CXTPTabManager::GetAppearance() const
{
	return GetPaintManager()->GetAppearance();
}

XTPTabColorStyle CXTPTabManager::GetColor() const
{
	return GetPaintManager()->GetColor();
}