// Windows Template Library - WTL version 10.0
// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.
//
// This file is a part of the Windows Template Library.
// The use and distribution terms for this software are covered by the
// Microsoft Public License (http://opensource.org/licenses/MS-PL)
// which can be found in the file MS-PL.txt at the root folder.

#ifndef __ATLWINX_H__
#define __ATLWINX_H__

#pragma once

#ifndef __ATLAPP_H__
	#error atlwinx.h requires atlapp.h to be included first
#endif

#include <atlwin.h>


///////////////////////////////////////////////////////////////////////////////
// Classes in this file:
//
// CWindowEx


/////////////////////////////////////////////////////////////////////////////
// Additional macros needed for template classes

#ifndef DECLARE_WND_CLASS_EX2
  #define DECLARE_WND_CLASS_EX2(WndClassName, EnclosingClass, style, bkgnd) \
  static ATL::CWndClassInfo& GetWndClassInfo() \
  { \
	static ATL::CWndClassInfo wc = \
	{ \
		{ sizeof(WNDCLASSEX), style, EnclosingClass::StartWindowProc, \
		  0, 0, NULL, NULL, NULL, (HBRUSH)(bkgnd + 1), NULL, WndClassName, NULL }, \
		  NULL, NULL, IDC_ARROW, TRUE, 0, _T("") \
	}; \
	return wc; \
  }
#endif // DECLARE_WND_CLASS_EX2

#ifndef DECLARE_WND_SUPERCLASS2
  #define DECLARE_WND_SUPERCLASS2(WndClassName, EnclosingClass, OrigWndClassName) \
  static ATL::CWndClassInfo& GetWndClassInfo() \
  { \
	static ATL::CWndClassInfo wc = \
	{ \
		{ sizeof(WNDCLASSEX), 0, EnclosingClass::StartWindowProc, \
		  0, 0, NULL, NULL, NULL, NULL, NULL, WndClassName, NULL }, \
		  OrigWndClassName, NULL, NULL, TRUE, 0, _T("") \
	}; \
	return wc; \
  }
#endif // DECLARE_WND_SUPERCLASS2


///////////////////////////////////////////////////////////////////////////////
// Command Chaining Macros

#define CHAIN_COMMANDS(theChainClass) \
	if(uMsg == WM_COMMAND) \
		CHAIN_MSG_MAP(theChainClass)

#define CHAIN_COMMANDS_ALT(theChainClass, msgMapID) \
	if(uMsg == WM_COMMAND) \
		CHAIN_MSG_MAP_ALT(theChainClass, msgMapID)

#define CHAIN_COMMANDS_MEMBER(theChainMember) \
	if(uMsg == WM_COMMAND) \
		CHAIN_MSG_MAP_MEMBER(theChainMember)

#define CHAIN_COMMANDS_ALT_MEMBER(theChainMember, msgMapID) \
	if(uMsg == WM_COMMAND) \
		CHAIN_MSG_MAP_ALT_MEMBER(theChainMember, msgMapID)


///////////////////////////////////////////////////////////////////////////////
// Macros for parent message map to selectively reflect control messages

// NOTE: ReflectNotifications is a member of ATL's CWindowImplRoot
//  (and overridden in 2 cases - CContainedWindowT and CAxHostWindow)
//  Since we can't modify ATL, we'll provide the needed additions
//  in a separate function (that is not a member of CWindowImplRoot)

namespace WTL
{

inline LRESULT WtlReflectNotificationsFiltered(HWND hWndParent, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled,
                                               UINT uMsgFilter = WM_NULL, UINT_PTR idFromFilter = 0, HWND hWndChildFilter = NULL)
{
	if((uMsgFilter != WM_NULL) && (uMsgFilter != uMsg))
	{
		// The notification message doesn't match the filter.
		bHandled = FALSE;
		return 1;
	}

	HWND hWndChild = NULL;
	UINT_PTR idFrom = 0;

	switch(uMsg)
	{
	case WM_COMMAND:
		if(lParam != NULL)	// not from a menu
		{
			hWndChild = (HWND)lParam;
			idFrom = (UINT_PTR)LOWORD(wParam);
		}
		break;
	case WM_NOTIFY:
		hWndChild = ((LPNMHDR)lParam)->hwndFrom;
		idFrom = ((LPNMHDR)lParam)->idFrom;
		break;
	case WM_PARENTNOTIFY:
		switch(LOWORD(wParam))
		{
		case WM_CREATE:
		case WM_DESTROY:
			hWndChild = (HWND)lParam;
			idFrom = (UINT_PTR)HIWORD(wParam);
			break;
		default:
			hWndChild = ::GetDlgItem(hWndParent, HIWORD(wParam));
			idFrom = (UINT_PTR)::GetDlgCtrlID(hWndChild);
			break;
		}
		break;
	case WM_DRAWITEM:
		if(wParam)	// not from a menu
		{
			hWndChild = ((LPDRAWITEMSTRUCT)lParam)->hwndItem;
			idFrom = (UINT_PTR)wParam;
		}
		break;
	case WM_MEASUREITEM:
		if(wParam)	// not from a menu
		{
			hWndChild = ::GetDlgItem(hWndParent, ((LPMEASUREITEMSTRUCT)lParam)->CtlID);
			idFrom = (UINT_PTR)wParam;
		}
		break;
	case WM_COMPAREITEM:
		if(wParam)	// not from a menu
		{
			hWndChild = ((LPCOMPAREITEMSTRUCT)lParam)->hwndItem;
			idFrom = (UINT_PTR)wParam;
		}
		break;
	case WM_DELETEITEM:
		if(wParam)	// not from a menu
		{
			hWndChild = ((LPDELETEITEMSTRUCT)lParam)->hwndItem;
			idFrom = (UINT_PTR)wParam;
		}
		break;
	case WM_VKEYTOITEM:
	case WM_CHARTOITEM:
	case WM_HSCROLL:
	case WM_VSCROLL:
	case WM_CTLCOLORBTN:
	case WM_CTLCOLORDLG:
	case WM_CTLCOLOREDIT:
	case WM_CTLCOLORLISTBOX:
	case WM_CTLCOLORMSGBOX:
	case WM_CTLCOLORSCROLLBAR:
	case WM_CTLCOLORSTATIC:
		hWndChild = (HWND)lParam;
		idFrom = (UINT_PTR)::GetDlgCtrlID(hWndChild);
		break;
	default:
		break;
	}

	if((hWndChild == NULL) ||
		((hWndChildFilter != NULL) && (hWndChildFilter != hWndChild)))
	{
		// Either hWndChild isn't valid, or
		// hWndChild doesn't match the filter.
		bHandled = FALSE;
		return 1;
	}

	if((idFromFilter != 0) && (idFromFilter != idFrom))
	{
		// The dialog control id doesn't match the filter.
		bHandled = FALSE;
		return 1;
	}

	ATLASSERT(::IsWindow(hWndChild));
	LRESULT lResult = ::SendMessage(hWndChild, OCM__BASE + uMsg, wParam, lParam);
	if((lResult == 0) && (uMsg >= WM_CTLCOLORMSGBOX) && (uMsg <= WM_CTLCOLORSTATIC))
	{
		// Try to prevent problems with WM_CTLCOLOR* messages when
		// the message wasn't really handled
		bHandled = FALSE;
	}

	return lResult;
}

} // namespace WTL

// Try to prevent problems with WM_CTLCOLOR* messages when
// the message wasn't really handled
#define REFLECT_NOTIFICATIONS_EX() \
{ \
	bHandled = TRUE; \
	lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \
	if((lResult == 0) && (uMsg >= WM_CTLCOLORMSGBOX) && (uMsg <= WM_CTLCOLORSTATIC)) \
		bHandled = FALSE; \
	if(bHandled) \
		return TRUE; \
}

#define REFLECT_NOTIFICATIONS_MSG_FILTERED(uMsgFilter) \
	{ \
		bHandled = TRUE; \
		lResult = WTL::WtlReflectNotificationsFiltered(this->m_hWnd, uMsg, wParam, lParam, bHandled, uMsgFilter, 0, NULL); \
		if(bHandled) \
			return TRUE; \
	}

#define REFLECT_NOTIFICATIONS_ID_FILTERED(idFromFilter) \
	{ \
		bHandled = TRUE; \
		lResult = WTL::WtlReflectNotificationsFiltered(this->m_hWnd, uMsg, wParam, lParam, bHandled, WM_NULL, idFromFilter, NULL); \
		if(bHandled) \
			return TRUE; \
	}

#define REFLECT_NOTIFICATIONS_HWND_FILTERED(hWndChildFilter) \
	{ \
		bHandled = TRUE; \
		lResult = WTL::WtlReflectNotificationsFiltered(this->m_hWnd, uMsg, wParam, lParam, bHandled, WM_NULL, 0, hWndChildFilter); \
		if(bHandled) \
			return TRUE; \
	}

#define REFLECT_NOTIFICATIONS_MSG_ID_FILTERED(uMsgFilter, idFromFilter) \
	{ \
		bHandled = TRUE; \
		lResult = WTL::WtlReflectNotificationsFiltered(this->m_hWnd, uMsg, wParam, lParam, bHandled, uMsgFilter, idFromFilter, NULL); \
		if(bHandled) \
			return TRUE; \
	}

#define REFLECT_NOTIFICATIONS_MSG_HWND_FILTERED(uMsgFilter, hWndChildFilter) \
	{ \
		bHandled = TRUE; \
		lResult = WTL::WtlReflectNotificationsFiltered(this->m_hWnd, uMsg, wParam, lParam, bHandled, uMsgFilter, 0, hWndChildFilter); \
		if(bHandled) \
			return TRUE; \
	}

#define REFLECT_COMMAND(id, code) \
	if((uMsg == WM_COMMAND) && (id == LOWORD(wParam)) && (code == HIWORD(wParam))) \
	{ \
		bHandled = TRUE; \
		lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \
		if(bHandled) \
			return TRUE; \
	}

#define REFLECT_COMMAND_ID(id) \
	if((uMsg == WM_COMMAND) && (id == LOWORD(wParam))) \
	{ \
		bHandled = TRUE; \
		lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \
		if(bHandled) \
			return TRUE; \
	}

#define REFLECT_COMMAND_CODE(code) \
	if((uMsg == WM_COMMAND) && (code == HIWORD(wParam))) \
	{ \
		bHandled = TRUE; \
		lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \
		if(bHandled) \
			return TRUE; \
	}

#define REFLECT_COMMAND_RANGE(idFirst, idLast) \
	if((uMsg == WM_COMMAND) && (LOWORD(wParam) >= idFirst) && (LOWORD(wParam) <= idLast)) \
	{ \
		bHandled = TRUE; \
		lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \
		if(bHandled) \
			return TRUE; \
	}

#define REFLECT_COMMAND_RANGE_CODE(idFirst, idLast, code) \
	if((uMsg == WM_COMMAND) && (code == HIWORD(wParam)) && (LOWORD(wParam) >= idFirst) && (LOWORD(wParam) <= idLast)) \
	{ \
		bHandled = TRUE; \
		lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \
		if(bHandled) \
			return TRUE; \
	}

#define REFLECT_NOTIFY(id, cd) \
	if((uMsg == WM_NOTIFY) && (id == ((LPNMHDR)lParam)->idFrom) && (cd == ((LPNMHDR)lParam)->code)) \
	{ \
		bHandled = TRUE; \
		lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \
		if(bHandled) \
			return TRUE; \
	}

#define REFLECT_NOTIFY_ID(id) \
	if((uMsg == WM_NOTIFY) && (id == ((LPNMHDR)lParam)->idFrom)) \
	{ \
		bHandled = TRUE; \
		lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \
		if(bHandled) \
			return TRUE; \
	}

#define REFLECT_NOTIFY_CODE(cd) \
	if((uMsg == WM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code)) \
	{ \
		bHandled = TRUE; \
		lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \
		if(bHandled) \
			return TRUE; \
	}

#define REFLECT_NOTIFY_RANGE(idFirst, idLast) \
	if((uMsg == WM_NOTIFY) && (((LPNMHDR)lParam)->idFrom >= idFirst) && (((LPNMHDR)lParam)->idFrom <= idLast)) \
	{ \
		bHandled = TRUE; \
		lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \
		if(bHandled) \
			return TRUE; \
	}

#define REFLECT_NOTIFY_RANGE_CODE(idFirst, idLast, cd) \
	if((uMsg == WM_NOTIFY) && (cd == ((LPNMHDR)lParam)->code) && (((LPNMHDR)lParam)->idFrom >= idFirst) && (((LPNMHDR)lParam)->idFrom <= idLast)) \
	{ \
		bHandled = TRUE; \
		lResult = this->ReflectNotifications(uMsg, wParam, lParam, bHandled); \
		if(bHandled) \
			return TRUE; \
	}


///////////////////////////////////////////////////////////////////////////////
// GetClassLong/SetClassLong redefinition to avoid problems with class members

#ifdef SetClassLongPtrA
  #undef SetClassLongPtrA
  inline LONG_PTR SetClassLongPtrA(HWND hWnd, int nIndex, LONG_PTR dwNewLong)
  {
	return ::SetClassLongA(hWnd, nIndex, LONG(dwNewLong));
  }
#endif

#ifdef SetClassLongPtrW
  #undef SetClassLongPtrW
  inline LONG_PTR SetClassLongPtrW(HWND hWnd, int nIndex, LONG_PTR dwNewLong)
  {
	return ::SetClassLongW(hWnd, nIndex, LONG(dwNewLong));
  }
#endif

#ifdef GetClassLongPtrA
  #undef GetClassLongPtrA
  inline LONG_PTR GetClassLongPtrA(HWND hWnd, int nIndex)
  {
	return ::GetClassLongA(hWnd, nIndex);
  }
#endif

#ifdef GetClassLongPtrW
  #undef GetClassLongPtrW
  inline LONG_PTR GetClassLongPtrW(HWND hWnd, int nIndex)
  {
	return ::GetClassLongW(hWnd, nIndex);
  }
#endif


///////////////////////////////////////////////////////////////////////////////
// CWindowEx - extension of ATL::CWindow

namespace WTL
{

class CWindowEx : public ATL::CWindow
{
public:
	CWindowEx(HWND hWnd = NULL) : ATL::CWindow(hWnd)
	{ }

	CWindowEx& operator =(HWND hWnd)
	{
		m_hWnd = hWnd;
		return *this;
	}

	operator HWND() const
	{
		return m_hWnd;
	}

// Methods
	BOOL PrintWindow(HDC hDC, UINT uFlags = 0)
	{
		ATLASSERT(::IsWindow(m_hWnd));
		return ::PrintWindow(m_hWnd, hDC, uFlags);
	}

	BOOL DragDetect(POINT pt)
	{
		ATLASSERT(::IsWindow(m_hWnd));
		return ::DragDetect(m_hWnd, pt);
	}

	BOOL DragDetect()
	{
		ATLASSERT(::IsWindow(m_hWnd));

		POINT pt = {};
		::GetCursorPos(&pt);
		return ::DragDetect(m_hWnd, pt);
	}

	CWindowEx GetAncestor(UINT uFlags) const
	{
		ATLASSERT(::IsWindow(m_hWnd));
		return CWindowEx(::GetAncestor(m_hWnd, uFlags));
	}

	// Note: Does not work properly on Vista Aero and above
	BOOL AnimateWindow(DWORD dwFlags, DWORD dwTime = 200)
	{
		ATLASSERT(::IsWindow(m_hWnd));
		return ::AnimateWindow(m_hWnd, dwTime, dwFlags);
	}

	BOOL FlashWindowEx(DWORD dwFlags, UINT uCount, DWORD dwTimeout = 0)
	{
		ATLASSERT(::IsWindow(m_hWnd));

		FLASHWINFO fi = { sizeof(FLASHWINFO) };
		fi.hwnd = m_hWnd;
		fi.dwFlags = dwFlags;
		fi.uCount = uCount;
		fi.dwTimeout = dwTimeout;
		return ::FlashWindowEx(&fi);
	}

	BOOL StopFlashWindowEx()
	{
		ATLASSERT(::IsWindow(m_hWnd));

		FLASHWINFO fi = { sizeof(FLASHWINFO) };
		fi.hwnd = m_hWnd;
		fi.dwFlags = FLASHW_STOP;
		return ::FlashWindowEx(&fi);
	}

// Class long properties
	DWORD GetClassLong(int nIndex) const
	{
		ATLASSERT(::IsWindow(m_hWnd));
		return ::GetClassLong(m_hWnd, nIndex);
	}

	DWORD SetClassLong(int nIndex, LONG dwNewLong)
	{
		ATLASSERT(::IsWindow(m_hWnd));
		return ::SetClassLong(m_hWnd, nIndex, dwNewLong);
	}

	ULONG_PTR GetClassLongPtr(int nIndex) const
	{
		ATLASSERT(::IsWindow(m_hWnd));
		return ::GetClassLongPtr(m_hWnd, nIndex);
	}

	ULONG_PTR SetClassLongPtr(int nIndex, LONG_PTR dwNewLong)
	{
		ATLASSERT(::IsWindow(m_hWnd));
		return ::SetClassLongPtr(m_hWnd, nIndex, dwNewLong);
	}

// Layered windows
	BOOL SetLayeredWindowAttributes(COLORREF crlKey, BYTE byteAlpha, DWORD dwFlags)
	{
		ATLASSERT(::IsWindow(m_hWnd));
		ATLASSERT((GetExStyle() & WS_EX_LAYERED) != 0);

		return ::SetLayeredWindowAttributes(m_hWnd, crlKey, byteAlpha, dwFlags);
	}

	BOOL UpdateLayeredWindow(HDC hdcDst, LPPOINT pptDst, LPSIZE psize, HDC hdcSrc, LPPOINT pptSrc, COLORREF crlKey, BLENDFUNCTION* pblend, DWORD dwFlags)
	{
		ATLASSERT(::IsWindow(m_hWnd));
		ATLASSERT((GetExStyle() & WS_EX_LAYERED) != 0);

		return ::UpdateLayeredWindow(m_hWnd, hdcDst, pptDst, psize, hdcSrc, pptSrc, crlKey, pblend, dwFlags);
	}

	BOOL UpdateLayeredWindow(LPPOINT pptDst = NULL)
	{
		ATLASSERT(::IsWindow(m_hWnd));
		ATLASSERT((GetExStyle() & WS_EX_LAYERED) != 0);

		return ::UpdateLayeredWindow(m_hWnd, NULL, pptDst, NULL, NULL, NULL, CLR_NONE, NULL, 0);
	}

	BOOL GetLayeredWindowAttributes(COLORREF* pcrlKey, BYTE* pbyteAlpha, DWORD* pdwFlags) const
	{
		ATLASSERT(::IsWindow(m_hWnd));
		ATLASSERT((GetExStyle() & WS_EX_LAYERED) != 0);

		return ::GetLayeredWindowAttributes(m_hWnd, pcrlKey, pbyteAlpha, pdwFlags);
	}

// Mouse tracking
	BOOL StartTrackMouseLeave()
	{
		ATLASSERT(::IsWindow(m_hWnd));

		TRACKMOUSEEVENT tme = {};
		tme.cbSize = sizeof(TRACKMOUSEEVENT);
		tme.dwFlags = TME_LEAVE;
		tme.hwndTrack = m_hWnd;
		return ::TrackMouseEvent(&tme);
	}

	BOOL StartTrackMouse(DWORD dwFlags, DWORD dwHoverTime = HOVER_DEFAULT)
	{
		ATLASSERT(::IsWindow(m_hWnd));

		TRACKMOUSEEVENT tme = {};
		tme.cbSize = sizeof(TRACKMOUSEEVENT);
		tme.dwFlags = dwFlags;
		tme.hwndTrack = m_hWnd;
		tme.dwHoverTime = dwHoverTime;
		return ::TrackMouseEvent(&tme);
	}

	BOOL CancelTrackMouse(DWORD dwType)
	{
		ATLASSERT(::IsWindow(m_hWnd));

		TRACKMOUSEEVENT tme = {};
		tme.cbSize = sizeof(TRACKMOUSEEVENT);
		tme.dwFlags = TME_CANCEL | dwType;
		tme.hwndTrack = m_hWnd;
		return ::TrackMouseEvent(&tme);
	}

// CString support
#ifdef __ATLSTR_H__
	int GetWindowText(ATL::CString& strText) const
	{
		int nLength = GetWindowTextLength();
		LPTSTR pszText = strText.GetBuffer(nLength + 1);
		nLength = ::GetWindowText(m_hWnd, pszText, nLength + 1);
		strText.ReleaseBuffer(nLength);

		return nLength;
	}

	UINT GetDlgItemText(int nID, ATL::CString& strText) const
	{
		ATLASSERT(::IsWindow(m_hWnd));

		HWND hItem = GetDlgItem(nID);
		if(hItem != NULL)
		{
			int nLength = ::GetWindowTextLength(hItem);
			LPTSTR pszText = strText.GetBuffer(nLength + 1);
			nLength = ::GetWindowText(hItem, pszText, nLength + 1);
			strText.ReleaseBuffer(nLength);

			return nLength;
		}
		else
		{
			strText.Empty();

			return 0;
		}
	}
#endif // __ATLSTR_H__

// Dialog window only
	UINT DlgGetDefID() const
	{
		ATLASSERT(::IsWindow(m_hWnd));

		LRESULT lRet = ::SendMessage(m_hWnd, DM_GETDEFID, 0, 0L);
		UINT uID = 0U;
		if(HIWORD(lRet) == DC_HASDEFID)
			uID = LOWORD(lRet);

		return uID;
	}

	void DlgSetDefID(UINT uID)
	{
		ATLASSERT(::IsWindow(m_hWnd));

		::SendMessage(m_hWnd, DM_SETDEFID, uID, 0L);
	}

	void DlgReposition()
	{
		ATLASSERT(::IsWindow(m_hWnd));

		::SendMessage(m_hWnd, DM_REPOSITION, 0, 0L);
	}
};

} // namespace WTL

#endif // __ATLWINX_H__
