/*	XApplication.h
 *
 *		This contains the various declarations for the different
 *	types of application that can be built with the YAAF library
 */

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

  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_XAPPLICATION
#define PandaWave_XAPPLICATION

#include <XConfig.h>
#include <XEvent.h>
#include <XTypes.h>

#if OPT_XWIN
#include <X11/Xlib.h>
#define XK_MISCELLANY
#include <X11/keysymdef.h>
#endif

/**$name Application Event Engine
 *
 *		These classes provide the basic application handling object.
 *
 *		There are two classes which you should derive your own application
 *	object from. Which one you use depends on if you are writing a single
 *	window application, or a multiple window application.
 *
 *		Both classes provide similar functionality. The difference is that
 *	the multiple window application object also provides Window menu
 *	processing, while the single window application automatically quits
 *	when the window is closed.
 */
 
//@{

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

class XGAppCore;
class XGView;
class XGWindow;
class XGPopWindow;
class XGMenuBar;
class XGEventOwner;

#if OPT_MACOS
extern "C" void XGRDoActivate(WindowPtr,short);
#endif

/************************************************************************/
/*																		*/
/*	Application entry point												*/
/*																		*/
/************************************************************************/

/*$	StartApplication
 *
 *		This is the user-provided routine which marks the start of a
 *	YAAF application. What this routine does depends on if you are
 *	creating a single-window application or a multiple window application.
 *
 *		If you are creating a multiple window application, you simply
 *	need to create your application object and return:
 *
\begin{verbatim}

		XGAppCore *StartApplication()
		{
			return new CMyApplication;
		}

\end{verbatim}
 *
 *		If you are creating a single window application, it's a hair
 *	more tricky. Instead, you would write:
 *
\begin{verbatim}

		XGAppCore *StartApplication()
		{
			CMyApplication *app;
			XGWindow *w;
			
			app = new CMyApplication;
			w = XGWindow::CreateWindow(KMyWindow);
			return app;
		}

\end{verbatim}
 *
 */

extern XGAppCore *StartApplication(void);

/************************************************************************/
/*																		*/
/*	Class Declarations													*/
/*																		*/
/************************************************************************/

/*$	XGAppCore
 *
 *		This encapsulates the core event code for the YAAF application.
 *	Your application class would be declared as a descendant of the
 *	XGAppSingleWindow or XGAppMultiWindow classes instead of this one.
 */

class XGAppCore : public XGDispatch {
	public:
								XGAppCore();
		virtual					~XGAppCore();
		
		/*
		 *	Current application
		 */
		
		/*$	XGAppCore::GetApplication
		 *
		 *		Returns a pointer to the currently created application
		 *	object.
		 */
		
		static XGAppCore		*GetApplication()
									{
										return gApplication;
									}
		
		/*
		 *	Event processing
		 */
				
		virtual void			Run();
		virtual void			Exit();
		void					RunOnce(XGWindow *w);
		
		virtual bool			FilterEvent(void *eptr);
		
		/*$	XGAppCore::WaitCheck
		 *
		 *		Static method for running the event loop once.
		 *	Returns 'true' if the cancel key was selected. This
		 *	permits you to run long 'wait' periods while providing
		 *	the user the option to cancel the operation.
		 */
		
		static bool				WaitCheck()
									{
										return gApplication->CancelCheck();
									}
		bool					CancelCheck();

		/*$	XGAppCore::IsModalWindow
		 *
		 *		Returns true if the event loop is currently executing
		 *	in behalf of modal window dialog processing. This is usually
		 *	used internally by YAAF to determine if a dialog box is
		 *	running.
		 */
		
		bool					IsModalWindow()
									{
										return (NULL != fModalWindow);
									}

		/*$	XGAppCore::WaitCheck
		 *
		 *		Returns the modifier flags associated with the last
		 *	keyboard or mouse event processed.
		 */
		
		static short			GetLastModifiers()
									{
										return gLastMods;
									}
									
		/*$	XGAppCore::GetLastEvent
		 *
		 *		Gets the system-dependant event record associated with
		 *	the last event processed (or the event currently being processed)
		 */
		
		static void				*GetLastEvent()
									{
										return (void *)&gLastMessage;
									}
		
		
		/*
		 *	Basic global menus
		 */
		
		DECLAREDISPATCH
		long					DoMenuCommand(long,void *);
		long					DoGetMenuStatus(long,void *);
		long					DoQuitApplication(long,void *);
		
		/*
		 *	copyright support
		 */
		
		/*$	XGAppCore::ShowCopyright
		 *
		 *		This routine is called internally by YAAF in response to
		 *	the "About" menu command. This by default shows a copyright
		 *	dialog box, and returns when the box is closed. Override this
		 *	routine if you want to provide your own custom copyright box.
		 */
		
		virtual void			ShowCopyright();		// in reply to event

		/*$	XGAppCore::OpenCopyright
		 *
		 *		This routine is called internally by YAAF when the
		 *	application first starts up. This opens up a temporary
		 *	copyright window, and closes it again when CloseCopyright()
		 *	is called. By default this presents a picture on the main
		 *	window. Override if you want to provide your own custom
		 *	copyright dialog box, or if you want to get rid of it.
		 */
		
		virtual void			OpenCopyright();		// open first window

		/*$	XGAppCore::CloseCopyright
		 *
		 *		This routine is called internally by YAAF after
		 *	initialization, to close the copyright dialog box that
		 *	was opened by the OpenCopyright method above. Override
		 *	this if you want to provide your own custom dialog box
		 *	handling, or if you want to get rid of it.
		 */
		
		virtual void			CloseCopyright();		// close first window

	protected:
		/*
		 *	Menu focus support routines
		 */
		
		virtual XGMenuBar		*GetCurMenu() = 0;
		XGDispatch				*GetParentDispatch();
		void					AdjustFocus();

		XGWindow				*fEventOwner;
		XGWindow				*fModalWindow;
		
#if OPT_WINOS
		/*
		 *	A MS Windows specific thing: prevent multiple instances
		 *	of an application from launching. Call this from the
		 *	constructor if there is only one instance of this app.
		 */
		
		void					MarkSingleInstance(char *name);	
		
		/*
		 *	I also need to note my app's name for the purpose
		 *	of handling open/print shell commands
		 */
		
		void					DDEAppName(char *app, char *topic = NULL);
#endif

	private:
		static XGAppCore		*gApplication;
		static short			gLastMods;
		bool					fQuitFlag;
		
		/*
		 *	Common Internals
		 */
		
		void					InitOS();
		void					KillOS();
		void					GetAppArgs();
		void					DoEvent();
		
		void					DoPreEvent();
		void					DoPostEvent();
		
#if OPT_MACOS == 1
		/*
		 *	Macintosh specific stuff
		 */
		
		XPoint					fLastClick;
		long					fLastTime;
		
		bool					fAppleEvents;
		static EventRecord		gLastMessage;
		
		/*
		 *	Macintosh routines
		 */
		
		bool					IsSystemMenu(short);
		bool					DoSystemMenu(short);
		
		/*
		 *	Event management
		 */
		 		
		void					GetWindowMinMax(WindowPtr,XRect *);
		void					SetWindowZoom(WindowPtr);
		void					DrawGrowIcon(WindowPtr);
		short					GetEventFlags(EventRecord &);
		short					GetKeyCode(EventRecord &);
		bool					DoMenuKey(char);
		void					DoMenuClick(XPoint);
		void					DoWindowKey(EventRecord &);
		void					DoActivate(WindowPtr,bool);
		void					DoUpdate(WindowPtr);
		void					DoSizeContents(WindowPtr);
		void					DoWindowClick(WindowPtr,EventRecord &);
		void					DoCloseWindow(WindowPtr);
		void					DoMouseClick(EventRecord &);
		void					DoMouseMove(EventRecord &);
		
		void					RunUpdateOnce();
		
		void					RunEventLoop(void);
		
		void					InitOS6Events(void);
		void					InitAppleEvents(void);
		static OSErr 			GotRequiredParams(const AppleEvent *);
		static pascal OSErr		AEHandleProc(const AppleEvent *event, 
											 AppleEvent *retval, 
											 long refNum);
		
	friend void XGRDoActivate(WindowPtr,short);
#endif

#if OPT_XWIN
		/*
		 *	Event management for X
		 */
		
		static XEvent			gLastMessage;
		void					ArrangeOrder();
		short					GetSpecialKey(KeySym);
		short					DeriveFlags(long);
		static Window			GetParentWindow(Window);
		static void				UpdateWindows();
#endif

	/*
	 *	WINPROC access
	 */
	 
#if OPT_WINOS
		HANDLE					fMutex;
		static MSG				gLastMessage;
		static XGView			*gCaptureView;
		
		static short 			GetExtendedKeyCode(WPARAM wp, LPARAM lp);
		static short 			GetStateFlags();
		static long				ProcKeyboardCommon(UINT,WPARAM,LPARAM);
		static long				ProcXGWindowCommon(XGWindow *,UINT,WPARAM,LPARAM);

	friend LRESULT CALLBACK _GToolBarWindowProc(HWND, UINT, WPARAM, LPARAM);
	friend LRESULT CALLBACK _GMainMDIWindowProc(HWND, UINT, WPARAM, LPARAM);
	friend LRESULT CALLBACK _GChildWindowProc(HWND, UINT, WPARAM, LPARAM);
	friend LRESULT CALLBACK _GSingleWindowProc(HWND, UINT, WPARAM, LPARAM);
#endif
	
	friend class XGEventOwner;
	friend class XGWindow;
	friend class XGPopWindow;
	friend class XGToolbarView;
	friend class XGDragger;
};

/*
 *	Internal globals
 */

#if OPT_WINOS

extern UINT _GUpActive;			// Windows message to activate this application

#endif

#if OPT_MACOS

extern long _GMacOSVersion;		// Macintosh; version of MacOS I'm running
extern long	_GAppearance;		// Macintosh; version of Appearance Manager API
extern bool _GAppFrontmost;		// Macintosh; is application frontmost?
extern bool _GHasWNETrap;		// Macintosh; has WaitNextEvent() trap?
extern bool	_GHasFSSpec;		// Macintosh; has FSSpec specifier calls?
extern bool	_GHasDragDrop;		// Macintosh; Macintosh Drag Manager present?

#endif

#if OPT_WINOS || OPT_XWIN

/*	XGEventOwner
 *
 *		A small stack-based class which assists in noting the owner
 *	of an event (for Windows and XWindows applications). This is
 *	created during the processing of an event owned by a window,
 *	which lets my application core know where the event should be
 *	routed to.
 */

class XGEventOwner {
	public:
		XGEventOwner(XGWindow *w)
			{
				XGAppCore::GetApplication()->fEventOwner = w;
			}
		~XGEventOwner()
			{
				XGAppCore::GetApplication()->fEventOwner = NULL;
			}
};

#endif

/************************************************************************/
/*																		*/
/*	Single Window Application Type										*/
/*																		*/
/************************************************************************/

/*$	XGAppSingleWindow
 *
 *		This implements a single-window application. On Windows, this
 *	creates an SDI window application; on the Macintosh this creates
 *	an application which quits as soon as the window is closed.
 *
 *		The way this works is to create a single menu bar object
 *	(in Windows, in the opened window), and update the menu bar
 *	according to the messages received by 'ReceiveDispatch' (on the
 *	periodic events).
 *
 *		When the main window close event is received, we terminate
 *	the event loop and quit this program.
 */

class XGAppSingleWindow : public XGAppCore {
	public:
								XGAppSingleWindow();
		virtual					~XGAppSingleWindow();
		
		/*
		 *	Application initialization procedures
		 */
		
		void					Run(void);
		
		DECLAREDISPATCH
		long					DoWindowCreate(long, void *);
		long					DoWindowDestroy(long, void *);
		long					DoPostPeriodic(long, void *);

	protected:
		/*
		 *	Overrides--this determines how we handle the focus
		 */
		 
		virtual XGMenuBar		*GetCurMenu();

	private:
		XGWindow				*fMyWindow;
		XGMenuBar				*fMyMenuBar;
		
		/*
		 *	Initialization
		 */

#if OPT_WINOS == 1
		void					InitInstance();
		void					InitApplication();
#endif
};

/************************************************************************/
/*																		*/
/*	Multiple Window Application Type									*/
/*																		*/
/************************************************************************/

/*	XGLstRecord
 *
 *		This is an internal record used to record the list of windows that
 *	are in my application
 */

struct XGLstRecord {
	short						menuAccel;
	short						menuID;
	XGWindow					*fWindow;
	
#if OPT_WINOS || OPT_XWIN
	XGMenuBar					*fMenuBar;
#endif
};

/*$	XGAppMultiWindow
 *
 *		This implements a multi-window application. On Windows, this
 *	creates an MDI window application; on the Macintosh this creates
 *	the standard multi-window application
 *
 *		This works by creating a global menu bar, and maintaining both
 *	that menu bar and a global table of all the open windows. (This
 *	is used to maintain the window menu.)
 *
 *		This terminates when the quit command is issued. This may also
 *	quit when all the windows are closed, depending on the mode and
 *	the platform this is running under.
 *
 *		Macintosh:		App quits only on quit command
 *
 *		X:				App quits when all windows are closed
 *
 *		MS Windows:		If 'appName' is NULL, SDI application will
 *						quit when all windows are closed; otherwise,
 *						MDI app quits on quit command only.
 */

class XGAppMultiWindow : public XGAppCore {
	public:
								XGAppMultiWindow(char *appName = NULL);
		virtual					~XGAppMultiWindow();
		
		/*
		 *	Application initialization procedures
		 */
		
		DECLAREDISPATCH
		long					DoWindowCreate(long, void *);
		long					DoWindowDestroy(long, void *);
		long					DoWindowName(long, void *);
		long					DoGetMenuStatus(long, void *);
		long					DoMenuCommand(long, void *);
		long					DoPostPeriodic(long, void *);

	protected:
		/*
		 *	Overrides--this determines how we handle the focus
		 */
		 
		virtual XGMenuBar		*GetCurMenu();

	private:
		/*
		 *	Initialization
		 */

#if OPT_WINOS == 1
		void					InitMDIInstance(char *);
		void					InitMDIApplication();
		void					InitSDIInstance();
		void					InitSDIApplication();
		
		void					InitToolbar(short resID);
#endif

		/*
		 *	Support routines
		 */
		
		void					DoCreateWindow(XGWindow *);
		void					DoDestroyWindow(XGWindow *);
		void					DoRenameWindow(XGWindow *);
		
		void					SortWindowList();
		void					UpdateWindowMenu();
		void					UpdateWindowMenu(XGMenuBar *);

		
#if (OPT_MACOS == 1) || (OPT_WINOS == 1)
		void					TileWindows();
		void					CascadeWindows();
#endif

#if OPT_WINOS
		void					ArrangeIcons();
#endif

		/*
		 *	Internal structures
		 */
		
		XGSDynArray<XGLstRecord> fWindowList;
	
#if (OPT_MACOS == 1) || (OPT_WINOS == 1)
		XGMenuBar				*fMyMenuBar;
#endif
};

//@}

#endif // PandaWave_XAPPLICATION

