/*****************************************************************************
* 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__