// XTThemeManager.cpp: implementation of the CXTThemeManager 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/XTPDrawHelpers.h"
#include "Common/XTPColorManager.h"

#include "XTThemeManager.h"

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

CProcessLocal<CXTThemeManager> CXTThemeManager::m_sThemeManager;


//////////////////////////////////////////////////////////////////////////
// CXTThemeManagerStyle

CXTThemeManagerStyle::CXTThemeManagerStyle()
: m_enumTheme(xtThemeOffice2000)
, m_pFactory(NULL)
{

}

CXTThemeManagerStyle::~CXTThemeManagerStyle()
{

}

void CXTThemeManagerStyle::RefreshMetrics()
{

}

BOOL CXTThemeManagerStyle::IsOfficeTheme()
{
	return m_enumTheme != xtThemeOffice2000;
}

BOOL CXTThemeManagerStyle::TakeSnapShot(CWnd* pWndOwner)
{
	CWnd *pWndParent = pWndOwner->GetParent();
	if (::IsWindow(pWndParent->GetSafeHwnd()))
	{
		if (m_bmpSnapShot.GetSafeHandle() != NULL)
			m_bmpSnapShot.DeleteObject();

		//convert our coordinates to our parent coordinates.
		CXTPWindowRect rc(pWndOwner);
		pWndParent->ScreenToClient(&rc);

		//copy what's on the parents background at this point
		CDC *pDC = pWndParent->GetDC();

		CDC memDC;
		memDC.CreateCompatibleDC(pDC);
		m_bmpSnapShot.CreateCompatibleBitmap(pDC, rc.Width(), rc.Height());

		CXTPBitmapDC bitmapDC(&memDC, &m_bmpSnapShot);
		memDC.BitBlt(0, 0, rc.Width(), rc.Height(), pDC, rc.left, rc.top, SRCCOPY);

		pWndParent->ReleaseDC(pDC);

		return TRUE;
	}

	return FALSE;
}

BOOL CXTThemeManagerStyle::DrawTransparentBack(CDC* pDC, CWnd* pWndOwner)
{
	if (::GetWindowLong(pWndOwner->GetSafeHwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT)
	{
		// Get background.
		if (!TakeSnapShot(pWndOwner))
			return FALSE;

		CXTPClientRect rc(pWndOwner);

		CDC memDC;
		memDC.CreateCompatibleDC(pDC);

		CXTPBitmapDC bitmapDC(&memDC, &m_bmpSnapShot);
		pDC->BitBlt(0, 0, rc.Width(), rc.Height(), &memDC, 0, 0, SRCCOPY);

		return TRUE;
	}

	return FALSE;
}

//////////////////////////////////////////////////////////////////////////
// CXTThemeManagerStyleFactory

IMPLEMENT_DYNCREATE(CXTThemeManagerStyleFactory, CObject)

CXTThemeManagerStyleFactory::CXTThemeManagerStyleFactory()
: m_pTheme(NULL)
, m_pNextFactory(NULL)
{
	XTThemeManager()->m_factoryList.AddHead(this);
	m_hostList.Construct(offsetof(CXTThemeManagerStyleHost, m_pNextHost));
}

CXTThemeManagerStyleFactory::~CXTThemeManagerStyleFactory()
{
	SAFE_DELETE(m_pTheme);
	XTThemeManager()->m_factoryList.Remove(this);
	m_hostList.RemoveAll();
}

CXTThemeManagerStyle* CXTThemeManagerStyleFactory::CreateTheme(XTThemeStyle /*theme*/)
{
	ASSERT(FALSE);
	return NULL;
}

void CXTThemeManagerStyleFactory::SetTheme(CXTThemeManagerStyle* pTheme)
{
	SAFE_DELETE(m_pTheme);

	if (pTheme)
	{
		m_pTheme = pTheme;
		m_pTheme->m_pFactory = this;
		m_pTheme->RefreshMetrics();

		for (CXTThemeManagerStyleHost* pHost = m_hostList.GetHead();
			pHost != NULL; pHost = pHost->m_pNextHost)
		{
			pHost->OnThemeChanged();
		}
	}
}

void CXTThemeManagerStyleFactory::SetTheme(XTThemeStyle theme)
{
	SetTheme(CreateTheme(theme));
}

CXTThemeManagerStyle* CXTThemeManagerStyleFactory::GetSafeTheme()
{
	if (m_pTheme == NULL)
	{
		m_pTheme = CreateTheme(XTThemeManager()->m_enumTheme);
	}

	if (m_pTheme)
	{
		m_pTheme->m_pFactory = this;
		m_pTheme->RefreshMetrics();
	}

	return m_pTheme;
}

void CXTThemeManagerStyleFactory::RefreshMetrics()
{
	if (m_pTheme)
	{
		m_pTheme->RefreshMetrics();
	}
}

//////////////////////////////////////////////////////////////////////////
// CXTThemeManagerStyleHost

CXTThemeManagerStyleHost::CXTThemeManagerStyleHost(CRuntimeClass* pThemeFactoryClass)
{
	m_bAutoDeleteCustomFactory = FALSE;

	m_pDefaultFactory = NULL;
	m_pCustomFactory = NULL;
	m_pNextHost = NULL;
	m_pThemeFactoryClass = NULL;

	if (pThemeFactoryClass)
	{
		InitStyleHost(pThemeFactoryClass);
	}
}

void CXTThemeManagerStyleHost::InitStyleHost(CRuntimeClass* pThemeFactoryClass)
{
	ASSERT(pThemeFactoryClass);
	if (!pThemeFactoryClass)
		return;
	ASSERT(pThemeFactoryClass->IsDerivedFrom(RUNTIME_CLASS(CXTThemeManagerStyleFactory)));

	if (m_pDefaultFactory)
	{
		m_pDefaultFactory->m_hostList.Remove(this);
	}

	m_pThemeFactoryClass = pThemeFactoryClass;

	m_pDefaultFactory = XTThemeManager()->GetDefaultThemeFactory(pThemeFactoryClass);
	m_pDefaultFactory->m_hostList.AddHead(this);
}

CXTThemeManagerStyleHost::~CXTThemeManagerStyleHost()
{
	GetSafeThemeFactory()->m_hostList.Remove(this);

	if (m_bAutoDeleteCustomFactory)
	{
		SAFE_DELETE(m_pCustomFactory);
	}
}

CXTThemeManagerStyleFactory* CXTThemeManagerStyleHost::CreateThemeFactory()
{
	return (CXTThemeManagerStyleFactory*)m_pThemeFactoryClass->CreateObject();
}

CXTThemeManagerStyleFactory* CXTThemeManagerStyleHost::GetSafeThemeFactory() const
{
	if (m_pCustomFactory != NULL)
		return m_pCustomFactory;

	return m_pDefaultFactory;
}


CXTThemeManagerStyle* CXTThemeManagerStyleHost::GetSafeTheme() const
{
	CXTThemeManagerStyleFactory* pFactory = GetSafeThemeFactory();
	ASSERT(pFactory);
	if (!pFactory)
		return NULL;

	return pFactory->GetSafeTheme();
}

void CXTThemeManagerStyleHost::SetTheme (XTThemeStyle theme)
{
	SetTheme(m_pDefaultFactory->CreateTheme(theme));
}

void CXTThemeManagerStyleHost::SetTheme (CXTThemeManagerStyle* pTheme)
{
	if (m_pCustomFactory == NULL && pTheme == NULL)
		return;

	GetSafeThemeFactory()->m_hostList.Remove(this);

	if (m_bAutoDeleteCustomFactory)
	{
		SAFE_DELETE(m_pCustomFactory);
	}
	if (pTheme != NULL)
	{
		m_pCustomFactory = CreateThemeFactory();
		m_pCustomFactory->SetTheme(pTheme);
		m_bAutoDeleteCustomFactory = TRUE;
	}
	else
	{
		m_pCustomFactory = NULL;
		m_bAutoDeleteCustomFactory = FALSE;
	}

	OnThemeChanged();
	GetSafeThemeFactory()->m_hostList.AddHead(this);
}

void CXTThemeManagerStyleHost::AttachThemeFactory(CXTThemeManagerStyleFactory* pFactory)
{
	GetSafeThemeFactory()->m_hostList.Remove(this);

	if (m_bAutoDeleteCustomFactory)
	{
		SAFE_DELETE(m_pCustomFactory);
	}
	m_pCustomFactory = pFactory;
	m_bAutoDeleteCustomFactory = FALSE;

	OnThemeChanged();
	GetSafeThemeFactory()->m_hostList.AddHead(this);
}

void CXTThemeManagerStyleHost::OnThemeChanged()
{
	// Can be overridden by subclass
}


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CXTThemeManager::CXTThemeManager()
{
	m_factoryList.Construct(offsetof(CXTThemeManagerStyleFactory, m_pNextFactory));
	m_enumTheme = xtThemeOffice2000;
}

CXTThemeManager::~CXTThemeManager()
{
	POSITION pos = m_mapDefaultFactories.GetStartPosition();
	void* pThemeFactoryClass;
	CXTThemeManagerStyleFactory* pFactory = 0;
	while (pos)
	{
		m_mapDefaultFactories.GetNextAssoc(pos, pThemeFactoryClass, (void *&)pFactory);
		SAFE_DELETE(pFactory);
	}

	ASSERT(m_factoryList.m_pHead == 0);
	m_factoryList.RemoveAll();
}

CXTThemeManagerStyleFactory* CXTThemeManager::GetDefaultThemeFactory(CRuntimeClass* pThemeFactoryClass)
{
	ASSERT(pThemeFactoryClass);
	if (!pThemeFactoryClass)
		return 0;
	ASSERT(pThemeFactoryClass->IsDerivedFrom(RUNTIME_CLASS(CXTThemeManagerStyleFactory)));

	CXTThemeManagerStyleFactory* pFactory = 0;

	if (!m_mapDefaultFactories.Lookup(pThemeFactoryClass, (void *&)pFactory))
	{
		pFactory = (CXTThemeManagerStyleFactory*)pThemeFactoryClass->CreateObject();
		m_mapDefaultFactories.SetAt(pThemeFactoryClass, pFactory);
	}

	return pFactory;
}

void CXTThemeManager::SetTheme(XTThemeStyle eTheme)
{
	if (m_enumTheme == eTheme)
		return;

	m_enumTheme = eTheme;

	RefreshXtremeColors();

	POSITION pos = m_mapDefaultFactories.GetStartPosition();
	void* pThemeFactoryClass;
	CXTThemeManagerStyleFactory* pFactory = 0;
	while (pos)
	{
		m_mapDefaultFactories.GetNextAssoc(pos, pThemeFactoryClass, (void *&)pFactory);

		pFactory->SetTheme(eTheme);
	}
}

void CXTThemeManager::RefreshMetrics()
{
	RefreshXtremeColors();

	for (CXTThemeManagerStyleFactory* pFactory = m_factoryList.GetHead();
			pFactory != NULL; pFactory = pFactory->m_pNextFactory)
	{
		pFactory->RefreshMetrics();
	}
}

_XTP_EXT_CLASS CXTThemeManager* AFX_CDECL XTThemeManager()
{
	return CXTThemeManager::m_sThemeManager.GetData();
}