// XTPDockingPaneSidePanel.cpp : implementation of the CXTPDockingPaneSidePanel class.
//
// This file is a part of the XTREME DOCKINGPANE 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/XTPColorManager.h"
#include "Common/XTPResourceManager.h"
#include "Common/XTPToolTipContext.h"
#include "Common/XTPSystemHelpers.h"

#include "TabManager/XTPTabManager.h"

#include "XTPDockingPaneDefines.h"
#include "XTPDockingPaneBase.h"
#include "XTPDockingPaneBaseContainer.h"
#include "XTPDockingPane.h"
#include "XTPDockingPaneManager.h"
#include "XTPDockingPaneTabbedContainer.h"
#include "XTPDockingPaneSidePanel.h"
#include "XTPDockingPaneLayout.h"
#include "XTPDockingPaneContext.h"
#include "XTPDockingPaneAutoHidePanel.h"
#include "XTPDockingPanePaintManager.h"

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

#define TID_CHECKACTIVE 1
#define TID_SLIDEIN 2
#define TID_SLIDEOUT 3


AFX_INLINE int ResetStepsCount()
{
	if (CXTPDockingPaneAutoHideWnd::m_nAnimationInterval == 0)
		return 1;

	return max(1, CXTPDockingPaneAutoHideWnd::m_nAnimationDuration / CXTPDockingPaneAutoHideWnd::m_nAnimationInterval);
}

CXTPDockingPaneSidePanel::CXTPDockingPaneSidePanel(CXTPDockingPaneLayout* pLayout)
	: CXTPDockingPaneBaseContainer(xtpPaneTypeSidePanel, pLayout)
{
	m_direction = xtpPaneDockLeft;
	m_bActive = FALSE;

	m_pCaptionButtons = new CXTPDockingPaneCaptionButtons();
	m_pCaptionButtons->Add(new CXTPDockingPaneCaptionButton(XTP_IDS_DOCKINGPANE_CLOSE, this));
	m_pCaptionButtons->Add(new CXTPDockingPaneCaptionButton(XTP_IDS_DOCKINGPANE_AUTOHIDE, this));

	m_bSlideOut = m_bExpanded = m_bCollapsed = FALSE;

	m_nSlideStep = 0;
	m_nStepsCount = ResetStepsCount();

	m_nDeactivationCount = 0;

}

CXTPDockingPaneSidePanel::~CXTPDockingPaneSidePanel()
{

}

void CXTPDockingPaneSidePanel::DeletePane()
{
	InternalRelease();
}

void CXTPDockingPaneSidePanel::OnFinalRelease()
{
	if (m_hWnd != NULL)
		DestroyWindow();

	CCmdTarget::OnFinalRelease();
}

BOOL CXTPDockingPaneSidePanel::Init(CXTPDockingPaneBase* pBasePane, XTPDockingPaneDirection direction, CRect rc)
{
	CXTPDockingPaneBaseList lstPanes;
	pBasePane->FindPane(xtpPaneTypeDockingPane, &lstPanes);
	if (lstPanes.GetCount() == 0)
		return FALSE;

	m_direction = direction;
	m_rcWindow = rc;
	CreateContainer();

	CXTPDockingPaneTabbedContainer* pContainer = (CXTPDockingPaneTabbedContainer*)GetDockingPaneManager()->
		OnCreatePane(xtpPaneTypeTabbedContainer, m_pLayout);

	BOOL bInit = FALSE;

	POSITION pos = lstPanes.GetHeadPosition();
	while (pos)
	{
		CXTPDockingPane* pPane = (CXTPDockingPane*)lstPanes.GetNext(pos);

		if (pPane->GetContainer())
		{
			pPane->GetContainer()->RemovePane(pPane);
		}

		if (!bInit)
		{
			pContainer->Init((CXTPDockingPane*)pPane, this);
		}
		else
		{
			pContainer->_InsertPane(pPane, FALSE);
		}
		bInit = TRUE;
	}

	_InsertPane(pContainer);

	OnFocusChanged();

	m_nIdleFlags = 0;

	return TRUE;
}

void CXTPDockingPaneSidePanel::CreateContainer()
{
	m_bInRecalcLayout = TRUE;

	if (!m_hWnd)
	{
		VERIFY(Create(m_rcWindow));
	}

	m_bInRecalcLayout = FALSE;
}

BOOL CXTPDockingPaneSidePanel::Create(CRect rc)
{
	if (m_hWnd)
		return TRUE;

	if (!CreateEx(0, _T("XTPDockingPaneSidePanel"), _T(""), WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_CHILD, rc, GetDockingPaneManager()->GetSite(), 0))
	{
		return FALSE;
	}
	m_pCaptionButtons->CheckForMouseOver(CPoint(-1, -1));

	return TRUE;
}

void CXTPDockingPaneSidePanel::PostNcDestroy()
{
	// prevent auto deleting
}

void CXTPDockingPaneSidePanel::RecalcLayout(BOOL /*bNotify*/)
{
	if (m_bInRecalcLayout)
		return;

	m_bInRecalcLayout = TRUE;

	CXTPDockingPaneManager* pManager = GetDockingPaneManager();
	CWnd* pSite = GetDockingSite();
	CRect rcClient = pManager->GetClientPane()->GetPaneWindowRect();
	pSite->ScreenToClient(rcClient);

	OnSizeParentEx(pManager, pSite, rcClient);

	m_bInRecalcLayout = FALSE;


}

void CXTPDockingPaneSidePanel::OnFocusChanged()
{
	CXTPDockingPaneTabbedContainer* pContainer = GetTopContainer();
	if (pContainer)
	{
		pContainer->OnFocusChanged();
		BOOL bActive = pContainer->IsActive();

		if (bActive != m_bActive)
		{
			m_bActive = bActive;
			InvalidatePane(FALSE);
		}
	}
}

void CXTPDockingPaneSidePanel::InvalidatePane(BOOL /*bSelectionChanged*/)
{
	if (m_hWnd)
	{
		Invalidate(FALSE);
	}
}

CXTPDockingPaneTabbedContainer* CXTPDockingPaneSidePanel::GetTopContainer() const
{
	return (CXTPDockingPaneTabbedContainer*)GetFirstPane();
}

void CXTPDockingPaneSidePanel::OnChildContainerChanged(CXTPDockingPaneBase* /*pContainer*/)
{
	if (!m_hWnd)
		return;

	m_bExpanded = FALSE;
	m_nSlideStep = 0;

	if (IsEmpty())
	{
		DestroyWindow();
	}
	else
	{
		PostMessage(WM_IDLEUPDATECMDUI);
	}

	GetDockingPaneManager()->RecalcFrameLayout(NULL, TRUE);
}

void CXTPDockingPaneSidePanel::RemovePane(CXTPDockingPaneBase* pPane)
{
	POSITION pos = m_lstPanes.Find(pPane);
	ASSERT(pos);

	m_lstPanes.RemoveAt(pos);

	if (IsEmpty())
	{
		DestroyWindow();
	}

	pPane->m_pParentContainer = NULL;
}

struct CXTPDockingPaneSidePanel::LENGTH
{
	int nPos;
	int nSize;
	int nIndex;
	int nHeight;
	CXTPDockingPaneSidePanel* pPanel;

	int StartPos() {
		return nPos;
	}
	int EndPos() {
		return nPos + nSize;
	}
};

int _cdecl CXTPDockingPaneSidePanel::CompareLength(const void *arg1, const void *arg2)
{
	int& dOffset1 = ((LENGTH*)arg1)->nPos;
	int& dOffset2 = ((LENGTH*)arg2)->nPos;
	return dOffset1 - dOffset2;
}

void CXTPDockingPaneSidePanel::SortLength(LENGTH* pLength, int nFirstIndex, int nLastIndex)
{
	if (nLastIndex - nFirstIndex < 1)
		return;

	qsort(pLength + nFirstIndex, nLastIndex - nFirstIndex + 1, sizeof(LENGTH), CompareLength);

	for (int i = nFirstIndex; i <= nLastIndex; i++)
	{
		CXTPDockingPaneSidePanel* pPanel = pLength[i].pPanel;
		pPanel->m_nLengthIndex = i;
	}
}

void CXTPDockingPaneSidePanel::MovePanel(XTPDockingPaneDirection direction, CRect rect)
{
	m_direction = direction;
	m_rcWindow = rect;

	m_pLayout->MoveToTail(this);
	RecalcLayout(FALSE);
	InvalidatePane(FALSE);
}

void CXTPDockingPaneSidePanel::MovePanel(LENGTH* pLength, CRect rect)
{
	m_bInRecalcLayout = TRUE;

	if (m_bCollapsed && m_bSlideOut)
	{
		m_bSlideOut = FALSE;
		m_bExpanded = FALSE;
		m_nSlideStep = 0;
		KillTimer(TID_SLIDEOUT);
		OnAction(xtpPaneActionCollapsed);
	}

	BOOL bHorizontal = IsHorizontal();
	CSize sz = bHorizontal ? CSize(pLength->nSize, pLength->nHeight) : CSize(pLength->nHeight, pLength->nSize);

	int nMinHeight = GetMinHeight();

	if (m_bCollapsed && !m_bExpanded)
	{
		if (bHorizontal) sz.cy = nMinHeight; else sz.cx = nMinHeight;
	}
	else
	{
		if (bHorizontal) sz.cy = max(sz.cy, nMinHeight); else sz.cx = max(sz.cx, nMinHeight);
	}

	CPoint pt;

	switch (m_direction)
	{
	case xtpPaneDockLeft: pt = CPoint(rect.left, pLength->nPos + rect.top); break;
	case xtpPaneDockTop: pt = CPoint(rect.left + pLength->nPos, rect.top); break;
	case xtpPaneDockRight: pt = CPoint(rect.right - sz.cx, rect.top + pLength->nPos); break;
	case xtpPaneDockBottom: pt = CPoint(rect.left + pLength->nPos, rect.bottom - sz.cy); break;
	}

	SetWindowPos(&CWnd::wndTop, pt.x, pt.y, sz.cx, sz.cy, SWP_NOOWNERZORDER | SWP_SHOWWINDOW);

	CRect rcClient;
	GetClientRect(rcClient);

	GetPaintManager()->AdjustClientRect(this, rcClient);

	CXTPDockingPaneTabbedContainer* pContainer = GetTopContainer();
	if (pContainer)
	{
		pContainer->OnSizeParent(this, rcClient, NULL);
	}
	m_bInRecalcLayout = FALSE;
}

void CXTPDockingPaneSidePanel::OnSizeParentEx(CSidePanelArray& arrSide, CWnd* /*pParent*/, CRect rect)
{
	XTPDockingPaneDirection direction = arrSide[0]->m_direction;
	BOOL bHorizontal = direction == xtpPaneDockTop || direction == xtpPaneDockBottom;

	int nCount = (int)arrSide.GetSize(), i, j;

	LENGTH* pLength = new LENGTH[nCount];

	for (i = 0; i < nCount; i++)
	{
		CXTPDockingPaneSidePanel* pPanel = arrSide[i];

		CRect rcPanel(pPanel->m_rcWindow);

		MINMAXINFO mmi;
		pPanel->GetMinMaxInfo(&mmi);

		if (rcPanel.Height() > mmi.ptMaxTrackSize.y) rcPanel.bottom = rcPanel.top + mmi.ptMaxTrackSize.y;
		if (rcPanel.Height() < mmi.ptMinTrackSize.y) rcPanel.bottom = rcPanel.top + mmi.ptMinTrackSize.y;
		if (rcPanel.Width() > mmi.ptMaxTrackSize.x) rcPanel.right = rcPanel.left + mmi.ptMaxTrackSize.x;
		if (rcPanel.Width() < mmi.ptMinTrackSize.x) rcPanel.right = rcPanel.left + mmi.ptMinTrackSize.x;

		pLength[i].nSize = bHorizontal ? rcPanel.Width() : rcPanel.Height();
		pLength[i].nPos = bHorizontal ? rcPanel.left : rcPanel.top;
		pLength[i].nIndex = i;
		pLength[i].pPanel = pPanel;

		pLength[i].nHeight = bHorizontal ? min(rect.Height(), rcPanel.Height()) : min(rect.Width(), rcPanel.Width());
	}

	SortLength(pLength, 0, nCount - 1);

	for (i = 1; i < nCount; i++)
	{
		int nLengthIndex = arrSide[i]->m_nLengthIndex;
		int nLeft = pLength[nLengthIndex].StartPos();
		int nRight = pLength[nLengthIndex].EndPos();

		for (j = nLengthIndex - 1; j >= 0; j--)
		{
			if (pLength[j].EndPos() > nLeft)
			{
				if (pLength[j].nIndex < i)
				{
					pLength[j].nPos = nLeft - pLength[j].nSize;
					nLeft = pLength[j].StartPos();
				}
			}
			else break;
		}
		SortLength(pLength, j + 1, nLengthIndex - 1);

		for (j = nLengthIndex + 1; j < nCount; j++)
		{
			if (pLength[j].StartPos() < nRight)
			{
				if (pLength[j].nIndex < i)
				{
					pLength[j].nPos = nRight;
					nRight = pLength[j].EndPos();
				}
			}
			else break;
		}
		SortLength(pLength, nLengthIndex + 1, j - 1);
	}


	int nBegin = 0;
	int nEnd = bHorizontal ? rect.Width() : rect.Height();
	int nRight = nEnd;
	int nLeft = nBegin;

	for (i = nCount - 1; i >= 0; i--)
	{
		if (pLength[i].EndPos() > nRight)
		{
			pLength[i].nPos = nRight - pLength[i].nSize;
			nRight = pLength[i].StartPos();
		}
		else break;
	}
	for (i = 0; i < nCount; i++)
	{
		if (pLength[i].StartPos() < nLeft)
		{
			pLength[i].nPos = nLeft;
			nLeft = pLength[i].EndPos();
		}
		else break;
	}

	int nTotal = pLength[nCount - 1].EndPos();
	int cxExtra = nTotal - nEnd;
	if (cxExtra > 0)
	{
		nLeft = 0;

		for (i = 0; i < nCount; i++)
		{
			pLength[i].nPos = nLeft;

			int cxAddExtra = nTotal == 0 ? cxExtra / nCount : cxExtra * pLength[i].nSize / nTotal;

			nTotal -= pLength[i].nSize;
			pLength[i].nSize -= cxAddExtra;

			cxExtra -= cxAddExtra;
			nLeft = pLength[i].EndPos();
		}
	}

	for (i = 0; i < nCount; i++)
	{
		int nIndex = pLength[i].nIndex;
		CXTPDockingPaneSidePanel* pPanel = arrSide[nIndex];

		pPanel->MovePanel(&pLength[i], rect);
	}

	delete[] pLength;
}

void CXTPDockingPaneSidePanel::OnSizeParentEx(CXTPDockingPaneManager* pManager, CWnd* pParent, CRect rect)
{
	rect.DeflateRect(pManager->GetSideDockingMargin());

	CXTPDockingPaneBaseList& list = pManager->GetPaneStack();
	CSidePanelArray arrPanels[4];

	POSITION pos = list.GetHeadPosition();
	while (pos)
	{
		CXTPDockingPaneBase* pPane = list.GetNext(pos);
		if (pPane->GetType() == xtpPaneTypeSidePanel)
		{
			CXTPDockingPaneSidePanel* pPanel = (CXTPDockingPaneSidePanel*)pPane;

			if (!pPanel->IsEmpty())
			{
				arrPanels[pPanel->m_direction].Add(pPanel);
			}
		}
	}

	for (int i = 0; i < 4; i++)
	{
		if (arrPanels[i].GetSize() > 0)
		{
			OnSizeParentEx(arrPanels[i], pParent, rect);
		}
	}
}

void CXTPDockingPaneSidePanel::_InsertPane(CXTPDockingPaneBase* pPane)
{
	ASSERT(m_lstPanes.IsEmpty());
	ASSERT(pPane->GetType() == xtpPaneTypeTabbedContainer);

	if (!m_hWnd && !m_pLayout->IsUserLayout() && pPane->GetPaneHwnd())
	{
		CreateContainer();
	}

	m_lstPanes.AddTail(pPane);

	pPane->SetParentContainer(this);
	pPane->SetDockingSite(this);

	pPane->OnParentContainerChanged(this);

	((CXTPDockingPaneTabbedContainer*)pPane)->ShowTitle(FALSE);

}

void CXTPDockingPaneSidePanel::Copy(CXTPDockingPaneBase* pCloneBase, CXTPPaneToPaneMap* pMap, DWORD /*dwIgnoredOptions*/)
{
	CXTPDockingPaneSidePanel* pClone = (CXTPDockingPaneSidePanel*)pCloneBase;

	ASSERT(pClone);
	if (!pClone)
		return;

	m_direction = pClone->m_direction;
	m_rcWindow = pClone->m_rcWindow;
	m_bCollapsed = pClone->m_bCollapsed;

	m_pDockingSite = GetDockingPaneManager()->GetSite();

	CXTPDockingPaneTabbedContainer* pContainer = pClone->GetTopContainer();
	if (pContainer)
	{
		_InsertPane(pContainer->Clone(m_pLayout, pMap));
	}
}

CString CXTPDockingPaneSidePanel::GetTitle() const
{
	CXTPDockingPaneTabbedContainer* pContainer = GetTopContainer();
	if (!pContainer)
		return _T("");

	return pContainer->GetTitle();
}

BEGIN_MESSAGE_MAP(CXTPDockingPaneSidePanel, CMiniFrameWnd)
	ON_WM_PAINT()
	ON_WM_LBUTTONDOWN()
	ON_WM_TIMER()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_RBUTTONUP()
	ON_WM_SIZE()
	ON_WM_MOUSEMOVE()
	ON_MESSAGE_VOID(WM_MOUSELEAVE, OnMouseLeave)
	ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
	ON_WM_NCHITTEST_EX()
	ON_WM_NCLBUTTONDOWN()
END_MESSAGE_MAP()

void CXTPDockingPaneSidePanel::OnPaint()
{
	CPaintDC dcPaint(this);
	CXTPClientRect rc(this);
	CXTPBufferDC dc(dcPaint);

	GetPaintManager()->DrawSidePanel(&dc, this, rc);
}

void CXTPDockingPaneSidePanel::OnSize(UINT nType, int cx, int cy)
{
	CMiniFrameWnd::OnSize(nType, cx, cy);

	Invalidate(FALSE);
}

CXTPDockingPaneCaptionButton* CXTPDockingPaneSidePanel::HitTestCaptionButton(CPoint point) const
{
	for (int i = 0; i < m_pCaptionButtons->GetSize(); i++)
	{
		CXTPDockingPaneCaptionButton* pButton = m_pCaptionButtons->GetAt(i);
		if (pButton->PtInRect(point))
			return pButton->IsEnabled() ? pButton : NULL;
	}
	return NULL;
}

void CXTPDockingPaneSidePanel::OnCaptionButtonClick(CXTPDockingPaneCaptionButton* pButton)
{
	CXTPDockingPaneManager* pManager = GetDockingPaneManager();

	switch (pButton->GetID())
	{
		case XTP_IDS_DOCKINGPANE_CLOSE:
			{
				CXTPDockingPaneBaseList lstPanes;
				FindPane(xtpPaneTypeDockingPane, &lstPanes);

				POSITION pos = lstPanes.GetTailPosition();
				while (pos)
				{
					CXTPDockingPane* pPane = (CXTPDockingPane*)lstPanes.GetPrev(pos);

					if ((pPane->GetOptions() & xtpPaneNoCloseable) != 0)
						continue;

					pPane->InternalAddRef();

					if (!pManager->NotifyAction(xtpPaneActionClosing, pPane))
					{
						pPane->Close();
						pManager->NotifyAction(xtpPaneActionClosed, pPane);
					}

					pPane->InternalRelease();
				}
			}
			break;

		case XTP_IDS_DOCKINGPANE_AUTOHIDE:
			OnPinButtonClick();
			break;
	}
}

CXTPDockingPane* CXTPDockingPaneSidePanel::GetSelectedPane() const
{
	if (GetTopContainer())
		return GetTopContainer()->GetSelected();
	return NULL;
}

BOOL CXTPDockingPaneSidePanel::IsCaptionButtonVisible(CXTPDockingPaneCaptionButton* pButton)
{
	CXTPDockingPane* pSelectedPane = GetSelectedPane();

	if (pButton->GetID() == XTP_IDS_DOCKINGPANE_CLOSE)
		return pSelectedPane && ((pSelectedPane->GetOptions() & xtpPaneNoCloseable) == 0);

	if (pButton->GetID() == XTP_IDS_DOCKINGPANE_AUTOHIDE)
	{
		pButton->SetState(m_bCollapsed ? xtpPanePinPushed : 0);
		return pSelectedPane && ((pSelectedPane->GetOptions() & xtpPaneNoHideable) == 0);
	}

	return TRUE;
}


void CXTPDockingPaneSidePanel::_RestoreFocus()
{
	CXTPDockingPaneTabbedContainer* pContainer = GetTopContainer();
	if (pContainer) pContainer->_RestoreFocus();
}

void CXTPDockingPaneSidePanel::OnCaptionLButtonDown(CPoint point)
{
	if (GetKeyState(VK_LBUTTON) < 0)
	{
		if (OnAction(xtpPaneActionDragging))
			return;

		CXTPDockingPaneContext* pContext = GetDockingPaneManager()->GetDockingContext();

		CRect rcWindow;
		GetWindowRect(rcWindow);

		pContext->Drag(GetTopContainer(), point, rcWindow);
	}
}

void CXTPDockingPaneSidePanel::OnLButtonDown(UINT /*nFlags*/, CPoint point)
{
	if (m_bCollapsed && !m_bExpanded)
	{
		Expand();
		GetCursorPos(&point);
		ScreenToClient(&point);
		InvalidatePane(FALSE);
		UpdateWindow();
	}

	_RestoreFocus();

	CXTPDockingPaneCaptionButton* pButton = HitTestCaptionButton(point);

	if (pButton)
	{
		if (pButton->Click(this, point))
		{
			OnCaptionButtonClick(pButton);
		}

		return;
	}

	CRect rcCaption = GetPaintManager()->GetPaneCaptionRect(this);

	if (rcCaption.PtInRect(point))
	{
		ClientToScreen(&point);
		OnCaptionLButtonDown(point);
	}
}

void CXTPDockingPaneSidePanel::OnLButtonDblClk(UINT /*nFlags*/, CPoint point)
{
	if (HitTestCaptionButton(point))
		return;

	CRect rcCaption = GetPaintManager()->GetPaneCaptionRect(this);

	if (rcCaption.PtInRect(point))
	{
		GetDockingPaneManager()->ToggleDocking(GetTopContainer());
	}
}


void CXTPDockingPaneSidePanel::OnMouseMove(UINT nFlags, CPoint point)
{
	m_pCaptionButtons->CheckForMouseOver(point);

	if (m_bCollapsed && point != CPoint(-1, -1) && !m_bExpanded)
	{
		TRACKMOUSEEVENT tme =
		{
			sizeof(TRACKMOUSEEVENT), TME_HOVER, m_hWnd, CXTPDockingPaneAutoHideWnd::m_nMouseHoverDelay
		};
		_TrackMouseEvent(&tme);
	}

	CWnd::OnMouseMove(nFlags, point);
}

LRESULT CXTPDockingPaneSidePanel::OnMouseHover(WPARAM, LPARAM)
{
	if (m_bCollapsed)
	{
		if (!CXTPDrawHelpers::IsTopParentActive(m_hWnd))
			return 1;

		Expand();
	}

	return 1;
}

void CXTPDockingPaneSidePanel::OnMouseLeave()
{
	OnMouseMove(0, CPoint(-1, -1));
}

void CXTPDockingPaneSidePanel::OnPinButtonClick()
{
	if (!m_hWnd)
		return;

	BOOL bPinning = m_bCollapsed;

	if (OnAction(bPinning ? xtpPaneActionPinning : xtpPaneActionUnpinning))
		return;

	if (!m_bCollapsed)
	{
		Collapse(TRUE);
	}
	else
	{
		if (m_nStepsCount != m_nSlideStep)
		{
			SetWindowPos(0, 0, 0, m_rcWindow.Width(), m_rcWindow.Height(), SWP_NOZORDER | SWP_NOMOVE);
		}
		m_bCollapsed = FALSE;
		m_bExpanded = FALSE;

		KillTimer(TID_CHECKACTIVE);
		KillTimer(TID_SLIDEOUT);

	}
	InvalidatePane(FALSE);

	OnAction(bPinning ? xtpPaneActionPinned : xtpPaneActionUnpinned);
}

int CXTPDockingPaneSidePanel::GetMinHeight()
{
	CRect rc = GetPaintManager()->GetPaneCaptionRect(this);
	return 2 + (IsHorizontal() ? rc.Height() : rc.Width());
}

void CXTPDockingPaneSidePanel::DoSlideStep(BOOL bActivate)
{
	m_bInRecalcLayout = TRUE;

	int nMinHeight = GetMinHeight();
	BOOL bHorizontal = IsHorizontal();

	CXTPWindowRect rc(this);
	int nSize = max(nMinHeight, m_nSlideStep * ( bHorizontal ? m_rcWindow.Height() : m_rcWindow.Width()) / m_nStepsCount);

	switch (m_direction)
	{
		case xtpPaneDockLeft: rc.right = rc.left + nSize; break;
		case xtpPaneDockTop: rc.bottom = rc.top + nSize; break;
		case xtpPaneDockRight: rc.left = rc.right - nSize; break;
		case xtpPaneDockBottom: rc.top = rc.bottom - nSize; break;
	}

	GetParent()->ScreenToClient(rc);

	SetWindowPos(&CWnd::wndTop, rc.left, rc.top, rc.Width(), rc.Height(), (!bActivate ? SWP_NOZORDER | SWP_NOACTIVATE : SWP_NOACTIVATE));
	Invalidate(FALSE);

	CRect rcClient;
	GetClientRect(rcClient);

	GetPaintManager()->AdjustClientRect(this, rcClient);

	CXTPDockingPaneTabbedContainer* pContainer = GetTopContainer();
	if (pContainer)
	{
		pContainer->OnSizeParent(this, rcClient, NULL);
	}


	m_bInRecalcLayout = FALSE;
}


void CXTPDockingPaneSidePanel::Expand()
{
	if (m_bCollapsed)
	{
		if (m_bSlideOut)
		{
			m_bSlideOut = FALSE;
			m_bExpanded = FALSE;
			KillTimer(TID_SLIDEOUT);
			OnAction(xtpPaneActionCollapsed);
		}

		if (OnAction(xtpPaneActionExpanding))
			return;

		m_bExpanded = TRUE;
		m_nSlideStep = m_nStepsCount = ResetStepsCount();
		DoSlideStep(TRUE);

		m_nDeactivationCount = 8;
		SetTimer(TID_CHECKACTIVE, 100, NULL);

		OnAction(xtpPaneActionExpanded);
	}
}

void CXTPDockingPaneSidePanel::Collapse(BOOL bDelay)
{
	m_nSlideStep = m_nStepsCount = ResetStepsCount();

	m_bCollapsed = TRUE;
	m_bExpanded = TRUE;

	if (!bDelay)
	{
		if (!OnAction(xtpPaneActionCollapsing))
		{
			m_bExpanded = FALSE;
			m_nSlideStep = 0;
			DoSlideStep(FALSE);
			KillTimer(TID_SLIDEOUT);
			KillTimer(TID_CHECKACTIVE);
			OnAction(xtpPaneActionCollapsed);
			return;
		}
	}

	if (m_bSlideOut)
	{
		m_bSlideOut = FALSE;
		KillTimer(TID_SLIDEOUT);
		OnAction(xtpPaneActionCollapsed);
	}

	m_nDeactivationCount = 6;
	SetTimer(TID_CHECKACTIVE, 100, NULL);
}

void CXTPDockingPaneSidePanel::OnTimer(UINT_PTR nIDEvent)
{
	if (nIDEvent == TID_SLIDEOUT && m_bSlideOut)
	{
		m_nSlideStep--;

		if (m_nSlideStep > -1)
			DoSlideStep();
		else
		{
			m_bSlideOut = FALSE;
			m_bExpanded = FALSE;
			KillTimer(TID_SLIDEOUT);
			OnAction(xtpPaneActionCollapsed);
		}
	}

	if (nIDEvent == TID_CHECKACTIVE)
	{
		CPoint pt;
		GetCursorPos(&pt);

		CWnd* pFocus = GetFocus();
		BOOL bActive = (pFocus->GetSafeHwnd() && (pFocus == this || IsChild(pFocus)));

		if (!bActive && !m_bSlideOut && !CXTPWindowRect(this).PtInRect(pt) && ::GetCapture() == NULL)
		{
			if (--m_nDeactivationCount <= 0)
			{
				if (OnAction(xtpPaneActionCollapsing))
				{
					m_nDeactivationCount = 6;
					return;
				}

				m_bSlideOut = TRUE;
				SetTimer(TID_SLIDEOUT, CXTPDockingPaneAutoHideWnd::m_nAnimationInterval, NULL);

				KillTimer(TID_CHECKACTIVE);
			}
		}
	}

	CMiniFrameWnd::OnTimer(nIDEvent);
}

void CXTPDockingPaneSidePanel::GetMinMaxInfo(LPMINMAXINFO pMinMaxInfo) const
{
	CXTPDockingPaneBase::GetMinMaxInfo(pMinMaxInfo);

	if (IsEmpty())
		return;

	GetTopContainer()->GetMinMaxInfo(pMinMaxInfo);

	CSize szBorder(6, 6);

	int nCaptionHeight = GetPaintManager()->GetCaptionHeight();
	if (IsHorizontal()) szBorder.cy += nCaptionHeight; else szBorder.cx += nCaptionHeight;

	pMinMaxInfo->ptMinTrackSize.x += szBorder.cx;
	pMinMaxInfo->ptMinTrackSize.y += szBorder.cy;

	pMinMaxInfo->ptMaxTrackSize.x += szBorder.cx;
	pMinMaxInfo->ptMaxTrackSize.y += szBorder.cy;

	pMinMaxInfo->ptMinTrackSize.x = max(pMinMaxInfo->ptMinTrackSize.x, nCaptionHeight * 2);
	pMinMaxInfo->ptMinTrackSize.y = max(pMinMaxInfo->ptMinTrackSize.y, nCaptionHeight * 2);

}

BOOL CXTPDockingPaneSidePanel::IsHorizontal() const
{
	return m_direction == xtpPaneDockTop || m_direction == xtpPaneDockBottom;
}

BOOL CXTPDockingPaneSidePanel::IsResizable(int nHit)
{
	if (IsHorizontal())
	{
		if (nHit == HTTOP) return m_direction == xtpPaneDockBottom;
		if (nHit == HTBOTTOM) return m_direction == xtpPaneDockTop;
		if (nHit == HTLEFT || nHit == HTRIGHT) return TRUE;
	}
	else
	{
		if (nHit == HTLEFT) return m_direction == xtpPaneDockRight;
		if (nHit == HTRIGHT) return m_direction == xtpPaneDockLeft;
		if (nHit == HTTOP || nHit == HTBOTTOM) return TRUE;
	}

	return FALSE;
}

void CXTPDockingPaneSidePanel::OnNcLButtonDown(UINT nHitTest, CPoint point)
{
	if (nHitTest >= HTSIZEFIRST && nHitTest <= HTSIZELAST)
	{
		_RestoreFocus();
		Expand();
		SetCapture();
		m_pLayout->MoveToTail(this);

		CXTPWindowRect rcWindow(this);

		CRect rcClient(GetDockingPaneManager()->GetClientPane()->GetPaneWindowRect());
		rcClient.DeflateRect(GetDockingPaneManager()->GetSideDockingMargin());

		rcWindow.OffsetRect(-rcClient.TopLeft());


		MINMAXINFO mmi;
		GetMinMaxInfo(&mmi);

		BOOL bResizeTop = nHitTest == HTTOP || nHitTest == HTTOPLEFT || nHitTest == HTTOPRIGHT;
		BOOL bResizeBottom = nHitTest == HTBOTTOM || nHitTest == HTBOTTOMLEFT || nHitTest == HTBOTTOMRIGHT;
		BOOL bResizeRight = nHitTest == HTRIGHT || nHitTest == HTTOPRIGHT || nHitTest == HTBOTTOMRIGHT;
		BOOL bResizeLeft = nHitTest == HTLEFT || nHitTest == HTTOPLEFT || nHitTest == HTBOTTOMLEFT;

		while (::GetCapture() == m_hWnd)
		{
			MSG msg;

			if (!GetMessage(&msg, NULL, 0, 0))
				break;

			if (msg.message == WM_MOUSEMOVE)
			{
				CPoint pt = CPoint(msg.lParam);
				ClientToScreen(&pt);

				CPoint ptOffset = pt - point;
				point = pt;

				if (bResizeTop) rcWindow.top += ptOffset.y;
				if (bResizeBottom) rcWindow.bottom += ptOffset.y;
				if (bResizeRight) rcWindow.right += ptOffset.x;
				if (bResizeLeft) rcWindow.left += ptOffset.x;

				m_rcWindow = rcWindow;

				if (m_rcWindow.Height() > mmi.ptMaxTrackSize.y)
				{
					if (bResizeTop) m_rcWindow.top = m_rcWindow.bottom - mmi.ptMaxTrackSize.y; else m_rcWindow.bottom = m_rcWindow.top + mmi.ptMaxTrackSize.y;
				}
				if (m_rcWindow.Height() < mmi.ptMinTrackSize.y)
				{
					if (bResizeTop) m_rcWindow.top = m_rcWindow.bottom - mmi.ptMinTrackSize.y; else m_rcWindow.bottom = m_rcWindow.top + mmi.ptMinTrackSize.y;
				}

				if (m_rcWindow.Width() > mmi.ptMaxTrackSize.x)
				{
					if (bResizeLeft) m_rcWindow.left = m_rcWindow.right - mmi.ptMaxTrackSize.x; else m_rcWindow.right = m_rcWindow.left + mmi.ptMaxTrackSize.x;
				}
				if (m_rcWindow.Width() < mmi.ptMinTrackSize.x)
				{
					if (bResizeLeft) m_rcWindow.left = m_rcWindow.right - mmi.ptMinTrackSize.x; else m_rcWindow.right = m_rcWindow.left + mmi.ptMinTrackSize.x;
				}

				RecalcLayout(FALSE);
			}
			else if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE) break;
			else if (msg.message == WM_LBUTTONUP) break;
			else ::DispatchMessage(&msg);
		}

		if (CWnd::GetCapture() == this) ReleaseCapture();
	}
}

LRESULT CXTPDockingPaneSidePanel::OnNcHitTest(CPoint point)
{
	if (m_bCollapsed && !m_bExpanded)
		return HTCLIENT;

	CXTPWindowRect rcWindow(this);
	CRect rcBorders = rcWindow;
	rcBorders.DeflateRect(3, 3);

	if (rcWindow.PtInRect(point) && !rcBorders.PtInRect(point))
	{
		rcBorders.DeflateRect(8, 8);

		int ht = 0;

		if (point.y < rcBorders.top && IsResizable(HTTOP))
			ht = (HTTOP - HTSIZEFIRST + 1);
		else if (point.y >= rcBorders.bottom  && IsResizable(HTBOTTOM))
			ht = (HTBOTTOM - HTSIZEFIRST + 1);

		if (point.x < rcBorders.left && IsResizable(HTLEFT))
			ht += (HTLEFT - HTSIZEFIRST + 1);
		else if (point.x >= rcBorders.right  && IsResizable(HTRIGHT))
			ht += (HTRIGHT - HTSIZEFIRST + 1);

		return (LRESULT)(ht + HTSIZEFIRST - 1);
	}

	return HTCLIENT;
}

INT_PTR CXTPDockingPaneSidePanel::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
{

	ASSERT_VALID(this);
	ASSERT(::IsWindow(m_hWnd));

	// check child windows first by calling CControlBar
	INT_PTR nHit = CWnd::OnToolHitTest(point, pTI);
	if (nHit != -1)
		return nHit;

	CXTPDockingPaneCaptionButton* pButton = HitTestCaptionButton(point);

	if (pButton)
	{
		nHit = (INT_PTR)pButton->GetID();
		CString strTip;
		XTPResourceManager()->LoadString(&strTip, (UINT)nHit);

		if (strTip.GetLength() == 0)
			return -1;

		CXTPToolTipContext::FillInToolInfo(pTI, m_hWnd, pButton->GetRect(), nHit, strTip);

		return nHit;
	}

	return -1;
}

BOOL CXTPDockingPaneSidePanel::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
	CXTPDockingPaneManager* pManager = GetDockingPaneManager();

	if (pManager)
	{
		pManager->GetToolTipContext()->FilterToolTipMessage(this, message, wParam, lParam);
	}

	return CMiniFrameWnd::OnWndMsg(message, wParam, lParam, pResult);
}

void CXTPDockingPaneSidePanel::OnRButtonUp(UINT /*nFlags*/, CPoint point)
{
	XTP_DOCKINGPANE_CLICK menu;
	menu.pPane = GetSelectedPane();
	menu.pContainer = this;
	ClientToScreen(&point);
	menu.pt = point;
	menu.rcExclude.SetRectEmpty();

	if (GetDockingPaneManager()->NotifyOwner(XTP_DPN_CONTEXTMENU, (LPARAM)&menu))
		return;
}