// XTPCoreTreeItem.cpp: implementation of the CXTPCoreTreeItem class.
//
// This file is a part of the XTREME CONTROLS 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/XTPImageManager.h"
#include "Common/XTPMarkupRender.h"

#include "XTPCoreTreeItem.h"
#include "XTPCoreTreeControl.h"
#include "XTPCoreTreePaintManager.h"

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

IMPLEMENT_SERIAL(CXTPCoreTreeItem, CObject, VERSIONABLE_SCHEMA | _XTP_SCHEMA_CURRENT);

//////////////////////////////////////////////////////////////////////
// CXTPCoreTreeItem

CXTPCoreTreeItem::CXTPCoreTreeItem()
{
	m_pChildren = NULL;

	m_pNextItem = NULL;
	m_pPrevItem = NULL;
	m_pParentItems = NULL;

	m_pControl = NULL;

	m_rcItem.SetRectEmpty();

	m_bLabel = FALSE;
	m_bHasCheckBox = FALSE;
	m_bExpanded = FALSE;
	m_bEnabled = TRUE;

	m_nChecked = 0;
	m_nIconId = -1;
	m_dwData = 0;
	m_dwRef = 1;

	m_pMarkupUIElement = NULL;
}

CXTPCoreTreeItem::~CXTPCoreTreeItem()
{
	SAFE_DELETE(m_pChildren);
	XTPMarkupReleaseElement(m_pMarkupUIElement);
}


void CXTPCoreTreeItem::DoPropExchange(CXTPPropExchange* pPX)
{
	PX_String(pPX, _T("Caption"), m_strCaption, _T(""));
	PX_Int(pPX, _T("IconIndex"), m_nIconId, -1);
	PX_Bool(pPX, _T("Enabled"), m_bEnabled, TRUE);
	PX_Bool(pPX, _T("IsLabel"), m_bLabel, FALSE);
	PX_Bool(pPX, _T("HasCheckBox"), m_bHasCheckBox, FALSE);
	PX_Int(pPX, _T("Checked"), m_nChecked, 0);

	ULONGLONG dwData = m_dwData;
	PX_UI8(pPX, _T("Data"), dwData, 0);
	if (pPX->IsLoading())
	{
		m_dwData = (DWORD_PTR)dwData;
	}

}

DWORD CXTPCoreTreeItem::AddRef()
{
	return InterlockedIncrement(&m_dwRef);
}

DWORD CXTPCoreTreeItem::Release()
{
	if (m_dwRef == 0)
		return 0;

	LONG lResult = InterlockedDecrement(&m_dwRef);
	if (lResult == 0)
	{
		delete this;
	}
	return lResult;
}

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

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

		XTPMarkupReleaseElement(m_pMarkupUIElement);

		if (m_pControl && m_pControl->GetMarkupContext())
		{
			m_pMarkupUIElement = XTPMarkupParseText(m_pControl->GetMarkupContext(), lpszCaption);

		}
		Invalidate();
	}
}

CSize CXTPCoreTreeItem::CalcSize(CDC* /*pDC*/, int /*nWidth*/) const
{
	int nHeight = m_pControl->GetPaintManager()->GetItemHeight();

	return CSize(0, nHeight);
}

void CXTPCoreTreeItem::SetRect(CRect rc)
{
	m_rcItem = rc;
}

BOOL CXTPCoreTreeItem::HasChildren() const
{
	return m_pChildren && m_pChildren->GetCount() != 0;
}

CXTPCoreTreeItems* CXTPCoreTreeItem::GetChildren() const
{
	if (!m_pChildren)
	{
		m_pChildren = new CXTPCoreTreeItems();

		ASSERT(m_pControl);
		m_pChildren->m_pControl = m_pControl;
		m_pChildren->m_pOwnerItem = (CXTPCoreTreeItem*)this;
	}

	return m_pChildren;
}

void CXTPCoreTreeItem::Draw(CDC* pDC)
{
	m_pControl->GetPaintManager()->DrawItem(pDC, this);
}

void CXTPCoreTreeItem::SetExpanded(BOOL bExpanded)
{
	if (m_bExpanded != bExpanded)
	{
		m_bExpanded = bExpanded;

		if (m_pControl && HasChildren())
			m_pControl->RecalcLayout();
	}
}

void CXTPCoreTreeItem::SetEnabled(BOOL bEnabled)
{
	m_bEnabled = bEnabled;
	Invalidate();
}

void CXTPCoreTreeItem::Invalidate()
{
	if (m_rcItem.IsRectEmpty())
		return;

	if (m_pControl)
	{
		CRect rcItem(m_rcItem);
		rcItem.OffsetRect(0, -m_pControl->GetScrollOffset());

		m_pControl->RedrawControl(rcItem);
	}
}

BOOL CXTPCoreTreeItem::IsExpanded() const
{
	return m_bExpanded;
}

BOOL CXTPCoreTreeItem::IsSelected() const
{
	return m_pControl->GetFocusedItem() == this;
}

CXTPCoreTreeItem* CXTPCoreTreeItem::GetNextSiblingItem() const
{
	return m_pNextItem;
}

CXTPCoreTreeItem* CXTPCoreTreeItem::GetPrevSiblingItem() const
{
	return m_pPrevItem;
}

CXTPCoreTreeItem* CXTPCoreTreeItem::GetParentItem() const
{
	return m_pParentItems->m_pOwnerItem;
}

CXTPCoreTreeItem* CXTPCoreTreeItem::GetFirstChildItem() const
{
	if (m_pChildren)
	{
		return m_pChildren->GetFirstItem();
	}

	return NULL;
}

CRect CXTPCoreTreeItem::GetRect() const
{
	return m_rcItem;
}

CRect CXTPCoreTreeItem::GetScreenRect() const
{
	CRect rcItem = m_rcItem;

	if (m_pControl) rcItem.OffsetRect(0, -m_pControl->GetScrollOffset());

	return rcItem;
}

CXTPImageManagerIcon* CXTPCoreTreeItem::GetImage() const
{
	if (m_nIconId == -1)
		return NULL;

	return m_pControl->GetImageManager()->GetImage(m_nIconId);
}

CRect CXTPCoreTreeItem::GetButtonRect() const
{
	CXTPCoreTreePaintManager* pPaintManager = m_pControl->GetPaintManager();

	if (!pPaintManager->m_bShowButtons)
	{
		return CRect(0, 0, 0, 0);
	}

	CRect rc(m_rcItem);
	rc.right = rc.left + pPaintManager->GetItemHeight();

	return rc;
}

CRect CXTPCoreTreeItem::GetCheckboxRect() const
{
	if (!IsCheckboxVisible())
		return CRect(0, 0, 0, 0);

	CXTPCoreTreePaintManager* pPaintManager = m_pControl->GetPaintManager();

	int nHeight = pPaintManager->GetItemHeight();

	CRect rc(m_rcItem);

	if (pPaintManager->m_bShowButtons)
	{
		rc.left += nHeight;
	}
	rc.right = rc.left + nHeight;

	return rc;
}

BOOL CXTPCoreTreeItem::IsParent(CXTPCoreTreeItem* pItem) const
{
	while (pItem != NULL)
	{
		if (pItem == (CXTPCoreTreeItem*)this)
			return TRUE;

		pItem = pItem->GetParentItem();
	}

	return FALSE;
}

void CXTPCoreTreeItem::Remove()
{
	m_pParentItems->RemoveItem(this);

}


BOOL CXTPCoreTreeItem::PrepareDrag (COleDataSource& srcItem)
{
	HGLOBAL hGlobal = CacheGlobalData();

	if (!hGlobal)
		return FALSE;

	srcItem.CacheGlobalData (CXTPCoreTreeControl::GetClipboardFormat(), hGlobal);

	return TRUE;
}

HGLOBAL CXTPCoreTreeItem::CacheGlobalData()
{
	try
	{
		CSharedFile sharedFile;
		CArchive ar (&sharedFile, CArchive::store);

		CRuntimeClass* pClass = GetRuntimeClass ();
		ASSERT (pClass != NULL);

		ar.WriteClass (pClass);

		CXTPPropExchangeArchive px(ar);
		DoPropExchange(&px);

		ar.Close();

		return sharedFile.Detach();
	}
	catch (COleException* pEx)
	{
		TRACE(_T("CXTPTaskPanelItem::PrepareDrag. OLE exception: %x\r\n"),
			pEx->m_sc);
		pEx->Delete ();
	}
	catch (CArchiveException* pEx)
	{
		TRACE(_T("CXTPTaskPanelItem::PrepareDrag. Archive exception\r\n"));
		pEx->Delete ();
	}

	return NULL;
}

CXTPCoreTreeItem* CXTPCoreTreeItem::CreateFromOleFile(CFile* pFile)
{
	ASSERT (pFile != NULL);

	CXTPCoreTreeItem* pItem = NULL;

	try
	{
		CArchive ar (pFile, CArchive::load);

		CRuntimeClass* pClass = ar.ReadClass ();

		if (pClass != NULL)
		{
			pItem = (CXTPCoreTreeItem*) pClass->CreateObject();

			if (pItem != NULL)
			{
				CXTPPropExchangeArchive px(ar);
				pItem->DoPropExchange(&px);
			}
		}

		ar.Close ();
		return pItem;
	}
	catch (COleException* pEx)
	{
		TRACE(_T("CXTPTaskPanelItem::CreateFromOleData. OLE exception: %x\r\n"),
			pEx->m_sc);
		pEx->Delete ();
	}
	catch (CArchiveException* pEx)
	{
		TRACE(_T("CXTPTaskPanelItem::CreateFromOleData. Archive exception\r\n"));
		pEx->Delete ();
	}
	catch (CNotSupportedException *pEx)
	{
		TRACE(_T("CXTPTaskPanelItem::CreateFromOleData. \"Not Supported\" exception\r\n"));
		pEx->Delete ();
	}

	if (pItem != NULL)
	{
		delete pItem;
	}

	return NULL;
}



CXTPCoreTreeItem* CXTPCoreTreeItem::CreateFromOleData(COleDataObject* pDataObject)
{
	ASSERT (pDataObject != NULL);
	if (!pDataObject)
		return NULL;

	CFile* pFile = pDataObject->GetFileData(CXTPCoreTreeControl::GetClipboardFormat());
	if (!pFile)
		return NULL;

	CXTPCoreTreeItem* pItem = CreateFromOleFile(pFile);

	delete pFile;

	return pItem;

}

//////////////////////////////////////////////////////////////////////////
// CXTPCoreTreeItems

CXTPCoreTreeItems::CXTPCoreTreeItems()
{
	m_pFirstItem = NULL;
	m_pLastItem = NULL;
	m_nCount = 0;
	m_pControl = NULL;
	m_pOwnerItem = NULL;
}

CXTPCoreTreeItems::~CXTPCoreTreeItems()
{
	m_pControl = NULL;
	RemoveAll();
}

void CXTPCoreTreeItems::RemoveAll()
{
	for (CXTPCoreTreeItem* pItem = m_pFirstItem; pItem != NULL;)
	{
		CXTPCoreTreeItem* pNextItem = pItem->m_pNextItem;

		if (m_pControl)
		{
			m_pControl->OnItemRemoved(pItem);
		}

		pItem->m_pControl = NULL;
		pItem->m_pParentItems = NULL;

		SAFE_RELEASE(pItem);

		pItem = pNextItem;
	}

	m_pFirstItem = m_pLastItem = NULL;
	m_nCount = 0;

	if (m_pControl)
	{
		m_pControl->RecalcLayout();
	}
}



void CXTPCoreTreeItems::RemoveItem(CXTPCoreTreeItem* pItem)
{
	if (!pItem || pItem->m_pParentItems != this)
		return;

	if (pItem->m_pPrevItem)
	{
		pItem->m_pPrevItem->m_pNextItem = pItem->m_pNextItem;

	}

	if (pItem->m_pNextItem)
	{
		pItem->m_pNextItem->m_pPrevItem = pItem->m_pPrevItem;
	}


	if (m_pLastItem == pItem)
	{
		m_pLastItem = pItem->m_pPrevItem;
	}

	if (m_pFirstItem == pItem)
	{
		m_pFirstItem = pItem->m_pNextItem;
	}

	m_nCount--;

	if (m_pControl)
	{
		m_pControl->OnItemRemoved(pItem);
	}

	pItem->m_pControl = NULL;
	pItem->m_pParentItems = NULL;

	SAFE_RELEASE(pItem);

	if (m_pControl)
	{
		m_pControl->RecalcLayout();
	}
}


CXTPCoreTreeItem* CXTPCoreTreeItems::AddItem(LPCTSTR lpszCaption)
{
	CXTPCoreTreeItem* pItem = new CXTPCoreTreeItem();

	AddItemAfter(pItem, m_pLastItem);

	pItem->SetCaption(lpszCaption);

	return pItem;
}

CXTPCoreTreeItem* CXTPCoreTreeItems::AddItemAfter(CXTPCoreTreeItem* pItem, CXTPCoreTreeItem* pPrevItem)
{
	if (!pItem)
		return NULL;

	ASSERT(pItem->m_pParentItems == NULL);

	if (pPrevItem == NULL)
		pPrevItem = m_pLastItem;

	CXTPCoreTreeItem* pNextItem = pPrevItem ? pPrevItem->m_pNextItem : NULL;

	if (pPrevItem)
	{
		pPrevItem->m_pNextItem = pItem;
	}

	if (pNextItem)
	{
		pNextItem->m_pPrevItem = pItem;
	}

	pItem->m_pPrevItem = pPrevItem;
	pItem->m_pNextItem = pNextItem;

	if (m_pLastItem == pPrevItem)
	{
		m_pLastItem = pItem;
	}

	if (m_pFirstItem == NULL)
	{
		m_pFirstItem = pItem;
	}

	ASSERT(m_pControl);
	pItem->m_pControl = m_pControl;
	pItem->m_pParentItems = this;
	m_nCount++;

	m_pControl->RecalcLayout();

	return pItem;
}



CXTPCoreTreeItem* CXTPCoreTreeItems::AddItemBefore(CXTPCoreTreeItem* pItem, CXTPCoreTreeItem* pNextItem)
{
	if (!pItem)
		return NULL;

	ASSERT(pItem->m_pParentItems == NULL);

	if (pNextItem == NULL)
		pNextItem = m_pFirstItem;

	CXTPCoreTreeItem* pPrevItem = pNextItem ? pNextItem->m_pPrevItem : NULL;

	if (pPrevItem)
	{
		pPrevItem->m_pNextItem = pItem;
	}

	if (pNextItem)
	{
		pNextItem->m_pPrevItem = pItem;
	}

	pItem->m_pPrevItem = pPrevItem;
	pItem->m_pNextItem = pNextItem;

	if (m_pLastItem == NULL)
	{
		m_pLastItem = pItem;
	}

	if (m_pFirstItem == pNextItem)
	{
		m_pFirstItem = pItem;
	}

	ASSERT(m_pControl);
	pItem->m_pControl = m_pControl;
	pItem->m_pParentItems = this;
	m_nCount++;

	m_pControl->RecalcLayout();

	return pItem;
}

void CXTPCoreTreeItems::MoveItemAfter(CXTPCoreTreeItem* pItem, CXTPCoreTreeItem* pPrevItem)
{
	ASSERT(pItem->GetParentItems());

	pItem->AddRef();
	pItem->Remove();

	AddItemAfter(pItem, pPrevItem);
}

void CXTPCoreTreeItems::MoveItemBefore(CXTPCoreTreeItem* pItem, CXTPCoreTreeItem* pPrevItem)
{
	ASSERT(pItem->GetParentItems());

	pItem->AddRef();
	pItem->Remove();

	AddItemBefore(pItem, pPrevItem);
}


POSITION CXTPCoreTreeItems::GetHeadPosition() const
{
	return (POSITION)m_pFirstItem;
}

CXTPCoreTreeItem* CXTPCoreTreeItems::GetNext(POSITION& rPosition) const
{
	CXTPCoreTreeItem* pItem = (CXTPCoreTreeItem*)rPosition;

	rPosition = (POSITION)pItem->m_pNextItem;

	return pItem;
}

CXTPCoreTreeItem* CXTPCoreTreeItems::GetNextItem(const CXTPCoreTreeItem* pItem) const
{
	if (!pItem)
		return NULL;

	return pItem->m_pNextItem;
}

CXTPCoreTreeItem* CXTPCoreTreeItems::GetPrevItem(const CXTPCoreTreeItem* pItem) const
{
	if (!pItem)
		return NULL;

	return pItem->m_pPrevItem;
}