/*	XGDocument.cpp
 *
 *		The basic document processing class
 */

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

#include <XConfig.h>			// must be first to optimize compile on MSVC++
#include <XDocument.h>
#include <XWindow.h>
#include <XForm.h>
#include <XMessages.h>
#include <XAppMenu.h>

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

// @G"Construction/Destruction"

/*	@F"XGDocument::XGDocument" @I
 *
 *		Create this
 */

XGDocument::XGDocument()
{
	fChanged = false;
	
	XGBroadcast::AddBroadcast(this);		// I want periodic events
}

/*	@F"XGDocument::~XGDocument" @I
 *
 *		Delete this
 */

XGDocument::~XGDocument()
{
	int i,len;
	
	len = fWindowList.Length();
	for (i = 0; i < len; i++) {
		delete fWindowList[i].fWindow;
	}
}

/************************************************************************/
/*																		*/
/*	Window Support														*/
/*																		*/
/************************************************************************/

// @G"Window Management"

/*	@F"XGDocument::AttachWindow" @I
 *
 *		Attach to this window.
 *
 *		the 'sdf' parameter is true if this is a 'sub-document editor'.
 *
 *		The idea is this. There are two types of windows which are
 *	associated with a document; a 'top level' editor window which
 *	is associated with the document, and a 'sub editor' which only
 *	views part of that document. This permits both the 'every window
 *	is a snapshot into a document' (such as used by text editors), and
 *	the 'window edits only one part of a document' model, such as that
 *	used by ResEdit.
 *
 *		When all the top level windows into a document is closed, the
 *	document destroys itself, and all the 'subwindows' that are also
 *	open; this is the same behaviour as used by ResEdit.
 *
 *		Note: there needs to be at least one top level editor window.
 *
 *		Also note: while the document is created first, and the document
 *	pointer is provided to the window, the window must be explicitly 
 *	attached to the document.
 */

void XGDocument::AttachWindow(XGWindow *w, bool sdf)
{
	_DocRecord dw;
	
	dw.fWindow = w;
	dw.fSubDoc = sdf;
	fWindowList += dw;
	
	/*
	 *	Add window to list of broadcast receivers
	 */
	
	AddReceiver(w);
}


/*	@F"XGDocument::DetachWindow" @I
 *
 *		Search and delete this window
 */

void XGDocument::DetachWindow(XGWindow *w)
{
	int i;
	int len;
	bool f;
	
	/*
	 *	Remove from my broadcaster
	 */
	
	RemoveReceiver(w);
	
	/*
	 *	Remove this list
	 */
	
	len = fWindowList.Length();
	for (i = 0; i < len; i++) if (fWindowList[i].fWindow == w) break;
	if (i >= len) return;
	f = fWindowList[i].fSubDoc;
	fWindowList.Delete(i);
	
	/*
	 *	Scan and delete if no edit windows are there
	 */
	
	if (!f) {
		/* Deleted editor object */
		len = fWindowList.Length();
		for (i = 0; i < len; i++) if (!fWindowList[i].fSubDoc) break;
		if (i >= len) delete this;				// no more editors
	}
}

/*	@F"XGDocument::GetNumAttachWindows" @I
 *
 *		Get the number of attached windows in this thing
 */

long XGDocument::GetNumAttachWindows()
{
	return fWindowList.Length();
}

/*	@F"XGDocument::GetAttachedWindow" @I
 *
 *		Get this attached window
 */

XGWindow *XGDocument::GetAttachedWindow(long index)
{
	return fWindowList[index].fWindow;
}

/*	@F"XGDocument::LastEditWindow" @I
 *
 *		This returns true if there is only one non-fSubDoc window left
 */

bool XGDocument::LastEditWindow(XGWindow *w)
{
	int i;
	int len;
	int count;
	bool f;
	
	f = false;
	count = 0;
	len = fWindowList.Length();
	for (i = 0; i < len; i++) {
		if (fWindowList[i].fWindow == w) f = fWindowList[i].fSubDoc;
		if (!fWindowList[i].fSubDoc) count++;
	}
	return (!f && (count == 1));
}

/************************************************************************/
/*																		*/
/*	Contents Changed Support											*/
/*																		*/
/************************************************************************/

// @G"Document message wrappers"

/*	@F"XGDocument::DocumentRenamed" @I
 *
 *		Send the document renamed message to all interested receivers
 */

void XGDocument::DocumentRenamed(void)
{
	XGSDocEvent ev;
	
	ev.doc = this;
	SendMessage(KXGEventDocRenamed,0,(void *)&ev);
}

/*	@F"XGDocument::DocumentSaved" @I
 *
 *		Send the document savedc message to all interested receivers
 */

void XGDocument::DocumentSaved(void)
{
	XGSDocEvent ev;
	
	ev.doc = this;
	SendMessage(KXGEventDocSaved,0,(void *)&ev);
}

/*	@F"XGDocument::DispatchChanged" @I
 *
 *		Run through and notify all the windows that the contents changed
 */

void XGDocument::DispatchChanged(void)
{
	XGSDocEvent ev;
	
	ev.doc = this;
	SendMessage(KXGEventDocChanged,0,(void *)&ev);
}

/************************************************************************/
/*																		*/
/*	Dispatch Support													*/
/*																		*/
/************************************************************************/

BEGINDISPATCH(XGDocument,XGDispatch)
	DISPATCHTABLE(KXGGetMenuStatus,XGDocument::DoGetMenuStatus)
	DISPATCHTABLE(KXGDoMenuCommand,XGDocument::DoMenuCommand)
	DISPATCHTABLE(KXGAppPostEvent,XGDocument::DoPostPeriodic)
ENDDISPATCH

/*	XGDocument::DoGetMenuStatus
 *
 *		Handle the save/restore menu items
 */

long XGDocument::DoGetMenuStatus(long arg, void *parg)
{
	XGSMenuStatus *gm;

	/*
	 *	Handle enabling those I can handle
	 */
	
	gm = (XGSMenuStatus *)parg;
	if (arg == KXGFileSaveAs) {
		gm->enable = true;
		return 1;
	}
	if (arg == KXGFileSave) {
		if (IsDocumentDirty()) gm->enable = true;
		return 1;
	}
	if (arg == KXGFileRevert) {
		if (IsDocumentDirty() && IsDocumentNamed()) gm->enable = true;
		return 1;
	}
	
	return -1;
}

/*	XGDocument::DoMenuCommand
 *
 *		Handle the save/restore menu items
 */

long XGDocument::DoMenuCommand(long arg, void *)
{
	/*
	 *	Handle the following menu commands:
	 *
	 *		Quit/Close/About
	 *		Undo/Cut/Copy/Paste/Clear (desk accessories)
	 */
	
	if (arg == KXGFileSave) {
		if (IsDocumentDirty()) {
			DoDocumentSave();
			return 1;
		}
	}
	if (arg == KXGFileSaveAs) {
		DoDocumentSaveAs();
		return 1;
	}
	if (arg == KXGFileRevert) {
		if (IsDocumentDirty() && IsDocumentNamed()) {
			DoDocumentRevert();
			return 1;
		}
	}
	
	return -1;
}

/*	XGDocument::DoPostPeriodic
 *
 *		Receive the post periodic broadcast--by seeing if the
 *	document has changed; if so, send a _DispatchChanged global
 */

long XGDocument::DoPostPeriodic(long, void *)
{
	if (fChanged) {
		fChanged = false;
		DispatchChanged();
	}
	return 0;
}

