// XTPDockingPaneLayout.cpp : implementation of the CXTPDockingPaneLayout 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 "Common/XTPPropExchange.h"
#include "Common/XTPColorManager.h"
#include "Common/XTPSystemHelpers.h"

#include "TabManager/XTPTabManager.h"

#include "XTPDockingPaneDefines.h"
#include "XTPDockingPaneBase.h"
#include "XTPDockingPaneBaseContainer.h"
#include "XTPDockingPaneLayout.h"
#include "XTPDockingPaneSplitterContainer.h"
#include "XTPDockingPaneTabbedContainer.h"
#include "XTPDockingPane.h"
#include "XTPDockingPaneMiniWnd.h"
#include "XTPDockingPaneManager.h"
#include "XTPDockingPaneAutoHidePanel.h"
#include "XTPDockingPaneSidePanel.h"

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

AFX_STATIC_DATA const TCHAR _xtRootSection[] = _T("DockingPaneLayouts");
AFX_STATIC_DATA const TCHAR _xtPaneSection[] = _T("Pane-%d");
AFX_STATIC_DATA const TCHAR _xtPanes[] = _T("Panes");
AFX_STATIC_DATA const TCHAR _xtSummary[] = _T("Summary");

CXTPDockingPaneLayout::CXTPDockingPaneLayout(CXTPDockingPaneManager* pManager)
	: m_pManager(pManager)
{
	m_bUserLayout = TRUE;

	memset(m_wndPanels, 0, sizeof(m_wndPanels));
	memset(m_wndMargins, 0, sizeof(m_wndMargins));

	m_pClient = m_pManager->OnCreatePane(xtpPaneTypeClient, this);
	m_pTopContainer = (CXTPDockingPaneSplitterContainer*)m_pManager->OnCreatePane(xtpPaneTypeSplitterContainer, this);
	m_pTopContainer->Init(m_pClient, TRUE, m_pManager->GetSite());

}

CXTPDockingPaneLayout::~CXTPDockingPaneLayout()
{
	Free();
}

BOOL CXTPDockingPaneLayout::IsValid() const
{
	return (m_pTopContainer != NULL) && (m_pClient != NULL)
		&& (m_pClient->GetType() == xtpPaneTypeClient) && (m_pManager != NULL);
}

void CXTPDockingPaneLayout::Free()
{
	m_pTopContainer = 0;
	m_pClient = 0;

	while (!m_lstStack.IsEmpty())
	{
		CXTPDockingPaneBase* pPane = m_lstStack.RemoveTail();
		pPane->DeletePane();
	}

	m_lstPanes.RemoveAll();

	memset(m_wndPanels, 0, sizeof(m_wndPanels));

	for (int i = 0; i < 4; i++)
	{
		SAFE_DELETE(m_wndMargins[i]);
	}
}

BOOL CXTPDockingPaneLayout::_Load(CXTPPropExchange* pPX)
{
	if (!pPX->OnBeforeExchange())
		return FALSE;

	ASSERT(pPX->IsLoading());

	Free();

	CXTPPropExchangeSection secSummary(pPX->GetSection(_xtSummary));

	int nCount = 0;
	PX_Int(&secSummary, _xtPanes, nCount);

	if (nCount < 2)
		return FALSE;

	pPX->ExchangeSchema();


	TCHAR szSection[256];
	CXTPPaneIndexToPaneMap map;

	for (int i = 1; i <= nCount; i++)
	{
		wsprintf(szSection, _xtPaneSection, i);

		CXTPPropExchangeSection secPane(pPX->GetSection(szSection));

		int nType = -1;
		PX_Int(&secPane, _T("Type"), nType, -1);

		CXTPDockingPaneBase* pPane = m_pManager->OnCreatePane((XTPDockingPaneType)nType, this);

		if (!pPane)
		{
			return FALSE;
		}
		pPane->m_nIndex = i;
		map.SetAt(i, pPane);
	}

	ASSERT(map.GetCount() == m_lstStack.GetCount());

	POSITION pos = m_lstStack.GetHeadPosition();
	while (pos)
	{
		CXTPDockingPaneBase* pPane = m_lstStack.GetNext(pos);

		wsprintf(szSection, _xtPaneSection, pPane->m_nIndex);

		CXTPPropExchangeSection secPane(pPX->GetSection(szSection));
		secPane->m_dwData = (DWORD_PTR)&map;

		if (!pPane->DoPropExchange(&secPane))
		{
			return FALSE;
		}
	}

	int nIndex = 0;
	if (!PX_Int(&secSummary, _T("TopContainer"), nIndex))
		return FALSE;

	m_pTopContainer = (CXTPDockingPaneSplitterContainer*)map[nIndex];
	if (!m_pTopContainer)
		return FALSE;

	if (!PX_Int(&secSummary, _T("Client"), nIndex))
		return FALSE;

	m_pClient = map[nIndex];
	if (!m_pClient || m_pClient->GetType() != xtpPaneTypeClient)
		return FALSE;

	return TRUE;

}

// The main difference between the .Net versions and the normal versions of _Save is that in the .Net
// versions all the save is done is done in the exact same order of the load.

void CXTPDockingPaneLayout::_Save(CXTPPropExchange* pPX)
{
	ASSERT(!pPX->IsLoading());

	pPX->EmptySection();

	_FreeEmptyPanes();

	CXTPPropExchangeSection secSummary(pPX->GetSection(_xtSummary));

	int nCount = (int)m_lstStack.GetCount();
	PX_Int(&secSummary, _xtPanes, nCount);

	pPX->ExchangeSchema();


	TCHAR szSection[256];

	// Assign an unique value to each pane
	int nIndex = 1;  // important

	POSITION pos = m_lstPanes.GetHeadPosition();
	while (pos)
	{
		CXTPDockingPane* pPane = m_lstPanes.GetNext(pos);
		pPane->m_nIndex = nIndex++;

		wsprintf(szSection, _xtPaneSection, pPane->m_nIndex);

		CXTPPropExchangeSection secPane(pPX->GetSection(szSection));
		int nType = pPane->m_type;
		PX_Int(&secPane, _T("Type"), nType);
	}

	pos = m_lstStack.GetHeadPosition();
	while (pos)
	{
		CXTPDockingPaneBase* pPane = m_lstStack.GetNext(pos);
		if (pPane->GetType() == xtpPaneTypeDockingPane) continue;
		pPane->m_nIndex = nIndex++;

		wsprintf(szSection, _xtPaneSection, pPane->m_nIndex);

		CXTPPropExchangeSection secPane(pPX->GetSection(szSection));
		int nType = pPane->m_type;
		PX_Int(&secPane, _T("Type"), nType);
	}

	pos = m_lstPanes.GetHeadPosition();
	while (pos)
	{
		CXTPDockingPane* pPane = m_lstPanes.GetNext(pos);
		wsprintf(szSection, _xtPaneSection, pPane->m_nIndex);

		CXTPPropExchangeSection secPane(pPX->GetSection(szSection));
		pPane->DoPropExchange(&secPane);
	}

	// Save all panes
	pos = m_lstStack.GetHeadPosition();
	while (pos)
	{
		CXTPDockingPaneBase* pPane = m_lstStack.GetNext(pos);
		if (pPane->GetType() == xtpPaneTypeDockingPane) continue;
		wsprintf(szSection, _xtPaneSection, pPane->m_nIndex);

		CXTPPropExchangeSection secPane(pPX->GetSection(szSection));
		pPane->DoPropExchange(&secPane);
	}

	PX_Int(&secSummary, _T("TopContainer"), m_pTopContainer->m_nIndex);
	PX_Int(&secSummary, _T("Client"), m_pClient->m_nIndex);

}


BOOL CXTPDockingPaneLayout::DoPropExchange(CXTPPropExchange* pPX)
{
	if (pPX->IsLoading())
		return _Load(pPX);

	_Save(pPX);
	return TRUE;
}

BOOL CXTPDockingPaneLayout::Load(LPCTSTR strSection)
{
	CXTPPropExchangeSection pxRoot(TRUE, _xtRootSection);
	CXTPPropExchangeSection pxSection(pxRoot->GetSection(strSection));

	return DoPropExchange(&pxSection);
}

void CXTPDockingPaneLayout::Save(LPCTSTR strSection)
{
	CXTPPropExchangeSection pxRoot(FALSE, _xtRootSection);
	CXTPPropExchangeSection pxSection(pxRoot->GetSection(strSection));

	DoPropExchange(&pxSection);
}

void CXTPDockingPaneLayout::Serialize(CArchive& ar)
{
	CXTPPropExchangeArchive px(ar);
	DoPropExchange(&px);
}


#ifndef _XTP_EXCLUDE_XML

BOOL CXTPDockingPaneLayout::LoadFromNode(XTPXML::IXMLDOMNode* xmlNode, LPCTSTR strSection)
{
	if (xmlNode == 0)
		return FALSE;

	CXTPPropExchangeXMLNode px(TRUE, xmlNode, strSection);
	return DoPropExchange(&px);
}

BOOL CXTPDockingPaneLayout::LoadFromFile(LPCTSTR strFileName, LPCTSTR strSection)
{
	CXTPPropExchangeXMLNode px(TRUE, 0, _xtRootSection);
	if (!px.LoadFromFile(strFileName))
		return FALSE;

	CXTPPropExchangeSection pxSection(px.GetSection(strSection));
	if (!pxSection->OnBeforeExchange())
		return FALSE;

	return DoPropExchange(&pxSection);
}

void CXTPDockingPaneLayout::SaveToNode (XTPXML::IXMLDOMNode* xmlNode, LPCTSTR strSection)
{
	if (xmlNode == 0)
		return;

	CXTPPropExchangeXMLNode px(FALSE, xmlNode, strSection);
	DoPropExchange(&px);
}

void CXTPDockingPaneLayout::SaveToFile (LPCTSTR strFileName, LPCTSTR strSection)
{
	CXTPPropExchangeXMLNode px(FALSE, 0, _xtRootSection);

	CXTPPropExchangeSection pxSection(px.GetSection(strSection));
	DoPropExchange(&pxSection);

	px.SaveToFile(strFileName);
}

#endif



BOOL CXTPDockingPaneBase::DoPropExchange(CXTPPropExchange* pPX)
{
	PX_Long(pPX, _T("DockingCX"), m_szDocking.cx, 0);
	PX_Long(pPX, _T("DockingCY"), m_szDocking.cy, 0);

	return TRUE;
}

BOOL CXTPDockingPane::DoPropExchange(CXTPPropExchange* pPX)
{
	CXTPDockingPaneBase::DoPropExchange(pPX);

	if (pPX->IsStoring())
	{
		CString strTitle = m_strTitle + '\n' + m_strTabCaption + '\n' + m_strTitleToolTip;
		PX_String(pPX, _T("Title"), strTitle);
	}
	else
	{
		CString strTitle;
		PX_String(pPX, _T("Title"), strTitle);

		AfxExtractSubString(m_strTitle, strTitle, 0, '\n');
		AfxExtractSubString(m_strTabCaption, strTitle, 1, '\n');
		AfxExtractSubString(m_strTitleToolTip, strTitle, 2, '\n');
	}

	PX_Int(pPX, _T("ID"), m_nID, 0);

	PX_Int(pPX, _T("IconID"), m_nIconID, -1);
	PX_ULong(pPX, _T("Options"), m_dwOptions, 0);

	int nIndex = 0;
	CXTPPaneIndexToPaneMap* pMap = (CXTPPaneIndexToPaneMap*)pPX->m_dwData;

	if (!pPX->IsLoading())
	{
		XTP_DOCKINGPANE_INFO* pInfo = m_pLayout->FindPaneInfo(this);
		ASSERT(pInfo);
		if (pInfo)
		{

			nIndex = pInfo->pDockingHolder ? pInfo->pDockingHolder->m_nIndex : 0;
			PX_Int(pPX, _T("DockingHolder"), nIndex, 0);
			nIndex = pInfo->pFloatingHolder ? pInfo->pFloatingHolder->m_nIndex : 0;
			PX_Int(pPX, _T("FloatingHolder"), nIndex, 0);
			nIndex = pInfo->pLastHolder ? pInfo->pLastHolder->m_nIndex : 0;
			PX_Int(pPX, _T("LastHolder"), nIndex, 0);
		}
	}
	else
	{
		XTP_DOCKINGPANE_INFO info (this);

		PX_Int(pPX, _T("DockingHolder"), nIndex, 0);
		if (nIndex) info.pDockingHolder = (CXTPDockingPaneBase*)(*pMap)[nIndex];
		PX_Int(pPX, _T("FloatingHolder"), nIndex, 0);
		if (nIndex) info.pFloatingHolder = (CXTPDockingPaneBase*)(*pMap)[nIndex];
		PX_Int(pPX, _T("LastHolder"), nIndex, 0);
		if (nIndex) info.pLastHolder = (CXTPDockingPaneBase*)(*pMap)[nIndex];
		m_pLayout->m_lstPanes.AddTail(info);

	}

	PX_Long(pPX, _T("MinTrackX"), m_ptMinTrackSize.x, 0);
	PX_Long(pPX, _T("MinTrackY"), m_ptMinTrackSize.y, 0);
	PX_Long(pPX, _T("MaxTrackX"), m_ptMaxTrackSize.x, 32000);
	PX_Long(pPX, _T("MaxTrackY"), m_ptMaxTrackSize.y, 32000);

	if (pPX->GetSchema() > _XTP_SCHEMA_97)
	{
		PX_DWord(pPX, _T("TabColor"), (DWORD&)m_clrItemTab, COLORREF_NULL);
		PX_Int(pPX, _T("HelpId"), m_nIDHelp, 0);

		DWORD dwData = (DWORD)m_dwData;
		PX_DWord(pPX, _T("Tag"), dwData, 0);
		if (pPX->IsLoading()) m_dwData = dwData;
	}

	return m_nID > 0;
}

BOOL CXTPDockingPaneSplitterContainer::DoPropExchange(CXTPPropExchange* pPX)
{
	CXTPDockingPaneBase::DoPropExchange(pPX);

	PX_Bool(pPX, _T("Horiz"), m_bHoriz, FALSE);

	int nCount = 0;

	if (!pPX->IsLoading())
	{
		nCount = (int)m_lstPanes.GetCount();
		PX_Int(pPX, _xtPanes, nCount);

		int nIndex = 1;
		TCHAR szPane[256];
		POSITION pos = m_lstPanes.GetHeadPosition();
		while (pos)
		{
			CXTPDockingPaneBase* pPane = m_lstPanes.GetNext(pos);
			wsprintf(szPane, _xtPaneSection, nIndex++);

			PX_Int(pPX, szPane, pPane->m_nIndex);
		}
	}
	else
	{
		PX_Int(pPX, _xtPanes, nCount, 0);

		int nIndex = 0;
		TCHAR szPane[256];
		CXTPPaneIndexToPaneMap* pMap = (CXTPPaneIndexToPaneMap*)pPX->m_dwData;

		for (int i = 1; i <= nCount; i++)
		{
			wsprintf(szPane, _xtPaneSection, i);
			PX_Int(pPX, szPane, nIndex, 0);
			ASSERT(nIndex > 0);

			CXTPDockingPaneBase* pPane = NULL;
			ASSERT(pMap->Lookup(nIndex, pPane));

			pPane = (CXTPDockingPaneBase*)((*pMap)[nIndex]);
			if (!pPane) return FALSE;

			_InsertPane(pPane);
		}

		ASSERT(nCount > 0);

	}
	return TRUE;
}

BOOL CXTPDockingPaneSidePanel::DoPropExchange(CXTPPropExchange* pPX)
{
	CXTPDockingPaneBase::DoPropExchange(pPX);
	int direction = m_direction;

	PX_Int(pPX, _T("Direction"), direction);
	PX_Rect(pPX, _T("WindowRect"), m_rcWindow, CRect(0, 0, 0, 0));
	PX_Bool(pPX, _T("Collapsed"), m_bCollapsed, FALSE);

	if (!pPX->IsLoading())
	{
		int nTopContainer = GetTopContainer() ? GetTopContainer()->m_nIndex : 0;
		PX_Int(pPX, _T("TopContaner"), nTopContainer);
	}
	else
	{
		m_direction = (XTPDockingPaneDirection)direction;

		int nTopContainer = 0;
		PX_Int(pPX, _T("TopContaner"), nTopContainer);

		if (nTopContainer == 0)
			return FALSE;

		CXTPPaneIndexToPaneMap* pMap = (CXTPPaneIndexToPaneMap*)pPX->m_dwData;

		CXTPDockingPaneBase* pTop = (CXTPDockingPaneBase*)(*pMap)[nTopContainer];
		if (!pTop || pTop->GetType() != xtpPaneTypeTabbedContainer)
			return FALSE;

		_InsertPane(pTop);

	}

	return TRUE;
}

BOOL CXTPDockingPaneAutoHidePanel::DoPropExchange(CXTPPropExchange* pPX)
{
	CXTPDockingPaneBase::DoPropExchange(pPX);

	int direction = m_direction;
	PX_Int(pPX, _T("Direction"), direction);

	int nCount = 0;

	if (!pPX->IsLoading())
	{
		nCount = (int)m_lstPanes.GetCount();
		PX_Int(pPX, _xtPanes, nCount);

		int nIndex = 1;
		TCHAR szPane[256];
		POSITION pos = m_lstPanes.GetHeadPosition();
		while (pos)
		{
			CXTPDockingPaneBase* pPane = m_lstPanes.GetNext(pos);
			wsprintf(szPane, _xtPaneSection, nIndex++);
			PX_Int(pPX, szPane, pPane->m_nIndex);
		}
	}
	else
	{
		m_direction = (XTPDockingPaneDirection)direction;

		PX_Int(pPX, _xtPanes, nCount, 0);

		int nIndex = 0;
		TCHAR szPane[256];
		CXTPPaneIndexToPaneMap* pMap = (CXTPPaneIndexToPaneMap*)pPX->m_dwData;

		for (int i = 1; i <= nCount; i++)
		{
			wsprintf(szPane, _xtPaneSection, i);
			PX_Int(pPX, szPane, nIndex, 0);
			ASSERT(nIndex > 0);

			CXTPDockingPaneBase* pPane = NULL;
			ASSERT(pMap->Lookup(nIndex, pPane));

			pPane = (CXTPDockingPaneBase*)((*pMap)[nIndex]);
			if (!pPane) return FALSE;

			_InsertPane(pPane);
		}

		m_pLayout->m_wndPanels[m_direction] = this;
	}

	return TRUE;
}

BOOL CXTPDockingPaneTabbedContainer::DoPropExchange(CXTPPropExchange* pPX)
{
	CXTPDockingPaneBase::DoPropExchange(pPX);

	int nCount, nSelected;

	if (!pPX->IsLoading())
	{
		nCount = (int)m_lstPanes.GetCount();
		PX_Int(pPX, _xtPanes, nCount);

		nSelected = m_pSelectedPane ? m_pSelectedPane->m_nIndex : 0;
		PX_Int(pPX, _T("Selected"), nSelected, 0);

		PX_Bool(pPX, _T("Maximized"), m_bMaximized, FALSE);

		POSITION pos = m_lstPanes.GetHeadPosition();
		int nIndex = 1;
		while (pos)
		{
			CXTPDockingPaneBase* pPane = m_lstPanes.GetNext(pos);

			TCHAR szPane[256];
			wsprintf(szPane, _xtPaneSection, nIndex++);

			PX_Int(pPX, szPane, pPane->m_nIndex);
		}

	}
	else
	{
		PX_Int(pPX, _xtPanes, nCount);
		PX_Int(pPX, _T("Selected"), nSelected, 0);

		if (pPX->GetSchema() > _XTP_SCHEMA_1122)
		{
			PX_Bool(pPX, _T("Maximized"), m_bMaximized, FALSE);
		}

		TCHAR szPane[256];
		CXTPDockingPane* pSelected = NULL;
		int nIndex = 0;
		CXTPPaneIndexToPaneMap* pMap = (CXTPPaneIndexToPaneMap*)pPX->m_dwData;

		for (int i = 1; i <= nCount; i++)
		{
			wsprintf(szPane, _xtPaneSection, i);
			PX_Int(pPX, szPane, nIndex, 0);
			ASSERT(nIndex > 0);

			CXTPDockingPaneBase* pPane = NULL;
			ASSERT(pMap->Lookup(nIndex, pPane));

			pPane = (CXTPDockingPaneBase*)((*pMap)[nIndex]);

			if (!pPane || pPane->GetType() != xtpPaneTypeDockingPane) return FALSE;

			_InsertPane((CXTPDockingPane*)pPane);

			if (nIndex == nSelected) pSelected = (CXTPDockingPane*)pPane;
		}

		if (pSelected)
		{
			SelectPane(pSelected);
		}
	}

	return TRUE;
}

BOOL CXTPDockingPaneMiniWnd::DoPropExchange(CXTPPropExchange* pPX)
{
	CXTPDockingPaneBase::DoPropExchange(pPX);

	PX_Long(pPX, _T("WindowRectTopPos"), m_rcWindow.top);
	PX_Long(pPX, _T("WindowRectBottomPos"), m_rcWindow.bottom);
	PX_Long(pPX, _T("WindowRectLeftPos"), m_rcWindow.left);
	PX_Long(pPX, _T("WindowRectRightPos"), m_rcWindow.right);
	PX_Bool(pPX, _T("Collapsed"), m_bCollapsed, FALSE);
	PX_Int(pPX, _T("ExpandedHeight"), m_nExpandedHeight, 0);

	if (!pPX->IsLoading())
	{
		int nTopContainer = m_pTopContainer ? m_pTopContainer->m_nIndex : 0;
		PX_Int(pPX, _T("TopContaner"), nTopContainer);
	}
	else
	{
		int nTopContainer = 0;
		PX_Int(pPX, _T("TopContaner"), nTopContainer);

		if (nTopContainer == 0)
			return FALSE;

		CXTPPaneIndexToPaneMap* pMap = (CXTPPaneIndexToPaneMap*)pPX->m_dwData;

		CXTPDockingPaneBase* pTop = (CXTPDockingPaneBase*)(*pMap)[nTopContainer];
		if (!pTop || pTop->GetType() != xtpPaneTypeSplitterContainer)
			return FALSE;

		m_pTopContainer = (CXTPDockingPaneSplitterContainer*)pTop;
		m_pTopContainer->m_pParentContainer = this;

	}

	return TRUE;
}

void CXTPDockingPaneLayout::Copy(const CXTPDockingPaneLayout* pLayout)
{
	Free();

	CXTPPaneToPaneMap map;

	// Clone them
	POSITION pos = pLayout->m_lstStack.GetHeadPosition();
	while (pos)
	{
		CXTPDockingPaneBase* pPane = pLayout->m_lstStack.GetNext(pos);
		if (pPane->m_pParentContainer == NULL) // hidden pane, top pane, miniwnd.
		{
			pPane->Clone(this, &map);
		}
	}

	ASSERT(map.GetCount() == pLayout->m_lstStack.GetCount());
	ASSERT(map.Lookup(pLayout->m_pClient, m_pClient));
	ASSERT(map.Lookup(pLayout->m_pTopContainer, (CXTPDockingPaneBase*&)m_pTopContainer));

	m_pClient = map[pLayout->m_pClient];
	m_pTopContainer = (CXTPDockingPaneSplitterContainer*)map[pLayout->m_pTopContainer];

	for (int i = 0; i < 4; i++)
		if (pLayout->m_wndPanels[i]) m_wndPanels[i] = (CXTPDockingPaneAutoHidePanel*)map[pLayout->m_wndPanels[i]];

	// Map Holders
	pos = pLayout->m_lstPanes.GetHeadPosition();
	while (pos)
	{
		XTP_DOCKINGPANE_INFO& info = pLayout->GetPaneList().GetNext(pos);
		XTP_DOCKINGPANE_INFO infoCopy;
		ASSERT(map.Lookup((CXTPDockingPaneBase*)info.pPane, (CXTPDockingPaneBase*&)infoCopy.pPane));

		infoCopy.pPane = (CXTPDockingPane*)map[(CXTPDockingPaneBase*)info.pPane];

		if (info.pDockingHolder) infoCopy.pDockingHolder = (CXTPDockingPaneBase*)map[info.pDockingHolder];
		if (info.pLastHolder) infoCopy.pLastHolder = (CXTPDockingPaneBase*)map[info.pLastHolder];
		if (info.pFloatingHolder) infoCopy.pFloatingHolder = (CXTPDockingPaneBase*)map[info.pFloatingHolder];

		m_lstPanes.AddTail(infoCopy);
	}
}

void CXTPDockingPaneLayout::_FreeEmptyPanes()
{
	WORD w;
	CMapPtrToWord map;

	POSITION pos = m_lstPanes.GetHeadPosition();
	while (pos)
	{
		XTP_DOCKINGPANE_INFO& info = m_lstPanes.GetNext(pos);
		map.SetAt(info.pDockingHolder, TRUE);
		map.SetAt(info.pFloatingHolder, TRUE);
		map.SetAt(info.pPane->m_pParentContainer, TRUE);
	}

	BOOL bFound = TRUE;

	while (bFound)
	{
		bFound = FALSE;

		POSITION posRemove = pos = m_lstStack.GetHeadPosition();
		while (pos)
		{
			CXTPDockingPaneBase* pPane = m_lstStack.GetNext(pos);
			CXTPDockingPaneBase* pParentContainer = pPane->m_pParentContainer;

			switch (pPane->GetType())
			{
			case xtpPaneTypeTabbedContainer:
				if (!map.Lookup(pPane, w))
				{

					SAFE_CALLPTR(pParentContainer, RemovePane(pPane));

					m_lstStack.RemoveAt(posRemove);
					pPane->DeletePane();
					bFound = TRUE;
				}
				break;

			case xtpPaneTypeSplitterContainer:
				if (((CXTPDockingPaneSplitterContainer*)pPane)->m_lstPanes.GetCount() == 0)
				{
					SAFE_CALLPTR(pParentContainer, RemovePane(pPane));

					m_lstStack.RemoveAt(posRemove);
					pPane->DeletePane();
					bFound = TRUE;
				}
				else if (pParentContainer && (pParentContainer->GetType() == xtpPaneTypeSplitterContainer)
					&& (((CXTPDockingPaneSplitterContainer*)pPane)->m_lstPanes.GetCount() == 1)
					&& (((CXTPDockingPaneSplitterContainer*)pParentContainer)->m_lstPanes.GetCount() == 1))
				{
					CXTPDockingPaneBase* pChildPane = ((CXTPDockingPaneSplitterContainer*)pPane)->m_lstPanes.RemoveHead();
					((CXTPDockingPaneSplitterContainer*)pParentContainer)->m_lstPanes.RemoveHead();
					((CXTPDockingPaneSplitterContainer*)pParentContainer)->m_lstPanes.AddHead(pChildPane);
					pChildPane->m_pParentContainer = pParentContainer;

					m_lstStack.RemoveAt(posRemove);
					pPane->DeletePane();
					bFound = TRUE;

				}
				break;

			case xtpPaneTypeMiniWnd:
				if (((CXTPDockingPaneMiniWnd*)pPane)->m_pTopContainer == NULL)
				{
					m_lstStack.RemoveAt(posRemove);
					pPane->DeletePane();
					bFound = TRUE;
				}
				break;

			case xtpPaneTypeSidePanel:
				if (((CXTPDockingPaneSidePanel*)pPane)->m_lstPanes.GetCount() == 0)
				{
					m_lstStack.RemoveAt(posRemove);
					pPane->DeletePane();
					bFound = TRUE;
				}
				break;
			}
			posRemove = pos;
		}
	}
}


XTP_DOCKINGPANE_INFO* CXTPDockingPaneLayout::FindPaneInfo(CXTPDockingPane* pPane)
{
	POSITION pos = m_lstPanes.GetHeadPosition();
	while (pos)
	{
		XTP_DOCKINGPANE_INFO& info = m_lstPanes.GetNext(pos);

		if (info.pPane == pPane)
			return &info;
	}
	return 0;
}

XTPDockingPaneDirection CXTPDockingPaneLayout::_GetPaneDirection(const CXTPDockingPaneBase* pPane) const
{
	if (pPane->GetType() == xtpPaneTypeMiniWnd)
		return (XTPDockingPaneDirection)4;

	if (!pPane->m_pParentContainer || pPane->GetType() == xtpPaneTypeClient)
		return (XTPDockingPaneDirection)-1;

	if (pPane->m_pParentContainer->GetType() == xtpPaneTypeAutoHidePanel)
	{
		return ((CXTPDockingPaneAutoHidePanel*)pPane->m_pParentContainer)->m_direction;
	}

	if (pPane->GetType() == xtpPaneTypeSidePanel)
	{
		return ((CXTPDockingPaneSidePanel*)pPane)->m_direction;
	}

	if (pPane->m_pParentContainer->GetType() == xtpPaneTypeSplitterContainer)
	{
		POSITION pos = pPane->m_pParentContainer->ContainPane(m_pClient);
		if (pos)
		{
			CXTPDockingPaneSplitterContainer* pSplitter = (CXTPDockingPaneSplitterContainer*)pPane->m_pParentContainer;

			if (pSplitter->_Before(pPane, pos))
			{
				return  pSplitter->IsHoriz() ? xtpPaneDockLeft : xtpPaneDockTop;

			}
			else
			{
				return  pSplitter->IsHoriz() ? xtpPaneDockRight : xtpPaneDockBottom;
			}
		}
	}
	return _GetPaneDirection(pPane->m_pParentContainer);
}

void CXTPDockingPaneLayout::_AddPanesTo(CXTPDockingPaneTabbedContainer* pContainer, CXTPDockingPaneBaseList& lst, DWORD dwIgnoredOptions)
{
	POSITION pos = lst.GetHeadPosition();
	while (pos)
	{
		CXTPDockingPane* pPane = (CXTPDockingPane*)lst.GetNext(pos);

		if ((((CXTPDockingPane*)pPane)->GetOptions() & dwIgnoredOptions) == 0)
		{
			SAFE_CALLPTR(pPane->m_pParentContainer, RemovePane(pPane));
			pContainer->_InsertPane(pPane);
		}
	}
}

BOOL CXTPDockingPaneLayout::_FindTabbedPaneToHide(CXTPDockingPaneAutoHidePanel* pPanel, CXTPDockingPaneBase* pPane)
{
	CXTPDockingPaneBaseList lst;
	pPane->FindPane(xtpPaneTypeDockingPane, &lst);

	if (lst.IsEmpty())
		return FALSE;

	CXTPDockingPane* pPanePrimary = (CXTPDockingPane*)lst.GetHead();
	XTP_DOCKINGPANE_INFO* pInfo = FindPaneInfo(pPanePrimary);
	if (!pInfo || pInfo->pDockingHolder == NULL)
		return FALSE;

	POSITION pos = pPanel->m_lstPanes.GetHeadPosition();
	while (pos)
	{
		CXTPDockingPaneTabbedContainer* pContainer = (CXTPDockingPaneTabbedContainer*)pPanel->m_lstPanes.GetNext(pos);
		CXTPDockingPane* pContainerPane = (CXTPDockingPane*)pContainer->GetFirstPane();
		if (pContainerPane)
		{
			XTP_DOCKINGPANE_INFO* pContanerPaneInfo = FindPaneInfo(pContainerPane);

			if (pContanerPaneInfo && pContanerPaneInfo->pDockingHolder == pInfo->pDockingHolder)
			{
				_AddPanesTo(pContainer, lst, xtpPaneNoHideable);
				return TRUE;
			}
		}
	}



	return FALSE;
}

void CXTPDockingPaneLayout::HidePane(CXTPDockingPaneBase* pPane)
{
	ASSERT(!m_bUserLayout);

	XTPDockingPaneDirection direction = _GetPaneDirection(pPane);
	if (direction < 0 || direction >= 4)
		direction = xtpPaneDockLeft;

	if (m_wndPanels[direction] == 0)
	{
		m_wndPanels[direction] = (CXTPDockingPaneAutoHidePanel*)m_pManager->
			OnCreatePane(xtpPaneTypeAutoHidePanel, this);
		m_wndPanels[direction]->m_direction = direction;
	}

	if (_FindTabbedPaneToHide(m_wndPanels[direction], pPane))
		return;

	if (pPane->GetType() == xtpPaneTypeDockingPane)
	{
		pPane->m_pParentContainer->RemovePane(pPane);

		CXTPDockingPaneTabbedContainer* pContainer = (CXTPDockingPaneTabbedContainer*)m_pManager->
			OnCreatePane(xtpPaneTypeTabbedContainer, this);
		pContainer->Init((CXTPDockingPane*)pPane, m_pManager->GetSite());

		pPane = pContainer;

	}
	else if (pPane->GetType() == xtpPaneTypeTabbedContainer)
	{
		pPane = ((CXTPDockingPaneBase*)pPane)->Clone(this, 0, xtpPaneNoHideable);
	}

	m_wndPanels[direction]->_InsertPane(pPane);
}

void CXTPDockingPaneLayout::RepositionMargins(CRect& rect, const CRect& rcClientMargins)
{
	int i;

	if (rcClientMargins.IsRectNull())
	{
		for (i = 0; i < 4; i++)
		{
			SAFE_DELETE(m_wndMargins[i]);
		}
		return;
	}

	for (i = 0; i < 4; i++)
	{
		if (m_wndMargins[i] == 0)
		{
			m_wndMargins[i] = new CXTPDockingPaneSplitterWnd();
			m_wndMargins[i]->Create(m_pManager, i == xtpPaneDockTop || i == xtpPaneDockBottom);
		}
	}

	CRect rcClient(rect);
	rcClient.DeflateRect(rcClientMargins);

	m_wndMargins[xtpPaneDockLeft]->MoveWindow(CRect(rect.left, rect.top, rcClient.left, rect.bottom));
	m_wndMargins[xtpPaneDockRight]->MoveWindow(CRect(rcClient.right , rect.top, rect.right, rect.bottom));
	m_wndMargins[xtpPaneDockTop]->MoveWindow(CRect(rcClient.left, rect.top, rcClient.right, rcClient.top));
	m_wndMargins[xtpPaneDockBottom]->MoveWindow(CRect(rcClient.left, rcClient.bottom, rcClient.right, rect.bottom));

	for (i = 0; i < 4; i++)
	{
		m_wndMargins[i]->Invalidate(FALSE);
	}

	rect = rcClient;
}


void CXTPDockingPaneLayout::OnSizeParent(CWnd* pParent, CRect rect, LPVOID lParam)
{
	AFX_SIZEPARENTPARAMS* lpLayout = (AFX_SIZEPARENTPARAMS*)lParam;

	for (int i = 0; i < 4; i++)
	{
		SAFE_CALLPTR(m_wndPanels[i], OnSizeParentEx(pParent, rect, lParam));
	}

	int nMargin = m_pManager->GetClientMargin();
	CRect rcClientMargins(nMargin, nMargin, nMargin, nMargin);

	if (lpLayout->hDWP != NULL)
	{
		RepositionMargins(rect, rcClientMargins);
	}
	else
	{
		rect.DeflateRect(rcClientMargins);
	}

	m_pClient->OnSizeParent(pParent, rect, lParam);
	m_pTopContainer->OnSizeParent(pParent, rect, lParam);

	if (lpLayout->hDWP)
	{
		CXTPDockingPaneSidePanel::OnSizeParentEx(m_pManager, pParent, lpLayout->rect);
	}
}

CXTPDockingPane* CXTPDockingPaneLayout::FindPane(int nID) const
{

	POSITION pos = m_lstPanes.GetHeadPosition();
	while (pos)
	{
		XTP_DOCKINGPANE_INFO info = m_lstPanes.GetNext(pos);
		CXTPDockingPane* pPane = info.pPane;

		if (pPane->GetID() == nID)
			return pPane;
	}

	return NULL;
}

void CXTPDockingPaneLayout::DestroyPane(CXTPDockingPane* pPane)
{
	if (pPane == NULL) return;

	SAFE_CALLPTR(pPane->m_pParentContainer, RemovePane(pPane));

	POSITION pos = m_lstPanes.GetHeadPosition();
	while (pos)
	{
		if (m_lstPanes.GetAt(pos) == pPane)
		{
			m_lstPanes.RemoveAt(pos);
			break;
		}
		m_lstPanes.GetNext(pos);
	}

	pos = m_lstStack.GetHeadPosition();
	while (pos)
	{
		if (m_lstStack.GetAt(pos) == pPane)
		{
			m_lstStack.RemoveAt(pos);
			break;
		}
		m_lstStack.GetNext(pos);
	}
	((CXTPDockingPane*)pPane)->InternalRelease();
	_FreeEmptyPanes();

}

void CXTPDockingPaneLayout::MoveToTail(CXTPDockingPaneBase* pPane)
{
	POSITION pos = m_lstStack.GetHeadPosition();
	while (pos)
	{
		if (m_lstStack.GetAt(pos) == pPane)
		{
			m_lstStack.RemoveAt(pos);
			m_lstStack.AddTail(pPane);
			return;
		}
		m_lstStack.GetNext(pos);
	}
}