/*****************************************************************************
* Cursor.h : Utility class for dealing with HCURSORs
* 
* TODO: Summary of classes
*
*****************************************************************************/
#ifndef __CURSOR_H__
#define __CURSOR_H__

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#if (WINVER < 0x0500) && defined(_WTL_VER)
# include <atlctrls.h>
# include <atlctrlx.h> /*for the link cursor*/
#endif //(WINVER < 0x0500) && defined(_WTL_VER)

#include <winuser.h>

#if (!defined(ASSERT) && defined(_ATL_VER))
# define ASSERT ATLASSERT
#endif//(!defined(ASSERT) && defined(_ATL_VER))

/*****************************************************************************
* CLASS: 
*  CCursorT
*
* PLATFORM:
*  Windows, WTL 3, WTL 7.x - should work with MFC & pure win32
*
* AUTHOR:
*  [JED] Jason "Lumberjack" De Arte
*  http://www.1001010.com/
*  Copyright (c) 2002 Jason De Arte
*  Code is AS-IS & use at your own risk.  It may work, but then again, it probably won't :)
*
* DESCRIPTION: 
*  Simple utility class for using HCURSORs.  
*  I needed something that would wrap the existance/non-existence of IDC_HAND
*  It's not the "end-all be-all" solution, but it fits my needs
*
*  The design is inspired by the WTL classes in ATLGDI.H
*
* USAGE:
*  Just like you would an HCURSOR
*
* HISTORY: 
*  2002.Apr.09.JED - created
* 
*******************************************************************************/

// Simple enumeration of the predefined cursors
enum CursorEnum
{
	curArrow       = 32512,  // IDC_ARROW         Standard arrow
	curIBeam       = 32513,  // IDC_IBEAM         I-beam
	curWait        = 32514,  // IDC_WAIT          Hourglass
	curCross       = 32515,  // IDC_CROSS         Crosshair
	curUpArrow     = 32516,  // IDC_UPARROW       Vertical arrow
	curSize        = 32640,  // IDC_SIZE          Obsolete for applications marked version 4.0 or later. Use IDC_SIZEALL
	curIcon        = 32641,  // IDC_ICON          Obsolete for applications marked version 4.0 or later
	curSizeNWSE    = 32642,  // IDC_SIZENWSE      Double-pointed arrow pointing northwest and southeast
	curSizeNESW    = 32643,  // IDC_SIZENESW      Double-pointed arrow pointing northeast and southwest
	curSizeWE      = 32644,  // IDC_SIZEWE        Double-pointed arrow pointing west and east
	curSizeNS      = 32645,  // IDC_SIZENS        Double-pointed arrow pointing north and south
	curSizeAll     = 32646,  // IDC_SIZEALL       Four-pointed arrow pointing north, south, east, and west
	curNo          = 32648,  // IDC_NO            Slashed circle
	curHand        = 32649,  // IDC_HAND          Windows 98/Me, Windows 2000/XP: Hand
	curAppStarting = 32650,  // IDC_APPSTARTING   Standard arrow and small hourglass
	curHelp        = 32651,  // IDC_HELP          Arrow and question mark
	cur_NULL       = 0,      // Special, uninitialized state
	cur_Created    = 1,      // Special, this was created with ::CreateCursor()
};

// Did you know that all the system cursors (that I know about when writing this code)
// range from 0x7f00 to 0x7f8b?  It makes for a quick (& imperfect) sanity check
#define SYS_CURSOR_MASK  0x7F00

//**************************************************************************************
//
// The class CCursorT
//
template <bool t_bManaged>
class CCursorT
{
private:
	HCURSOR    m_hCursor;         // What this class wraps
	BOOL       m_bMustNotDestroy; // if we know we shouldn't do it - we shouldn't do it.
	CursorEnum m_id;              // Useful during debugging
	
public:

	////////////////////////////////////////////////////////////////////////////
	// 
	CCursorT( CursorEnum cur ) : m_hCursor(NULL), m_id(cur_NULL), m_bMustNotDestroy(0)
	{
		CCursorT::LoadCursor(cur);
	}

	////////////////////////////////////////////////////////////////////////////
	// 
	CCursorT( HCURSOR hCursor = NULL ) : m_hCursor(hCursor), m_id(cur_NULL), m_bMustNotDestroy(0)
	{ }

	////////////////////////////////////////////////////////////////////////////
	// 
	virtual ~CCursorT()
	{
		DestroyCursor();
	}

	////////////////////////////////////////////////////////////////////////////
	// 
	CCursorT<t_bManaged>& operator=(HCURSOR hCursor)
	{
		m_hCursor = hCursor;
		return *this;
	}

	////////////////////////////////////////////////////////////////////////////
	// 
	CCursorT<t_bManaged>& operator=(CursorEnum cur)
	{
		CCursorT::LoadCursor(cur);
		return *this;
	}

	////////////////////////////////////////////////////////////////////////////
	// 
	operator HCURSOR() const { return m_hCursor; }

	////////////////////////////////////////////////////////////////////////////
	// 
	bool IsNull() const      { return(NULL==m_hCursor); }

	////////////////////////////////////////////////////////////////////////////
	// 
	bool IsValid() const     { return(NULL!=m_hCursor); }

	////////////////////////////////////////////////////////////////////////////
	// Set our cursor as the active cursor
	HCURSOR SetCursor() const
	{
		if( m_hCursor )
			return ::SetCursor(m_hCursor);
		return NULL;
	}

	////////////////////////////////////////////////////////////////////////////
	// Display/Hide the currently displayed/active cursor (which may or may not be ours)
	static int ShowActiveCursor(BOOL bShow)
	{
		return ::ShowCursor(bShow);
	}

	////////////////////////////////////////////////////////////////////////////
	// Is the currently displayed/active cursor visible?  (which may or may not be ours)
	static BOOL IsActiveCursorVisible()
	{
		BOOL bVisible = FALSE;
		CCursorT::GetActiveCursorInfo(&bVisible,NULL,NULL);
		return bVisible;
	}

	////////////////////////////////////////////////////////////////////////////
	// Set the position of the currently displayed cursor (which may or may not be ours)
	static BOOL SetActiveCursorPos(POINT pt)
	{
		return ::SetCursorPos(pt.x,pt.y);
	}

	////////////////////////////////////////////////////////////////////////////
	//  Get the position of the currently displayed cursor (which may or may not be ours)
	static POINT GetActiveCursorPos()
	{
		POINT pt = {0,0};
		if( !::GetCursorPos(&pt) )
			pt.x = pt.y = 0;
		return pt;
	}

	////////////////////////////////////////////////////////////////////////////
	// Get information about the currently displayed cursor (which may or may not be ours)
	static BOOL GetActiveCursorInfo(BOOL* pbVisible, HCURSOR* phCursor, POINT* pptScreenPos)
	{
		if(!pbVisible && !phCursor && !pptScreenPos )
		{
			ASSERT(!"If you don't pass in anything, I can't do anything");
			return FALSE;
		}

#if(WINVER >= 0x0500)
		CURSORINFO info = {0};
		info.cbSize = sizeof(CURSORINFO);
		if( ::GetCursorInfo(&info) )
		{
			if( pbVisible )
				*pbVisible = ((info.flags&CURSOR_SHOWING)==CURSOR_SHOWING);
			if( phCursor )
				*phCursor = info.hCursor;
			if( pptScreenPos )
				*pptScreenPos = info.ptScreenPos;
			return TRUE;
		}
#endif

		if( pbVisible )
		{
			ShowActiveCursor(FALSE);
			int nDisplayCount = ShowActiveCursor(TRUE);
			if( nDisplayCount > 0 )
				*pbVisible = TRUE;
			*pbVisible = FALSE;
		}
		
		if( phCursor )
			*phCursor = ::GetCursor();

		if( pptScreenPos )
			::GetCursorPos(pptScreenPos);

		return TRUE;
	}
	
	////////////////////////////////////////////////////////////////////////////
	// Get the HCURSOR of the currently displayed cursor (which may or may not be ours)
	static HCURSOR GetActiveCursor()
	{
		HCURSOR hc = NULL;
		CCursorT::GetActiveCursorInfo(NULL,&hc,NULL);
		return hc;
	}

#if 0
	static BOOL SetActiveClipArea(LPCRECT lprcScreenArea)
	{
		// process must have WINSTA_WRITEATTRIBUTES 
		// call with NULL to remove clipping
		return ClipCursor(lprcScreenArea);
	}
#endif

#if 0
	static BOOL GetActiveClipArea(RECT& rcScreenArea)
	{
		// process must have WINSTA_READATTRIBUTES 
		return GetClipCursor(&rcScreenArea);
	}
#endif 

	////////////////////////////////////////////////////////////////////////////
	// 
	void Attach( HCURSOR hCursor )
	{
		DestroyCursor();
		m_hCursor = hCursor;
	}

	////////////////////////////////////////////////////////////////////////////
	// 
	HCURSOR Detach()
	{
		HCURSOR hCursor = m_hCursor;
		m_hCursor = NULL;
		return hCursor;
	}

	////////////////////////////////////////////////////////////////////////////
	// For naming convention compatability with the rest of atlgdi.h
	BOOL DeleteObject() 
	{ 
		return DestroyCursor(); 
	}

	////////////////////////////////////////////////////////////////////////////
	//
	BOOL DestroyCursor()
	{
		BOOL bResult = TRUE;

		if( t_bManaged && m_hCursor != NULL && !m_bMustNotDestroy )
		{
			// Do not use this function to destroy a shared cursor!
			// The following functions obtain a shared cursor: 
			//  * LoadCursor 
			//  * LoadCursorFromFile 
			//  * LoadImage (if you use the LR_SHARED flag) 
			//  * CopyImage (if you use the LR_COPYRETURNORG flag and the hImage parameter is a shared cursor) 
			bResult = ::DestroyCursor(m_hCursor);
		}
		m_hCursor         = NULL;
		m_id              = cur_NULL;
		m_bMustNotDestroy = FALSE;

		return bResult;
	}

	////////////////////////////////////////////////////////////////////////////
	// creates a cursor from image data
	BOOL CreateCursor( int        xHotSpot,           
	                   int        yHotSpot, 
	                   int        nWidth,
	                   int        nHeight, 
                     LPCVOID    pvANDPlane, 
	                   LPCVOID    pvXORPlane, 
#if defined(_ATL_VER)
	                   HINSTANCE  hApplicationInstance = _Module.GetModuleInstance() 
#elif defined(_MFC_VER)
	                   HINSTANCE  hApplicationInstance = AfxGetInstanceHandle()
#else
	                   HINSTANCE  hApplicationInstance
#endif
										 )
	{
		DestroyCursor();

		m_hCursor = ::CreateCursor( hApplicationInstance, xHotSpot, yHotSpot, nWidth, nHeight, pvANDPlane, pvXORPlane );
		if( NULL != m_hCursor )
		{
			m_id = cur_Created;
			m_bMustNotDestroy = FALSE;
			return TRUE;
		}
		return FALSE;
	}

	////////////////////////////////////////////////////////////////////////////
	// Load a user defined cursor from resources
	BOOL LoadCursor( LPCTSTR   lpCursorName, 
#if defined(_ATL_VER)
	                 HINSTANCE hResourceInstance = _Module.GetResourceInstance() 
#elif defined(_MFC_VER)
	                 HINSTANCE hResourceInstance = AfxGetResourceHandle()
#else
	                 HINSTANCE hResourceInstance
#endif
		)
	{
		DestroyCursor();
		m_hCursor = ::LoadCursor(hResourceInstance,lpCursorName);
		if( m_hCursor )
		{
			m_bMustNotDestroy = (hResourceInstance!=NULL);
			return TRUE;
		}
		return FALSE;
	}

	////////////////////////////////////////////////////////////////////////////
	// Load a normal "predefined" cursor
	BOOL LoadCursor(CursorEnum cur)
	{
		if( (SYS_CURSOR_MASK&cur) != SYS_CURSOR_MASK )
		{
			ASSERT(!"Not a valid cursor enum");
			return FALSE;
		}

		// load the new cursor
		if( CCursorT::LoadCursor( MAKEINTRESOURCE(cur), NULL ) )
		{
			m_id = cur;
		}
		else 
		{
			//
			// it failed?
			//
			// Maybe it was a special case cursor
			//
			switch( cur )
			{
			case curHand:
				{
				// the hand/link cursor is unavailable, get one from those kind folks that wrote WTL	
				#if (WINVER < 0x0500) && defined(_WTL_VER)
					CCursorT::CreateCursor( _AtlHyperLink_CursorData.xHotSpot,     
					                        _AtlHyperLink_CursorData.yHotSpot, 
					                        _AtlHyperLink_CursorData.cxWidth,
					                        _AtlHyperLink_CursorData.cyHeight, 
					                        _AtlHyperLink_CursorData.arrANDPlane,
					                        _AtlHyperLink_CursorData.arrXORPlane );
				#else
					ASSERT(!"unable to load a hand cursor");
					m_id = cur_NULL;
				#endif //(WINVER < 0x0500) && defined(_WTL_VER)
				}
				break;

			case curHelp:
				ASSERT(!"TODO: support IDC_HELP");
				m_id = cur_NULL;
				break;

			case curSize:
				ASSERT(!"WinUser.h comments say that IDC_SIZE is obsolete, I guess it finally is");
				return CCursorT::LoadCursor(curSizeAll);
				break;

			case curIcon:
				ASSERT(!"WinUser.h comments say that IDC_ICON is obsolete, I guess it finally is");
				return CCursorT::LoadCursor(curArrow);
				break;

			default:
				ASSERT(!"Hmm, maybe we need to add other special case cursors?");
				m_id = cur_NULL;
			}
		}

		return( NULL != m_hCursor );
	}
};

//**************************************************************************************
//
// Helpful typedefs
//
typedef CCursorT<false>  CCursorHandle;
typedef CCursorT<true>   CCursor;


//**************************************************************************************
//
// Simple scope based set cursor/restore old cursor class
//
class CAutoWaitCursor : public CCursor
{
public:
	CCursorHandle m_oldCursor;

	CAutoWaitCursor( CursorEnum cursor = curWait ) : CCursor(cursor)
	{
		m_oldCursor = SetCursor();
	}

	~CAutoWaitCursor()
	{
		m_oldCursor.SetCursor();
	}
};

#endif //__CURSOR_H__
