/*	XGDispatch.cpp
 *
 *		This is the dispatch object, the root of all message receiving
 *	classes in my application.
 */

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

  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/

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

#include <XConfig.h>			// must be first to optimize compile on MSVC++
#include <XEvent.h>
#include <XApplication.h>		// for the application root object

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

XGDispatch *XGDispatch::gFocus = NULL;

/************************************************************************/
/*																		*/
/*	Construction/Destruction											*/
/*																		*/
/************************************************************************/

/*$	XGDispatch::XGDispatch
 *
 *		Construct me. This constructs my dispatch object by 
 *	constructing me with the last constructed object as my parent.
 */

XGDispatch::XGDispatch()
{
}

/*$	XGDispatch::~XGDispatch
 *
 *		Destructs me. If this object has the current focus, the
 *	focus is moved to the main application. This prevents problems
 *	with events being sent to a focus object that was just deleted.
 *	This also removes me from my XGSender lists, if I was registered
 *	as a listener.
 */

XGDispatch::~XGDispatch()
{
	if (gFocus == this) {
		gFocus = XGAppCore::GetApplication();		// default to root
	}
	
	while (fSend.Length()) {
		fSend[0]->RemoveReceiver(this);
	}
}

/************************************************************************/
/*																		*/
/*	Focus Routines														*/
/*																		*/
/************************************************************************/

/*$	XGDispatch::SetFocus
 *
 *		Set the current focus. This forces the focus to this particular
 *	object. If this already has the focus, this operation does nothing.
 */

void XGDispatch::SetFocus()
{
	/*
	 *	Do I already have focus?
	 */
	
	if (gFocus == this) return;					// I already have the focus
	
	/*
	 *	I can switch focus. Send the appropriate messages
	 */
	
	if (gFocus) gFocus->DoLoseFocus();
	gFocus = this;
	DoGainFocus();
}

/*$	XGDispatch::HasFocus
 *
 *		Do I have focus? This returns true if this object already has
 *	the keyboard focus.
 */

bool XGDispatch::HasFocus()
{
	return (this == gFocus);
}

/*$	XGDispatch::DoGainFocus
 *
 *		Called right after I gain focus. This method is called by
 *	YAAF when this object receives the focus. Normally this does
 *	nothing; override if you need to react to gaining the focus,
 *	for example, to update the user interface of this object.
 */

void XGDispatch::DoGainFocus()
{
}

/*$	XGDispatch::DoLoseFocus
 *
 *		Called right before I lose focus. This method is called
 *	by YAAF when this object is about to lose the focus. By default
 *	this does nothing; override if you need to react by altering
 *	your user interface or bitching to the user about illegal
 *	entry.
 *
 *		Note that the focus model of YAAF no longer permits you
 *	to deny changing the focus to another focus holder if the
 *	input is illegal. Thus, it's best to cope with incorrect data
 *	entry the best way possible, by perhaps showing an alert.
 */

void XGDispatch::DoLoseFocus()
{
}

/*$	XGDispatch::GetParentDispatch
 *
 *		Internal routine which indicates who events should be passed
 *	to when I don't handle the event. By default this is NULL.
 *	This is overridden by the XGView object to point to it's parent
 *	XGView object, and overridden by XGWindows, XGDocument, XGFocus
 *	and XGAppCore objects to point to the appropriate item in the
 *	tree hierarchy.
 *
 *		Normally you would override this only if you really know
 *	what you are doing.
 */

XGDispatch *XGDispatch::GetParentDispatch()
{
	return NULL;
}

/************************************************************************/
/*																		*/
/*	Dispatch Stuff														*/
/*																		*/
/************************************************************************/

/*$	XGDispatch::DoDispatch
 *
 *		Dispatches the message specified. This is the core of the XGDispatch
 *	object, and performs a lookup of the message in the object's dispatch
 *	tables. If 'par' is true, this will also search up the parent dispatch
 *	tree until a single object is found which handles this message.
 *
 *		This is not a recursive routine. Because of the way my dispatch
 *	tables are built, this routine is able to operate without infinite
 *	recursions, a particularly useful trait on platforms with limited
 *	stack space.
 */

long XGDispatch::DoDispatch(long msg, long arg, void *parg, bool par)
{
	_DispatchData *dptr;
	XGDispatch *pThis;
	long i;
	long ret;
	
	/*
	 *	Run through the dispatch table built by my macros
	 */
	
	ret = -1;
	pThis = this;
	for (;;) {
		/*
		 *	Find the top level table for this instance
		 */
			
		dptr = pThis->_GetDispatchTable();
		for (;;) {
			/*
			 *	Send it to the 'dispatch' procedure if it exists.
			 *	This is a catchall which allows redirection of
			 *	events that make it up to here
			 */
			
			if (dptr->dispatch) {
				ret = (pThis->*(dptr->dispatch))(msg,arg,parg);
				if (ret != -1) return ret;
			}
			
			/*
			 *	Scan the table to dispatch to specific procedures
			 */
			
			for (i = 0; (dptr->table)[i].msg != -1; i++) {
				if ((dptr->table)[i].msg == msg) {
					ret = (pThis->*(dptr->table)[i].proc)(arg,parg);
					if (ret != -1) return ret;
					break;
				}
			}
			
			/*
			 *	Find the class parent's table
			 */
		
			dptr = dptr->base;
			if (dptr == NULL) break;
		}
		
		/*
		 *	Now go to my parent if the 'par' flag is true.
		 */
		
		if (!par) break;
		pThis = pThis->GetParentDispatch();
		if (!pThis) break;
	}
	
	return ret;
}


/************************************************************************/
/*																		*/
/*	Root dispatch tables												*/
/*																		*/
/************************************************************************/

/*	XGDispatch::_GetDispatchTable
 *
 *		Default has an empty table
 */

_DispatchData *XGDispatch::_GetDispatchTable()
{
	return &_TableData;
}

/*	XGDispatch::_TableData
 *
 *		This is the dispatch table data.
 */

_DispatchData XGDispatch::_TableData = {
	NULL,
	XGDispatch::_Dispatch
};

/*	XGDispatch::_Dispatch
 *
 *		The table contents
 */

_DispatchTable XGDispatch::_Dispatch[] = {
	{ -1, NULL }
};
