// Aero.h
//
//  Aero sample support classes

#ifndef __AERO_H__
#define __AERO_H__

#pragma once

#if _WIN32_WINNT < 0x0600
	#error Aero.h requires _WIN32_WINNT >= 0x0600
#elif !defined(NTDDI_VERSION) || (NTDDI_VERSION < NTDDI_LONGHORN)
	#error Aero.h requires the Windows Vista SDK
#endif

#ifndef __ATLTHEME_H__
	#include "atltheme.h"
#endif

#include <dwmapi.h>
#pragma comment (lib, "dwmapi.lib")


///////////////////////////////////////////////////////////////////////////////
// Classes in this file:
//
// CAeroImpl<T> - enables Aero translucency (when available) for any window
// CAeroDialogImpl<T, TBase> - dialog implementation of Aero translucency (when available)
// CAeroFrameImpl<T, TBase, TWinTraits> - frame implementation of Aero translucency (when available) 
// CAeroCtrlImpl - base implementation of Aero opacity for controls
// CAeroStatic - Aero opaque Static control
// CAeroButton - Aero opaque Button control
// CAeroEdit - Aero opaque Edit control

namespace WTL
{

template <class T>
class CAeroImpl :
	public CBufferedPaintImpl<T>,
	public CThemeImpl<T>
{
public:
	CAeroImpl(LPCWSTR lpstrThemeClassList = L"globals") 
	{
		m_PaintParams.dwFlags = BPPF_ERASE;

		SetThemeClassList(lpstrThemeClassList);
		MARGINS m = {0};
		m_Margins = m;
	}

	static bool IsAeroSupported() 
	{
		return IsBufferedPaintSupported();
	}

	bool IsCompositionEnabled() const
	{
		BOOL bEnabled = FALSE;
		return IsAeroSupported() ? SUCCEEDED(DwmIsCompositionEnabled(&bEnabled)) && bEnabled : false;
	}    
	
	bool IsTheming() const
	{
		return m_hTheme != 0;
	}    
	
	MARGINS m_Margins;

	bool SetMargins(MARGINS& m)
	{
		m_Margins = m;
		T* pT = static_cast<T*>(this);
		return pT->IsWindow() && IsAeroSupported() ? SUCCEEDED(DwmExtendFrameIntoClientArea(pT->m_hWnd, &m_Margins)) : true;
	}

// implementation
	void DoPaint(CDCHandle dc, RECT& rDest)
	{
		T* pT = static_cast<T*>(this);

		RECT rClient;
		pT->GetClientRect(&rClient);

		RECT rView = {rClient.left + m_Margins.cxLeftWidth, rClient.top + m_Margins.cyTopHeight, 
			rClient.right - m_Margins.cxRightWidth, rClient.bottom - m_Margins.cyBottomHeight};

		if (IsCompositionEnabled())
				dc.FillSolidRect(&rClient, RGB(0, 0, 0));
		else
			if (IsTheming())
				pT->DrawThemeBackground(dc, WP_FRAMEBOTTOM, pT->m_hWnd == GetFocus() ? 1 : 2, &rClient, &rDest);
			else
				dc.FillSolidRect(&rClient, GetSysColor(COLOR_MENUBAR));

		if (m_Margins.cxLeftWidth != -1)
			dc.FillSolidRect(&rView, GetSysColor(COLOR_WINDOW));
		else 
			::SetRectEmpty(&rView);

		pT->AeroDoPaint(dc, rClient, rView, rDest);
	}

	void AeroDrawText(HDC dc, LPCTSTR pStr, LPRECT prText, UINT uFormat, DTTOPTS &dto)
	{
        if(IsTheming())
			if (IsAeroSupported())
				DrawThemeTextEx (dc, TEXT_BODYTITLE, 0, pStr, -1, uFormat, prText, &dto );
			else
				DrawThemeText(dc, TEXT_BODYTITLE, 0, pStr, -1, uFormat, 0, prText);
		else
			CDCHandle(dc).DrawText(pStr, -1, prText, uFormat);
	}

	void AeroDrawText(HDC dc, LPCTSTR pStr, LPRECT prText, UINT uFormat, DWORD dwFlags, int iGlowSize)
	{
		DTTOPTS dto = { sizeof(DTTOPTS) };
		dto.dwFlags = dwFlags;
		dto.iGlowSize = iGlowSize;
		AeroDrawText(dc, pStr, prText, uFormat, dto);
	}

// overridable
	void AeroDoPaint(CDCHandle dc, RECT& rClient, RECT& rView, RECT& rDest)
	{}

	BEGIN_MSG_MAP(CAeroImpl)
		CHAIN_MSG_MAP(CThemeImpl<T>)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)
		MESSAGE_HANDLER(WM_ACTIVATE, OnActivate)
        MESSAGE_HANDLER(WM_DWMCOMPOSITIONCHANGED, OnCompositionChanged)
		MESSAGE_HANDLER(WM_PRINTCLIENT, OnPrintClient)
		CHAIN_MSG_MAP(CBufferedPaintImpl<T>)
	END_MSG_MAP()

	LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
	{
		if (IsThemingSupported())
			OpenThemeData();

		if (IsCompositionEnabled())
			DwmExtendFrameIntoClientArea(static_cast<T*>(this)->m_hWnd, &m_Margins);
		return bHandled = FALSE;
	}

	LRESULT OnActivate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
	{
		if (!IsCompositionEnabled() && IsTheming())
			static_cast<T*>(this)->Invalidate(FALSE);
		return bHandled = FALSE;
	}

	LRESULT OnCompositionChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		if (IsCompositionEnabled())
			SetMargins(m_Margins);
		return 0;
	}

	LRESULT OnPrintClient(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		T* pT = static_cast<T*>(this);
		return ::DefWindowProc(pT->m_hWnd, uMsg, wParam, lParam);
	}
};


///////////////////////////////////////////////////////////////////////////////
// CAeroDialogImpl - dialog implementation of Aero translucency (when available)

template <class T, class TBase  = CWindow>
class ATL_NO_VTABLE CAeroDialogImpl : public CDialogImpl<T, TBase>, public CAeroImpl<T>
{
public:
	CAeroDialogImpl(LPCWSTR lpstrThemeClassList = L"dialog") : CAeroImpl(lpstrThemeClassList)
	{}

	void AeroDoPaint(CDCHandle dc, RECT& rClient, RECT& rView, RECT& rDest)
	{
		if (!::IsRectEmpty(&rView))
		{
			if (IsTheming())
				DrawThemeBackground(dc, WP_DIALOG, 1, &rView, &rDest);
			else
				dc.FillSolidRect(&rView, GetSysColor(COLOR_BTNFACE));
		}
	}

	BEGIN_MSG_MAP(CAeroDialogImpl)
		MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
		CHAIN_MSG_MAP(CAeroImpl<T>)
	END_MSG_MAP()

	LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
	{
		if (IsThemingSupported())
		{
			OpenThemeData();
			EnableThemeDialogTexture(ETDT_ENABLE);
		}

		if (IsCompositionEnabled())
			DwmExtendFrameIntoClientArea(static_cast<T*>(this)->m_hWnd, &m_Margins);

		return bHandled = FALSE;
	}

};

///////////////////////////////////////////////////////////////////////////////
// CAeroFrameImpl - frame implementation of Aero translucency (when available)

template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CFrameWinTraits>
class ATL_NO_VTABLE CAeroFrameImpl : 
	public CFrameWindowImpl<T, TBase, TWinTraits>,
	public CAeroImpl<T>
{
	typedef CFrameWindowImpl<T, TBase, TWinTraits> _baseClass;
public:
	CAeroFrameImpl(LPCWSTR lpstrThemeClassList = L"window") : CAeroImpl(lpstrThemeClassList)
	{}

	void UpdateLayout(BOOL bResizeBars = TRUE)
	{
		RECT rect = { 0 };
		GetClientRect(&rect);

		// position margins
		if (m_Margins.cxLeftWidth != -1)
		{
			rect.left += m_Margins.cxLeftWidth;
			rect.top += m_Margins.cyTopHeight;
			rect.right -= m_Margins.cxRightWidth;
			rect.bottom -= m_Margins.cyBottomHeight;
		}

		// position bars and offset their dimensions
		UpdateBarsPosition(rect, bResizeBars);

		// resize client window
		if(m_hWndClient != NULL)
			::SetWindowPos(m_hWndClient, NULL, rect.left, rect.top,
				rect.right - rect.left, rect.bottom - rect.top,
				SWP_NOZORDER | SWP_NOACTIVATE);

		Invalidate(FALSE);
	}

	void UpdateBarsPosition(RECT& rect, BOOL bResizeBars = TRUE)
	{
		// resize toolbar
		if(m_hWndToolBar != NULL && ((DWORD)::GetWindowLong(m_hWndToolBar, GWL_STYLE) & WS_VISIBLE))
		{
			RECT rectTB = { 0 };
			::GetWindowRect(m_hWndToolBar, &rectTB);
			if(bResizeBars)
			{
				::SetWindowPos(m_hWndToolBar, NULL, rect.left, rect.top,
					rect.right - rect.left, rectTB.bottom - rectTB.top,
					SWP_NOZORDER | SWP_NOACTIVATE);
				::InvalidateRect(m_hWndToolBar, NULL, FALSE);
			}
			rect.top += rectTB.bottom - rectTB.top;
		}

		// resize status bar
		if(m_hWndStatusBar != NULL && ((DWORD)::GetWindowLong(m_hWndStatusBar, GWL_STYLE) & WS_VISIBLE))
		{
			RECT rectSB = { 0 };
			::GetWindowRect(m_hWndStatusBar, &rectSB);
			rect.bottom -= rectSB.bottom - rectSB.top;
			if(bResizeBars)
				::SetWindowPos(m_hWndStatusBar, NULL, rect.left, rect.bottom,
					rect.right - rect.left, rectSB.bottom - rectSB.top,
					SWP_NOZORDER | SWP_NOACTIVATE);
		}
	}

	BOOL CreateSimpleStatusBar(LPCTSTR lpstrText, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS , UINT nID = ATL_IDW_STATUS_BAR)
	{
		ATLASSERT(!::IsWindow(m_hWndStatusBar));
		m_hWndStatusBar = ::CreateStatusWindow(dwStyle | CCS_NOPARENTALIGN , lpstrText, m_hWnd, nID);
		return (m_hWndStatusBar != NULL);
	}

	BOOL CreateSimpleStatusBar(UINT nTextID = ATL_IDS_IDLEMESSAGE, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS , UINT nID = ATL_IDW_STATUS_BAR)
	{
		const int cchMax = 128;   // max text length is 127 for status bars (+1 for null)
		TCHAR szText[cchMax];
		szText[0] = 0;
		::LoadString(ModuleHelper::GetResourceInstance(), nTextID, szText, cchMax);
		return CreateSimpleStatusBar(szText, dwStyle, nID);
	}

	static HWND CreateSimpleReBarCtrl(HWND hWndParent, DWORD dwStyle = ATL_SIMPLE_REBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR)
	{
		return _baseClass::CreateSimpleReBarCtrl(hWndParent, dwStyle | CCS_NOPARENTALIGN, nID);
	}

	BOOL CreateSimpleReBar(DWORD dwStyle = ATL_SIMPLE_REBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR)
	{
		ATLASSERT(!::IsWindow(m_hWndToolBar));
		m_hWndToolBar = _baseClass::CreateSimpleReBarCtrl(m_hWnd, dwStyle | CCS_NOPARENTALIGN, nID);
		return (m_hWndToolBar != NULL);
	}

	BEGIN_MSG_MAP(CAeroFrameImpl)
		CHAIN_MSG_MAP(CAeroImpl<T>)
		CHAIN_MSG_MAP(_baseClass)
	END_MSG_MAP()
};

///////////////////////////////////////////////////////////////////////////////
// CAeroCtrlImpl - implementation of Aero opacity for controls

template <class TBase>
class CAeroCtrlImpl : public CBufferedPaintWindowImpl<CAeroCtrlImpl<TBase>, TBase>
{
public:
	DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())

	void DoBufferedPaint(CDCHandle dc, RECT& rect)
	{
		HDC hDCPaint = NULL;
		if(IsBufferedPaintSupported())
			m_BufferedPaint.Begin(dc, &rect, m_dwFormat, &m_PaintParams, &hDCPaint);

		if(hDCPaint != NULL)
			DoPaint(hDCPaint, rect);
		else
			DoPaint(dc.m_hDC, rect);

		if(IsBufferedPaintSupported() && !m_BufferedPaint.IsNull())
		{
			m_BufferedPaint.MakeOpaque(&rect);
			m_BufferedPaint.End();
		}
	}

	void DoPaint(CDCHandle dc, RECT& /*rect*/)
	{
		DefWindowProc(WM_PRINTCLIENT, (WPARAM)dc.m_hDC, PRF_CLIENT);
	}
};

///////////////////////////////////////////////////////////////////////////////
// CAeroStatic - Aero opaque Static control
// CAeroButton - Aero opaque Button control

typedef CAeroCtrlImpl<CStatic> CAeroStatic;
typedef CAeroCtrlImpl<CButton> CAeroButton;

///////////////////////////////////////////////////////////////////////////////
// CAeroEdit - Aero opaque Edit control
class CAeroEdit : public CAeroCtrlImpl<CEdit>
{
	BEGIN_MSG_MAP(CAeroEdit)
        REFLECTED_COMMAND_CODE_HANDLER(EN_CHANGE, OnChange)
		CHAIN_MSG_MAP(CAeroCtrlImpl<CEdit>)
	END_MSG_MAP()

	LRESULT OnChange(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& bHandled)
	{
        Invalidate(FALSE); 
		return bHandled = FALSE;
    } 
};

}; // namespace WTL

#endif // __AERO_H__
