/*	CursorUtil.c
 *
 *		Custom cursor utilities
 */

/**********************************************************************

  YAAF is Copyright 1997-1999 by William Woody.

  Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions 
  are met:

  Redistributions of source code must retain the above copyright notice, 
  this list of conditions and the following disclaimer.

  Neither the name of the copyright holder or other contributors may
  be used to promote products derived from this software without
  specific written prior permission.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  SOFTWARE.
  
  Contact:   woody@alumni.caltech.edu
  Web page:  http://www.pandawave.com/yaaf/

**********************************************************************/
 
/**@name Support--Cursor Utilities
 *
 *	These routines provide a standardized interface for displaying
 *	a particular mouse cursor, or dispalying an animated sequence
 *	used to indicate the system is waiting. This is portable across
 *	multiple platforms.
 *
 *	Normally you would call SetCursorID() to set the cursor to one of the
 *	predefined constants contained in <XCursorUtil.h>. Or, if you want
 *	to indicate that the application is busy, you can repeatedly call
 *	WaitCursor(), followed by NormalCursor() to restore the cursor after
 *	a wait. WaitCursor() contains all of the logic necessary to put up
 *	an hourglass, and start spinning the cursor animation if too much
 *	time has elapsed.
 */

//@{

#include <XConfig.h>			// must be first to optimize compile on MSVC++
#include <stdlib.h>
#include <XCursorUtil.h>
#include <XConfig.h>
#include <XMisc.h>

#if OPT_XWIN == 1
#include <X11/cursorfont.h>
#include <X11/Intrinsic.h>

#include <XWindow.h>
#endif

/************************************************************************/
/*																		*/
/*	Globals																*/
/*																		*/
/************************************************************************/

static short			*GCursor;
static short			GCurCursor = KNormalCursor;
static short			GCurWatch = -1;
static unsigned long	GWaitTime;

#if OPT_WINOS

extern "C" {
	extern HINSTANCE _GInstance;	// Needed for me to work...
}

#endif

/************************************************************************/
/*																		*/
/*	Initialization														*/
/*																		*/
/************************************************************************/

/*$	InitCursorAnimation
 *
 *		Initialize the cursor animation sequence. This is an optional call
 *	with an array of cursor resource numbers which is repeatedly sequenced
 *	through as WaitCursor() is called.
 *
 *		This is called with a null-terminated array of resource identifiers 
 *	indicating the sequence of frames to show during a wait sequence. 
 */

void InitCursorAnimation(short *l)
{
	GCursor = l;
}

/************************************************************************/
/*																		*/
/*	Set the cursor stuff												*/
/*																		*/
/************************************************************************/

/*	_SetCursorID
 *
 *		Set the cursor by the specified identifier
 */

static void _SetCursorID(short id)
{
#if OPT_MACOS == 1
	CursHandle c;
	CCrsrHandle cc;
	static short gCursor = -1;
	extern bool _GAppFrontmost;

	/*
	 *	Not frontmost? Then MacOS UI spec says don't change cursor.
	 *	Besides, it's rude.
	 */
		
	if (!_GAppFrontmost) {
		/*
		 *	Kludge: If I'm set to the normal cursor *and* I'm no
		 *	longer the active application, clear the cursor value.
		 *	This is used by the XGAppCore class to tell me that
		 *	I'm no longer active
		 */
	
		if (id == KNormalCursor) gCursor = -1;
		return;
	}
	
	/*
	 *	Fast skip this if I've already set the cursor to this.
	 *	This is kept separate from the other variables as they
	 *	may not reflect the state of the cursor. This does, as
	 *	*all* my cursor calls come down to this routine.
	 */
	
	if (gCursor == id) return;
	gCursor = id;
	
	/*
	 *	Macintosh world--first, find the color cursor
	 */
	
	if (id == KNormalCursor) {
		// Trap out this one--KNormalCursor is in global memory
		SetCursor(&qd.arrow);
	} else {
		// Get the color cursor if possible
		cc = GetCCursor(id);
		if (cc) {
			SetCCursor(cc);
			DisposeCCursor(cc);
		} else {
			// Color cursor is not there--try black and white
			c = GetCursor(id);
			if (c) {
				HLock((Handle)c);
				SetCursor(*c);
				HUnlock((Handle)c);
			/* releasing the resource here causes serious pain
			 * if the app is stored on a file server.
			 *	ReleaseResource((Handle)c);
			 */
			} else {
				SetCursor(&qd.arrow);
			}
		}
	}
#endif

#if OPT_WINOS == 1
	HCURSOR cursor;
	HCURSOR oldCursor;
	
	if (id == 0) {					// Arrow cursor
		cursor = LoadCursor(NULL,IDC_ARROW);
	} else if (id == 1) {			// iBeamCursor
		cursor = LoadCursor(NULL,IDC_IBEAM);
	} else if (id == 2) {			// crossCursor
		cursor = LoadCursor(NULL,IDC_CROSS);
	} else if (id == 3) {			// plusCursor
		cursor = LoadCursor(NULL,IDC_CROSS);
	} else if (id == 4) {			// watchCursor
		cursor = LoadCursor(NULL,IDC_WAIT);
	} else {
		cursor = LoadCursor(_GInstance,MAKEINTRESOURCE(id));
		if (cursor == NULL) {
			id = 0;
			cursor = LoadCursor(NULL,IDC_ARROW);
		}
	}
	
	oldCursor = SetCursor(cursor);
	
	if (oldCursor != cursor) {
		DestroyCursor(oldCursor);
	}
#endif

#if OPT_XWIN == 1
	/*
	 *	Get the shell window
	 */

	XGWindow *w = XGWindow::GetFrontWindow();
	if (w == NULL) return;
	extern Display *_GDisplay;
	Cursor cursor;						/* This really is a bit of a kludge... */
	
	if (w->fCursorID != id) {
		w->fCursorID = id;
		cursor = XCreateFontCursor(_GDisplay,id);
		XDefineCursor(_GDisplay,w->GetWindow(),cursor);
	}
#endif
}

/*$	SetCursorID
 *
 *		Sets the current cursor to the one specified. This is stored away
 *	so that the specified cursor can be redisplayed after a wait sequence.
 */

void SetCursorID(short id)
{
	GWaitTime = 0;
	GCurWatch = -1;
	GCurCursor = id;
	_SetCursorID(id);
}

/*$	NormalCursor
 *
 *		Restore the prior cursor after a wait cursor loop. This puts up
 *	a simple arrow cursor on the screen.
 */

void NormalCursor(void)
{
	if (GCurWatch != -1) {
		GWaitTime = 0;
		GCurWatch = -1;
		_SetCursorID(GCurCursor);
	}
}

/*$	WaitCursor
 *
 *		Wait cursor. This spins the cursor animation sequence if any, or
 *	else just puts up the standard 'wait' cursor if no sequence was provided.
 */

void WaitCursor(void)
{
	if (GCursor == NULL) {
		/*
		 *	No animation
		 */
		
		if (GCurWatch == -1) {
			GCurWatch = 0;
			_SetCursorID(KWatchCursor);		/* Default without animation */
		}
	} else {
		if (GWaitTime == 0) {
			GWaitTime = XGGetTime() + XGGetSecond()/4;
			return;
		}
		if (GWaitTime > XGGetTime()) return;
		GWaitTime = XGGetTime() + XGGetSecond()/4;

		GCurWatch++;
		if (GCursor[GCurWatch] == 0) GCurWatch = 0;
		_SetCursorID(GCursor[GCurWatch]);
	}
}

//@}
