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

/*  YAAF - Yet another application framework
 *  Copyright (C) 1997, 1998 William Edward Woody
 *  
 *  This library is free software; you can redistribute it
 *  and/or modify it under the terms of the GNU Library
 *  General Public License as published by the Free Software
 *  Foundation; either version 2 of the License, or any
 *  later version.
 *  
 *  This library is distributed in the hope that it will be
 *  useful, but WITHOUT ANY WARRANTY; without even the implied
 *  warranty of MERCHANTABIILITY or FITNESS FOR A PARTICULAR
 *  PURPOSE. See the GNU Library General Public License for
 *  more details.
 *  
 *  You should have received a copy of the GNU Library General
 *  Public License along with this library; if not, write to the
 *  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 *  
 *  To contact the author, either e-mail me at woody@pandawave.com, 
 *	or write to
 *  
 *          William Edward Woody
 *          In Phase Consulting
 *          1545 Ard Eevin Avenue
 *          Glendale, CA 91202
 *
 *	Or visit us on the web at http://www.pandawave.com
 */

#ifndef PandaWave_XEVENT
#define PandaWave_XEVENT

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

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

class XGDispatch;
class XGFocus;
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;
};

/*	@F"BEGINDISPATCH"
 *	@H"XEvent.h"
 *	@U"BEGINDISPATCH(classID,baseClassID)"
 *	@I
 *
 *		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.
 */

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

/*	@F"BEGINDISPATCHPROC"
 *	@H"XEvent.h"
 *	@U"BEGINDISPATCHPROC(classID,baseClassID,dispatchProcPtr)"
 *	@I
 *
 *		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.
 */

#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[] = {					

/*	@F"DISPATCHTABLE"
 *	@H"XEvent.h"
 *	@U"DISPATCHTABLE(messageID,messageProc)"
 *	@I
 *
 *		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
 */

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


/*	@F"ENDDISPATCH"
 *	@H"XEvent.h"
 *	@U"ENDDISPATCH"
 *	@I
 *
 *		Ends the event dispatch table declared using BEGINDISPATCH or
 *	BEGINDISPATCHPROC
 */

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

/*	@F"DECLAREDISPATCH"
 *	@H"XEvent.h"
 *	@U"DECLAREDISPATCH"
 *	@I
 *
 *		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.
 */

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

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

/*	@C"XGDispatch"
 *	@H"XEvent.h"
 *	@I
 *
 *		This is the universal receiver; this is the thing which receives
 *	all messages that are broadcast, sent or dispatched through the
 *	current focus
 */

class XGDispatch {
	public:
								XGDispatch(bool root = false);
		virtual					~XGDispatch();
		
		/*
		 *	focus
		 */
		
		virtual bool			SetFocus();
		bool					HasFocus();
		
		virtual bool			CanGainFocus();
		virtual bool			CanLoseFocus();
		virtual void			GainFocus();
		virtual void			LoseFocus();
		
		/*
		 *	message dispatch
		 */
		
		long					DoDispatch(long,long,void *,bool par = true);
		
		/*
		 *	Initialization Support
		 */
		
		// @G"Initialization Support"
		
		/*	@F"XGDispatch::GetCTorDispatch" @I
		 *
		 *		This returns the current dispatch object that will
		 *	be used as the parent for the next constructed XGDispatch
		 *	object.
		 */
		
		static XGDispatch		*GetCTorDispatch()
									{
										return gParent;
									}

		/*	@F"XGDispatch::SetCTorDispatch" @I
		 *
		 *		This sets the dispatch object that will be used as the
		 *	parent for the next constructed XGDispatch object.
		 */
		
		static void				SetCTorDispatch(XGDispatch *d)
									{
										gParent = d;
									}
									
		/*	@F"XGDispatch::SetFocusDispatch" @I
		 *
		 *		This sets the current focus object which owns this
		 *	dispatch object.
		 */
		
		void					SetFocusDispatch(XGFocus *f)
									{
										fFocus = f;
									}
									
		/*	@F"XGDispatch::SetDispatchParent" @I
		 *
		 *		This sets the parent of this dispatch object ot the
		 *	parent object provided..
		 */
		
		void					SetDispatchParent(XGDispatch *p)
									{
										fParent = p;
									}
		
		/*
		 *	Dispatch table internal stuff
		 */
		
		DECLAREDISPATCH
		
	private:
		static XGDispatch		*gParent;		// default parent for CTor
		
		XGDispatch				*fParent;		// my parent object
		XGFocus					*fFocus;		// the focus object
		XGPDynArray<XGSend>		fSend;			// stuff attached to me
	
	friend class XGSend;
};

/************************************************************************/
/*																		*/
/*	Focus Management													*/
/*																		*/
/************************************************************************/

/*	@C"XGFocus"
 *	@H"XEvent.h"
 *	@I
 *
 *		This is the object which provides focus support for a collection
 *	of dispatch objects.
 *
 *		Okay, here's the deal. If you have multiple windows up, each
 *	window can have it's own focus. Further, there is a "focus" when
 *	no window is present. The way I deal with this is to make each
 *	window and/or the application object itself contain a pointer to
 *	the current focus. The focus that is then used is found by the
 *	application by testing to see which is the frontmost window.
 *
 *		As this is assumed to be associated with the frontmost window
 *	or application, each focus object is it's own default focus.
 */

class XGFocus : public XGDispatch {
	public:
								XGFocus();
		virtual					~XGFocus();
		
		/*
		 *	Focus manipulation
		 */
		
		// @G"Focus Manipulation"
		
		void					_SetFocus(XGDispatch *x)
									{
										fFocus = x;
									}
		void					_ClearFocus()
									{
										fFocus = this;
									}
									
		/*	@F"XGFocus::GetFocus"
		 *	@I
		 *		This returns the current keyboard focus being tracked
		 *	by this focus object. Note that the application and each
		 *	window tracks a different "focus", but only one--the one in
		 *	the frontmost window or in the application if no windows
		 *	exist is used as the actual keyboard focus.
		 */
		
		XGDispatch				*GetFocus()
									{
										return fFocus;
									}
		
		/*
		 *	Send messages to the current focus of this branch
		 */
		
		long					SendDispatch(long msg, long arg, void *parg);
	private:
		XGDispatch				*fFocus;
};

/************************************************************************/
/*																		*/
/*	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;
};

/*	@C"XGSend"
 *	@H"XEvent.h"
 *	@I
 *
 *		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;
};

/*	@C"XGBroadcast"
 *	@H"XEvent.h"
 *	@I
 *
 *		This is the class for sending global broadcasts. This is basically
 *	a system-wide 'XGSend' object.
 */

class XGBroadcast {
	public:
		/*
		 *	Message dispatch
		 */
		
		// @G"Public methods"
		
		/*	@F"XGBroadcast::AddBroadcast"
		 *	@I
		 *		Adds the specified dispatch object to the list of
		 *	dispatch objects which will receive broadcasted messages.
		 */
		
		static void				AddBroadcast(XGDispatch *r)
									{
										gSend.AddReceiver(r);
									}
		
		/*	@F"XGBroadcast::RemoveBroadcast" @I
		 *
		 *		Removes the specified dispatch object from the list
		 *	of objects which will receive broadcasted messages.
		 */
		
		static void				RemoveBroadcast(XGDispatch *r)
									{
										gSend.RemoveReceiver(r);
									}
									
		/*	@F"XGBroadcast::SendBroadcast" @I
		 *
		 *		Actually broadcasts a message to all receivers which
		 *	are receiving globally broadcast messages
		 */
		
		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;
};


#endif // PandaWave_XEVENT
