/*	XEvent.h
 *
 *		Event management. This defines the various classes which define
 *	how messages are passed around.
 */

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

  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/

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

#ifndef PandaWave_XEVENT
#define PandaWave_XEVENT

#include <XConfig.h>
#include <XDynArray.h>

/**@name Event Management
 *
 *		In any Application Framework, event processing is key to making
 *	the individual components cooperate and work well together. YAAF
 *	is no exception.
 *
 *		The goals of an application framework's event processing
 *	routines is to enable different components to work together without
 *	having to have intimate knowledge of the other objects, and to
 *	permit a constant interface of system events to flow through the
 *	application, even when those components have been written by
 *	someone else. In fact, in the future these events may be sent
 *	by shared library objects which are not actually a part of your
 *	application's source kit.
 *
 *		YAAF implements three event models. The first is a 'send/
 *	receiver' pair, where an object declares itself a "sender" of
 *	messages, and broadcasts those messages to "receivers" who have
 *	explicitly asked to be registered to get those messages.
 *
 *		The second model is related to the first: a global "broadcast"
 *	model with a central broadcaster, and receivers which can ask
 *	for broadcast messages. This is the mechanism YAAF uses for sending
 *	periodic "tick" messages during the event loop, in order to handle
 *	idle processing.
 *
 *		The third mechanism YAAF implements is a tree-like structure,
 *	associated with YAAF XGView objects, for send user-interface
 *	related messages. Objects are stored in a hierarchy that roughly
 *	corrisponds to the embedding of views in a windows, and messages
 *	that are generated by one object or view is sent upwards through
 *	the tree until someone offers to process that event.
 *
 *		This mechanism includes the concept of a "focus" akin to the
 *	keyboard "focus" model used by Microsoft Windows--in each window,
 *	a single view can elect to have the "focus" and receive both
 *	keyboard and menu commands. These commands are then passed upwards
 *	through the tree until they reach the main XGAppCore objects.
 *
 *		This mechanism also permits objects to send messages to
 *	themselves for processing; that way, if the class sending the
 *	message isn't overridden, the message passes upwards. This is
 *	how controls embedded in a Windows or Dialog box are handled.
 *
 *		In order to simply event processing, only one object (the
 *	XGDispatch object) actually receives messages. It contains a pointer
 *	to a table of messages which it may handle, along with a pointer
 *	to it's "parent" XGDispatch object. To simply the construction
 *	of this dispatch table, a collection of macros are provided that
 *	are akin to the Microsoft MFC class library.
 */

//@{

/************************************************************************/
/*																		*/
/*	Forwards															*/
/*																		*/
/************************************************************************/

class XGDispatch;
class XGSend;

/************************************************************************/
/*																		*/
/*	Dispatch Macros and Structures										*/
/*																		*/
/************************************************************************/

/*	_DispatchProc
 *
 *		This is the dispatch procedure
 */

typedef long					(XGDispatch::*_DispatchProc)(long,long,void *);

/*	_DispatchTable
 *
 *		The entries in my table for the methods that point to the methods
 *	to call when a particlar message is sent
 */

struct _DispatchTable {
	long						msg;
	long						(XGDispatch::*proc)(long,void *);
};

/*	_DispatchData
 *
 *		This is the structure that is built by the dispatch macros to
 *	indicate which classes to search for dispatch methods
 */

struct _DispatchData {
	struct _DispatchData		*base;
	struct _DispatchTable		*table;
	_DispatchProc				dispatch;
};

/*$	BEGINDISPATCH
 *
 *		This is a shorthand macro for declaring the event dispatch
 *	table for the specified class. The base class of that class must
 *	also be provided so that if the event is not found in this table,
 *	the table of the base class can also be searched.
 *
 *		The contents of this table are provided by using one or more
 *	DISPATCHTABLE macros, and the table is ended with ENDDISPATCH.
 *
 *		Usage:
\begin{verbatim}
	BEGINDISPATCH(myClass,baseClass)
		DISPATCHTABLE(kMessage1,myClass::messageProc1)
		DISPATCHTABLE(kMessage2,myClass::messageProc2)
		DISPATCHTABLE(kMessage3,myClass::messageProc3)
	ENDDISPATCH
\end{verbatim}
 */

#define BEGINDISPATCH(classID, baseID)									\
				_DispatchData *classID::_GetDispatchTable()				\
										{								\
											return &classID::_TableData;\
										}								\
				_DispatchData classID::_TableData =	{					\
											&baseID::_TableData,		\
											classID::_Dispatch,			\
											NULL						\
										};								\
				_DispatchTable classID::_Dispatch[] = {					

/*$	BEGINDISPATCHPROC
 *
 *		This is a shorthand macro for declaring the event dispatch
 *	table for the specified class. The base class of that class must
 *	also be provided so that if the event is not found in this table,
 *	the table of the base class can also be searched.
 *
 *		The contents of this table are provided by using one or more
 *	DISPATCHTABLE macros, and the table is ended with ENDDISPATCH.
 *
 *		This is a variant of the BEGINDISPATCH macro, but which also
 *	provides a pointer to a function in the class which receives all
 *	messages. That can be useful in case you need to filter through all
 *	of the messages that pass through this object.
 *
 *		See BEGINDISPATCH for more information.
 */

#define BEGINDISPATCHPROC(classID, baseID, proc)						\
				_DispatchData *classID::_GetDispatchTable()				\
										{								\
											return &classID::_TableData;\
										}								\
				_DispatchData classID::_TableData =	{					\
											&baseID::_TableData,		\
											classID::_Dispatch,			\
											(long (XGDispatch::*)(long,	\
													long,void *))&proc	\
										};								\
				_DispatchTable classID::_Dispatch[] = {					

/*$	DISPATCHTABLE
 *
 *		Declares a single entry in the event dispatch table. This is
 *	how you declare the contents of the dispatch table created using
 *	BEGINDISPATCH or BEGINDISPATCHPROC
 *
 *		See BEGINDISPATCH for more information.
 */

#define DISPATCHTABLE(msg,proc)											\
					{ msg, (long (XGDispatch::*)(long,void *))&proc },				


/*$	ENDDISPATCH
 *
 *		Ends the event dispatch table declared using BEGINDISPATCH or
 *	BEGINDISPATCHPROC
 *
 *		See BEGINDISPATCH for more information.
 */

#define ENDDISPATCH														\
											{ -1, NULL }				\
										};								

/*$	DECLAREDISPATCH
 *
 *		This notes in this particular class the declarations needed
 *	to make the BEGINDISPATCH/ENDDISPATCH stuff. If your XGDispatch-
 *	derived event handling class actually handles events, you would
 *	insert this macro into the class declaration in your header file.
 *
 *		You must provide this to any class which handles messages, even
 *	if this class isn't directly responsible for processing events.
 *
 *	Usage:
\begin{verbatim}
	class MyClass: public XGDispatch {
		public:
			DECLAREDISPATCH
			
			...  (other declarations go here) ...
			
	};
\end{verbatim}
 */

#define	DECLAREDISPATCH													\
				virtual _DispatchData *_GetDispatchTable();				\
				static _DispatchData  _TableData;						\
				static _DispatchTable _Dispatch[];				

/************************************************************************/
/*																		*/
/*	Dispatcher															*/
/*																		*/
/************************************************************************/

/*$	XGDispatch
 *
 *		This is the universal receiver. This is the thing which receives
 *	all messages that are broadcast, sent or dispatched through the
 *	current focus.
 *
 *		If this is acting as a receiver from a sender, this can be
 *	inserted into multiple XGSender objects. Further, this can be
 *	inserted into a class hierarchy, making the source of the events
 *	a quiet mystery...
 */

class XGDispatch {
	public:
								XGDispatch();
		virtual					~XGDispatch();
		
		/*
		 *	focus
		 */
		
		static XGDispatch		*GetFocus()
									{
										return gFocus;
									}
		virtual void			SetFocus();
		virtual void			DoGainFocus();
		virtual void			DoLoseFocus();
		bool					HasFocus();
		
		/*
		 *	message dispatch directly to me
		 */
		
		long					DoDispatch(long,long,void *,bool par = true);
		static long				SendDispatch(long msg, long arg, void *parg)
									{
										if (gFocus) {
											return gFocus->DoDispatch(msg,arg,parg);
										} else {
											return -1;
										}
									}

		/*
		 *	Dispatch table internal stuff
		 */
		
		DECLAREDISPATCH
		
	protected:
		virtual XGDispatch		*GetParentDispatch();
		
	private:
		static XGDispatch		*gFocus;		// the one and only focus object
		XGPDynArray<XGSend>		fSend;			// stuff attached to me
	
	friend class XGSend;
};

/*********************************************=**********************/
/*																		*/
/*	Send/Receive Management												*/
/*																		*/
/************************************************************************/

/*	XGSDispRecord
 *
 *		How I store the array of dispatch objects; this also maintains
 *	a flag which allows me to make sure I send this thing regardless of
 *	what is deleted during broadcast
 */

struct XGSDispRecord {
	bool		mark;
	XGDispatch	*disp;
};

/*$	XGSend
 *
 *		This is part of a send/receive pair; this is the thing that
 *	sends messages
 */

class XGSend {
	public:
								XGSend(void);
		virtual					~XGSend();
		
		/*
		 *	Receiver management
		 */
		
		void					AddReceiver(XGDispatch *);
		void					RemoveReceiver(XGDispatch *);
		
		/*
		 *	Message dispatch
		 */
		
		void					SendMessage(long,long,void *);
		
	private:
		bool					fDelMark;
		XGSDynArray<XGSDispRecord> fReceive;
};

/************************************************************************/
/*																		*/
/*	Broadcaster															*/
/*																		*/
/************************************************************************/

/*	_PostRecord
 *
 *		This is how posts are actually stored
 */

struct _PostRecord {
	long			msg;
	long			arg;
	void			*parg;
};

/*$	XGBroadcast
 *
 *		This is the class for sending global broadcasts. This is basically
 *	a system-wide 'XGSend' object.
 */

class XGBroadcast {
	public:
		/*
		 *	Message dispatch
		 */
		
		/*$	XGBroadcast::AddBroadcast
		 *
		 *		Adds the specified dispatch object to the list of
		 *	dispatch objects which will receive broadcasted messages.
		 *	If you are interested in globally dispatched events,
		 *	you must register your receiver with this method.
		 */
		
		static void				AddBroadcast(XGDispatch *r)
									{
										gSend.AddReceiver(r);
									}
		
		/*$	XGBroadcast::RemoveBroadcast
		 *
		 *		Removes the specified dispatch object from the list
		 *	of objects which will receive broadcasted messages.
		 *	This method is autmoatically called when your receiver
		 *	object destroys itself.
		 */
		
		static void				RemoveBroadcast(XGDispatch *r)
									{
										gSend.RemoveReceiver(r);
									}
									
		/*$	XGBroadcast::SendBroadcast
		 *
		 *		Actually broadcasts a message to all receivers which
		 *	are receiving globally broadcast messages. Call this
		 *	method to immediately broadcast an event.
		 */
		
		static void				SendBroadcast(long msg, long arg, void *parg)
									{
										gSend.SendMessage(msg,arg,parg);
									}
									
		static void				PostBroadcast(long,long,void *);
		static void				SendPostBroadcast(void);
	private:
		static XGSend			gSend;
		
		static XGSDynArray<_PostRecord> gPostStack;
};

/************************************************************************/
/*																		*/
/*	Periodic Event Mixin												*/
/*																		*/
/************************************************************************/

/*$	XGPeriodic
 *
 *		This is a mixin object which supplies a pre event process and
 *	post event process periodic event tick. This provides a simplified
 *	way of handling periodic events, especially if you are only interested
 *	in periodic stuff instead of getting (and screening out) the whole
 *	gammut of global events.
 */

class XGPeriodic {
	public:
								XGPeriodic();
		virtual					~XGPeriodic();
		
		/*
		 *	Event overrides
		 */
		
		virtual void			DoPrePeriodic();
		virtual void			DoPostPeriodic();
	
		/*
		 *	Send the events
		 */
		
		static void				SendPeriodic(bool post);
	private:
		static XGPeriodic		*gList;

		XGPeriodic				*fNext;
		XGPeriodic				*fPrev;
};

//@}

/*
 *	Make DOC++ happy.
 */

#ifndef _BOGUS
#define _BOGUS }
#undef _BOGUS
#endif

#endif // PandaWave_XEVENT
