/*	XGAppCore.cpp
 *
 *		This is the application core class. This only defines the common
 *	routines which are not substantially different across operating systems.
 *	OS specific routines are contained in the various XGAppCore_XXX.cpp
 *	source files.
 */

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

  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 <XError.h>
#include <XMessages.h>
#include <XAppMenu.h>
#include <XEvent.h>
#include <XApplication.h>
#include <XWindow.h>
#include <XFile.h>

#include <XDialog.h>
#include <XTextControls.h>
#include <XButtons.h>
#include <XPopup.h>
#include <XList.h>
#include <XIcon.h>

#if OPT_MACOS
#include "MacFloater.h"
#endif

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

short XGAppCore::gLastMods;
XGAppCore *XGAppCore::gApplication = NULL;

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

/*$	XGAppCore::XGAppCore
 *
 *		Construct the application core object. This initializes the
 *	operating system and loads the initial windows.
 */

XGAppCore::XGAppCore()
{
	Assert(gApplication == NULL);
	gApplication = this;
	fModalWindow = NULL;
	fEventOwner = NULL;
	gLastMods = 0;
	
	XGDirectory::InitDirectoryInfo();	// Save my CWD to find app root.
	
	InitOS();							// Initialize OS
	RegisterWindow(XGWindow);			// add predefined window to me.
	RegisterView(XGView);				// add predefined view to me.
	
	/* Required for error handling alerts */
	RegisterView(XGStaticText);
	RegisterView(XGCommandButton);
	RegisterView(XGEditText);
	RegisterView(XGCommandButton);
	RegisterView(XGIcon);
	RegisterView(XGPopControl);
	RegisterView(XGListControl);
	RegisterWindow(XGDialog);
	
#if OPT_WINOS
	fMutex = NULL;
	_GUpActive = 0;
#endif
}

/*$	XGAppCore::~XGAppCore
 *
 *		Shut me down. This is normally automatically called by YAAF when
 *	the application is quit.
 */

XGAppCore::~XGAppCore()
{
	KillOS();
	
	if (gApplication == this) gApplication = NULL;
	
#if OPT_WINOS
	::CloseHandle(fMutex);
#endif
}

/************************************************************************/
/*																		*/
/*	Dispatched event processing											*/
/*																		*/
/************************************************************************/

/*$	XGAppCore::GetParentDispatch
 *
 *		This is the override of the XGDispatch::GetParentDispatch
 *	method which indicates that if the XGAppCore object doesn't handle
 *	the message, it's not handled.
 */

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

/*$	XGAppCore::AdjustFocus
 *
 *		This adjusts the focus, fixing things up when windows are
 *	moved around and such.
 */

void XGAppCore::AdjustFocus()
{
	XGDispatch *focus;
	XGView *view;
	XGWindow *window;
#if OPT_WINOS
	HWND hFocus;
#endif
	
	/*
	 *	Find who has the focus
	 */
	
	focus = GetFocus();
	view = dynamic_cast<XGView *>(focus);
	if (view != NULL) {
		window = view->GetWindow();
		
		/*
		 *	Focus belongs to a control inside of a window.
		 *	Determine if the window is active or a floater
		 */
		
		if (window->GetWindowAttributes() & KAttrFloater) return;
		if (window == fModalWindow) return;
		if (window == XGWindow::GetFrontWindow()) return;
	}
	
	window = XGWindow::GetFrontWindow();
	if (window == NULL) {
		/*
		 *	Set the focus to me, cause there are no windows
		 */
		
		SetFocus();					// does nothing if I already had focus
	} else {
		/*
		 *	Set the focus to the view that was noted for this window, or
		 *	to the window if the focus was never set. If the previous
		 *	focus is no longer visible, then set to the window instead.
		 */
		
		if (window->fFocus->IsVisible()) {
			window->fFocus->SetFocus();
		} else {
			window->SetFocus();
		}
	}
	
#if OPT_WINOS
	/*
	 *	(Windows only)
	 *
	 *		Resolve the current windows focus to match the current focus
	 *	state of my environment.
	 */
	
	hFocus = ::GetFocus();
	if (hFocus == NULL) return;		// Huh?
	
	if (NULL != (view = (XGView *)::GetProp(hFocus,_GControlClass))) {
		/* Yank the library focus here */
		view->SetFocus();
	} else if (NULL != (window = (XGWindow *)::GetProp(hFocus,_GWindowClass))) {
		/* Yank Window's focus to the one who has focus */
		view = dynamic_cast<XGView *>(GetFocus());
		if (view) {
			if (view->GetRootWindow() != hFocus) {
				::SetFocus(view->GetRootWindow());
			}
		}
	}
#endif
}

/************************************************************************/
/*																		*/
/*	Application Event Processing										*/
/*																		*/
/************************************************************************/

/*$	XGAppCore::Run
 *
 *		Run the application event loop. Basically this calls 'RunOnce'
 *	until quit. This is normally called internally by YAAF.
 */

void XGAppCore::Run()
{
	register XGWindow *w;
	
	//OpenCopyright();					// Open copyright window
	fQuitFlag = false;
	//CloseCopyright();					// close copyright window
	
	try {
		AdjustFocus();
		/* this call was moved here because I was seeing crashes
		 * on Windows when there was no window.
		 */
		GetAppArgs();						// and get arguments in X and Windows
		XGBroadcast::SendPostBroadcast();
		XGBroadcast::SendBroadcast(KXGAppPostEvent,0,NULL);
		XGPeriodic::SendPeriodic(true);
	}
	catch (XGError &e) {
		e.DisplayError();
	}
	catch (...) {
		Error(KXGRandomException);
	}
	
	for (;;) {
		while (!fQuitFlag) RunOnce(NULL);

		try {
			while (NULL != (w = XGWindow::GetFrontWindow())) {
				if (!(w->CloseWindow())) {
					fQuitFlag = false;
					break;
				}
			}
			if (w == NULL) break;		// done closing all windows
		}
		catch (XGError &e) {
			e.DisplayError();
			fQuitFlag = false;
		}
		catch (...) {
			Error(KXGRandomException);
			fQuitFlag = false;
		}
	}
}

/*$	XGAppCore::Exit
 *
 *		Handle exit. This simply sets the 'quit' flag. This is called
 *	by your application if you want to shut down the application
 *	gracefully, though normally it's called internally by YAAF in
 *	response to a Quit menu command.
 */

void XGAppCore::Exit()
{
	fQuitFlag = true;
}

/*$	XGAppCore::DoPreEvent
 *
 *		Handle the stuff that occurs before an event is executed
 */

void XGAppCore::DoPreEvent()
{
	XGPeriodic::SendPeriodic(false);
	XGBroadcast::SendBroadcast(KXGAppPreEvent,0,NULL);
	XGDispatch::SendDispatch(KXGAppPeriodic,0,NULL);
}

/*$	XGAppCore::DoPostEvent
 *
 *		Handle the stuff that occurs before an event is executed
 */

void XGAppCore::DoPostEvent()
{
	AdjustFocus();
	XGBroadcast::SendPostBroadcast();
	XGBroadcast::SendBroadcast(KXGAppPostEvent,0,NULL);
	XGPeriodic::SendPeriodic(true);
}

/*$	XGAppCore::RunOnce
 *
 *		Run the event loop once. This sends all of the periodic
 *	events that are processed by YAAF, and then processes one pass
 *	through the event loop.
 */

void XGAppCore::RunOnce(XGWindow *w)
{
	/*
	 *	Set the modal window flag
	 */
	
	fModalWindow = w;
		
	try {
		/*
		 *	Send all the periodic events that need to be sent.
		 */
		
		DoPreEvent();
		
		/*
		 *	Now call the OS-specific event loop
		 */
		
		DoEvent();
		
		/*
		 *	Send the post events
		 */
		
		DoPostEvent();
	}
	catch (XGError &e) {
		e.DisplayError();
	}
	catch (...) {
		Error(KXGRandomException);
	}
	
	/*
	 *	Clear the modal flag and return
	 */
	
	fModalWindow = NULL;
}

/*$	XGAppCore::FilterEvent
 *
 *		This method is called before an event is processed by YAAF, and
 *	gives your application the opportunity to capture and filter events
 *	before YAAF does. If this method returns 'false', the event was not
 *	processed, and YAAF will process it. If this returns 'true', the
 *	event was processed, and YAAF will ignore the event.
 */

bool XGAppCore::FilterEvent(void *)
{
	return false;
}


/************************************************************************/
/*																		*/
/*	Application Event Processing										*/
/*																		*/
/************************************************************************/

/*
 *	Dispatch table
 */

BEGINDISPATCH(XGAppCore,XGDispatch)
	DISPATCHTABLE(KXGGetMenuStatus,XGAppCore::DoGetMenuStatus)
	DISPATCHTABLE(KXGDoMenuCommand,XGAppCore::DoMenuCommand)
	DISPATCHTABLE(KXGQuitApplication,XGAppCore::DoQuitApplication)
ENDDISPATCH

/*$	XGAppCore::DoGetMenuStatus
 *
 *		Get the menu status for this menu item. This processes the
 *	standard menu commands.
 */

long XGAppCore::DoGetMenuStatus(long arg, void *parg)
{
	XGSMenuStatus *m = (XGSMenuStatus *)parg;
	
	if (IsModalWindow()) return -1;
	switch (arg) {
		case KXGAppAboutMe:
		case KXGFileQuit:
			m->enable = true;
			return 0;
		case KXGFileClose:
			m->enable = (XGWindow::GetFrontWindow() != NULL) ? true : false;
			return 0;

#if OPT_MACOS == 1
		case KXGEditUndo:
		case KXGEditCut:
		case KXGEditCopy:
		case KXGEditPaste:
		case KXGEditClear:
			m->enable = IsSystemMenu(arg);
			return 0;
#endif

		default:
			return -1;
	}
}

/*$	XGAppCore::DoMenuCommand
 *
 *		Do the menu command. This processes the standard menu commands,
 *	such as close window, do about me, and quit.
 */

long XGAppCore::DoMenuCommand(long arg, void *)
{
	XGWindow *w;
	
	if (IsModalWindow()) return -1;
	switch (arg) {
		case KXGAppAboutMe:
			ShowCopyright();
			return 0;
			
		case KXGFileQuit:
			XGDispatch::SendDispatch(KXGQuitApplication,0,NULL);
			return 0;

		case KXGFileClose:
			w = XGWindow::GetFrontWindow();
			if (w) w->CloseWindow();
			return 0;

#if OPT_MACOS == 1
		case KXGEditUndo:
		case KXGEditCut:
		case KXGEditCopy:
		case KXGEditPaste:
		case KXGEditClear:
			if (DoSystemMenu(arg)) return 0;
			return -1;
#endif

		default:
			return -1;
	}
}

/*$	XGAppCore::DoQuitApplication
 *
 *		Handle quit application message
 */

long XGAppCore::DoQuitApplication(long,void *)
{
	XGWindow *w;
	
	while (NULL != (w = XGWindow::gList)) {
		if (!w->CloseWindow()) return -1;
	}
	Exit();
	return 0;
}

/************************************************************************/
/*																		*/
/*	Macintosh Desktop Routines											*/
/*																		*/
/************************************************************************/

#if OPT_MACOS == 1

/*	XGAppCore::IsSystemMenu
 *
 *		Does this belong to the system menu?
 */

bool XGAppCore::IsSystemMenu(short id)
{
	WindowPtr w;
	
	w = MFrontWindow();
	if (((WindowPeek)w)->windowKind > 0) return false;
	switch (id) {
		case KXGEditUndo:
		case KXGEditCut:
		case KXGEditCopy:
		case KXGEditPaste:
		case KXGEditClear:
			return true;
	}
	return false;
}

/*	XGAppCore::DoSystemMenu
 *
 *		Does this belong to the system menu?
 */

bool XGAppCore::DoSystemMenu(short id)
{
	switch (id) {
		case KXGEditUndo:
			return SystemEdit(0);
		case KXGEditCut:
			return SystemEdit(2);
		case KXGEditCopy:
			return SystemEdit(3);
		case KXGEditPaste:
			return SystemEdit(4);
		case KXGEditClear:
			return SystemEdit(5);
	}
	return false;
}

#endif
