/*	XGAppMultiWindow.cpp
 *
 *		This creates a single-window application.
 */

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

  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 <XDataUtil.h>
#include <XCursorUtil.h>
#include <XWindow.h>
#include <XMenu.h>
#include <XMessages.h>
#include <XError.h>
#include <XApplication.h>
#include <XToolbar.h>
#include <XAppMenu.h>

/************************************************************************/
/*																		*/
/*	Support routines													*/
/*																		*/
/************************************************************************/

/*	ToUpper
 *
 *		My lower case to upper case routine
 */

static char ToUpper(char c)
{
	if ((c >= 'a') && (c <= 'z')) return c + ('A' - 'a');
	else return c;
}

/*	StringCompare
 *
 *		String compare: which is bigger and which is smaller
 */

static int StringCompare(char *a, char *b)
{
	while ((*a) && (ToUpper(*a) == ToUpper(*b))) a++, b++;
	
	if (*a == *b) return 0;
	if (ToUpper(*a) < ToUpper(*b)) return -1;
	return 1;
}

/*	WindowCompare
 *
 *		This compares these two windows
 */

static int WindowCompare(XGWindow *a, XGWindow *b)
{
	char bufa[256];
	char bufb[256];
	short la,lb;
	
	la = GETLOWORD(a->GetWindowAttributes());
	lb = GETLOWORD(b->GetWindowAttributes());
	if (la < lb) return -1;
	if (la > lb) return 1;

	a->GetTitle(bufa);
	b->GetTitle(bufb);
	return StringCompare(bufa,bufb);
}

/*	IsWindowMenuID
 *
 *		Is this in the range of my window menu identifiers?
 */

static bool IsWindowMenuID(long id)
{
	if (id == KXGWindowList) return 1;
	if ((id >= 0x7000) && (id < 0x8000)) return 1;
	return 0;
}

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

/*$	XGAppMultiWindow::XGAppMultiWindow
 *
 *		Create me. This initializes all of the internal data strucutres
 *	used for a multiple window application.
 *
 *		The #appName# parameter is ignored on the Macintosh and on
 *	X Windows. However, on Microsoft Windows, this is the string that
 *	is shown in the MDI frame window. If this is NULL, YAAF creates
 *	a SDI-style application with multiple windows on Microsoft Windows;
 *	this application will then quit if all of the SDI windows are
 *	closed.
 *
 *		For maximal portability, you should write your application so
 *	it functions correctly in both SDI and MDI mode. That means that
 *	you should create a new window when your application is started
 *	up, and reply to Open File messages. And you should be prepared
 *	to deal with your application shutting down when all fo the windows
 *	are closed.
 */

XGAppMultiWindow::XGAppMultiWindow(char *appName)
{
#if OPT_MACOS == 1
	#pragma unused(appName)

	fMyMenuBar = new XGMenuBar();
	fMyMenuBar->LoadFromResource(KXGDefaultMenu);
#endif

#if OPT_WINOS == 1
	if (appName) {
		/* MDI Framework Initialization */
		if (_GPrevInstance == NULL) InitMDIApplication();
		InitMDIInstance(appName);
	
		fMyMenuBar = new XGMenuBar(_GMainWindow,true);
		fMyMenuBar->LoadFromResource(KXGDefaultMenu);
	} else {
		/* SDI Framework Initialization */
		fMyMenuBar = NULL;
		if (_GPrevInstance == NULL) InitSDIApplication();
		InitSDIInstance();
	}
#endif

#if OPT_XWIN == 1
	#pragma unused(appName)
#endif
	
	XGBroadcast::AddBroadcast(this);
	
#if OPT_WINOS
	InitToolbar(128);					// try to create me.
#endif
}

/*$	XGAppMultiWindow:::~XGAppMultiWindow
 *
 *		Delete me. This is normally called internally by YAAF.
 */

XGAppMultiWindow::~XGAppMultiWindow()
{
#if OPT_MACOS || OPT_WINOS
	if (fMyMenuBar) delete fMyMenuBar;
#endif

#if OPT_XWIN || OPT_WINOS
	int i,len;
	
	len = fWindowList.Length();
	for (i = 0; i < len; i++) {
		if (fWindowList[i].fMenuBar) {
			delete fWindowList[i].fMenuBar;
		}
	}
#endif
}

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

/*
 *	Dispatch table
 */

BEGINDISPATCH(XGAppMultiWindow,XGAppCore)
	DISPATCHTABLE(KXGEventWindowCreate,XGAppMultiWindow::DoWindowCreate)
	DISPATCHTABLE(KXGEventWindowDestroy,XGAppMultiWindow::DoWindowDestroy)
	DISPATCHTABLE(KXGEventWindowName,XGAppMultiWindow::DoWindowName)
	DISPATCHTABLE(KXGGetMenuStatus,XGAppMultiWindow::DoGetMenuStatus)
	DISPATCHTABLE(KXGDoMenuCommand,XGAppMultiWindow::DoMenuCommand)
	DISPATCHTABLE(KXGAppPostEvent,XGAppMultiWindow::DoPostPeriodic)
ENDDISPATCH

/*	XGAppMultiWindow::DoWindowCreate
 *
 *		Handle window creation
 */

long XGAppMultiWindow::DoWindowCreate(long, void *parg)
{
	DoCreateWindow(((XGSWindowEvent *)parg)->window);
	return -1;
}

/*	XGAppMultiWindow::DoWindowDestroy
 *
 *		Handle window destruction
 */

long XGAppMultiWindow::DoWindowDestroy(long, void *parg)
{
	DoDestroyWindow(((XGSWindowEvent *)parg)->window);
	return -1;
}

/*	XGAppMultiWindow::DoWindowName
 *
 *		Handle window rename
 */

long XGAppMultiWindow::DoWindowName(long, void *parg)
{
	DoRenameWindow(((XGSWindowEvent *)parg)->window);
	return -1;
}

/*	XGAppMultiWindow::DoGetMenuStatus
 *
 *		Handle menu status message
 */

long XGAppMultiWindow::DoGetMenuStatus(long arg, void *parg)
{
	XGSMenuStatus *gs;
	long len,off;
	XGWindow *w;
		
	if (IsModalWindow()) return -1;				// skip if modal up
	
	gs = (XGSMenuStatus *)parg;
	w = XGWindow::GetFrontWindow();
	if (IsWindowMenuID(arg) && (arg != KXGWindowList)) {
		gs->enable = true;
		
		len = fWindowList.Length();
		for (off = 0; off < len; off++) {
			if (fWindowList[off].fWindow == w) {
				if (fWindowList[off].menuID == arg) {
					gs->check = true;
				}
				break;
			}
		}
		return 1;
	}
	switch (arg) {
		/* Handle tile/cascade stuff */
		case KXGWindowTile:
		case KXGWindowCascade:
#if OPT_WINOS
		case KXGWindowArrange:
			if (!_GMDIWindow) return -1;
#endif

#if OPT_MACOS || OPT_WINOS
			if (fWindowList.Length() > 0) {
				gs->enable = true;
				return 1;
			}
#endif
			break;
	}
	return -1;
}

/*	XGAppMultiWindow::DoMenuCommand
 *
 *		Handle menu command
 */

long XGAppMultiWindow::DoMenuCommand(long arg, void *)
{
	long len,off;

	if (IsModalWindow()) return -1;				// skip if modal up
	
	if (IsWindowMenuID(arg) && (arg != KXGWindowList)) {
		/* Find the window to enable */
		len = fWindowList.Length();
		for (off = 0; off < len; off++) {
			if (fWindowList[off].menuID == arg) {
				fWindowList[off].fWindow->ShowWindow();
				fWindowList[off].fWindow->SelectWindow();
				return 1;
			}
		}
	}
	switch (arg) {
#if (OPT_MACOS == 1) || (OPT_WINOS == 1)
		case KXGWindowTile:
			TileWindows();
			return 1;
		case KXGWindowCascade:
			CascadeWindows();
			return 1;
#endif

#if OPT_WINOS == 1
		case KXGWindowArrange:
			ArrangeIcons();
			return 1;
#endif
	}
	return -1;
}

/*	XGAppMultiWindow::DoPostPeriodic
 *
 *		Process postperiodic event to update menu bar
 */

long XGAppMultiWindow::DoPostPeriodic(long, void *)
{
	XGWindow *tw;
	short menuID;

#if OPT_XWIN || OPT_WINOS
	bool f;
	int i;
	int len;
	XGMenuBar *m;
#endif
	
	/*
	 *	Update the menu bar. On the Macintosh and on
	 *	Windows, there is only one menu bar.
	 */
	
#if (OPT_MACOS == 1) || (OPT_WINOS == 1)
	tw = XGWindow::GetFrontWindow();
	if (fMyMenuBar != NULL) {
		if (tw) {
			menuID = tw->GetMenuBarID();
			if (menuID == -1) return 0;
			if (menuID == 0) menuID = KXGDefaultMenu;
			fMyMenuBar->LoadFromResource((short)menuID);
#if OPT_WINOS == 1
			if (_GToolView) (dynamic_cast<XGToolbarView *>(_GToolView))->ChangeToolMenu(menuID);
#endif
		} else {
			fMyMenuBar->LoadFromResource(KXGDefaultMenu);
#if OPT_WINOS == 1
			if (_GToolView) (dynamic_cast<XGToolbarView *>(_GToolView))->ChangeToolMenu(KXGDefaultMenu);
#endif
		}
		UpdateWindowMenu();			// update the window
		fMyMenuBar->UpdateMenuBar(GetFocus());

#if OPT_WINOS == 1					// we could be an SDI app...
	} else {
		/*
		 *	Run through all the SDI windows
		 */
		
		len = fWindowList.Length();
		for (i = 0; i < len; i++) {
			m = fWindowList[i].fMenuBar;
			tw = fWindowList[i].fWindow;

			menuID = tw->GetMenuBarID();
			if (menuID == 0) menuID = KXGDefaultMenu;

			if (menuID != -1) {
				f = m->LoadFromResource(menuID);
				fEventOwner = tw;
				if (f) UpdateWindowMenu(m);
				if (tw->fFocus) m->UpdateMenuBar(tw->fFocus);
				else m->UpdateMenuBar(tw);
				fEventOwner = NULL;
			}
		}

#endif

	}
#endif

	/*
	 *	Under X Windows, there are multiple menu bars,
	 *	one for each document window. This updates all of 'em
	 */
	
#if OPT_XWIN == 1
	len = fWindowList.Length();
	for (i = 0; i < len; i++) {
		m = fWindowList[i].fMenuBar;
		tw = fWindowList[i].fWindow;

		menuID = tw->GetMenuBarID();
		if (menuID == 0) menuID = KXGDefaultMenu;

		f = m->LoadFromResource(menuID);
		fEventOwner = tw;
		if (f) UpdateWindowMenu(m);
		if (tw->fFocus) m->UpdateMenuBar(tw->fFocus);
		else m->UpdateMenuBar(tw);
		if (f) tw->RedrawMenuBar();
		fEventOwner = NULL;
	}
#endif
	return 0;
}

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

/*$	XGAppMultiWindow::GetCurMenu
 *
 *		The current menu for this single window application. This provides
 *	your application the opportunity to get the current menu bar for
 *	processing. The current menu bar depends on which is the frontmost
 *	window.
 */

XGMenuBar *XGAppMultiWindow::GetCurMenu()
{
#if OPT_MACOS == 1
	return fMyMenuBar;
#endif

#if OPT_WINOS == 1
	int i;
	int len;
	XGWindow *tw;
	
	if (fMyMenuBar) return fMyMenuBar;				// mdi
	else {
		if (fEventOwner) tw = fEventOwner;			// sdi
		else tw = XGWindow::GetFrontWindow();
		len = fWindowList.Length();
		for (i = 0; i < len; i++) {
			if (fWindowList[i].fWindow == tw) {
				return fWindowList[i].fMenuBar;
			}
		}
		return NULL;
	}
#endif
	
#if OPT_XWIN == 1
	int i;
	int len;
	XGWindow *tw;
	
	if (fEventOwner) tw = fEventOwner;
	else tw = XGWindow::GetFrontWindow();
	len = fWindowList.Length();
	for (i = 0; i < len; i++) {
		if (fWindowList[i].fWindow == tw) {
			return fWindowList[i].fMenuBar;
		}
	}
	return NULL;
#endif
}

/************************************************************************/
/*																		*/
/*	Window Menu Management												*/
/*																		*/
/************************************************************************/

/*	XGAppMultiWindow::DoCreateWindow
 *
 *		Insert this window into the list of windows that are being
 *	managed.
 */

void XGAppMultiWindow::DoCreateWindow(XGWindow *w)
{
	XGLstRecord wl;
	
	/*
	 *	Screen out non-document windows
	 */
	
	if (w->GetWindowKind() != KWinDocument) return;
	if (w->GetWindowAttributes() & KAttrNoMenu) return;
	
	/*
	 *	Insert this thing into my list of windows
	 */
	
	wl.menuAccel = -1;
	wl.menuID = -1;
	wl.fWindow = w;

#if OPT_XWIN == 1
	short menuID;
	menuID = w->GetMenuBarID();
	if (menuID == 0) menuID = KXGDefaultMenu;

	if (menuID != -1) {
		wl.fMenuBar = new XGMenuBar(w->GetWindow());
		w->SetMenuBar(wl.fMenuBar);
		wl.fMenuBar->LoadFromResource(menuID);
	} else {
		wl.fMenuBar = NULL;
	}
#endif
	
#if OPT_WINOS == 1
	if (fMyMenuBar == NULL) {
		short menuID;
		menuID = w->GetMenuBarID();
		if (menuID == 0) menuID = KXGDefaultMenu;

		if (menuID != -1) {
			wl.fMenuBar = new XGMenuBar(w->GetHWND(),false);
			wl.fMenuBar->LoadFromResource(menuID);
		} else {
			wl.fMenuBar = NULL;
		}
	} else {
		wl.fMenuBar = NULL;
	}
#endif

	fWindowList += wl;
	
	/*
	 *	Sort and update the window list
	 */
	
	SortWindowList();
	UpdateWindowMenu();
}

/*	XGAppMultiWindow::DoDestroyWindow
 *
 *		Scan the list of windows that are in me, and if this one is,
 *	delete it
 */

void XGAppMultiWindow::DoDestroyWindow(XGWindow *w)
{
	long len,off;
	
	/*
	 *	Screen out non-document windows
	 */
	
	if (w->GetWindowKind() != KWinDocument) return;
	if (w->GetWindowAttributes() & KAttrNoMenu) return;
	
	/*
	 *	Find window
	 */
	
	len = fWindowList.Length();
	for (off = 0; off < len; off++) {
		if (fWindowList[off].fWindow == w) break;
	}
	
	/*
	 *	Delete it
	 */
	
	if (off >= len) return;

#if OPT_XWIN || OPT_WINOS
	if (fWindowList[off].fMenuBar) delete fWindowList[off].fMenuBar;
#endif

	fWindowList.Delete(off);

#if OPT_XWIN == 1
	if (fWindowList.Length() == 0) {
		/*
		 *	All document windows closed. Force exit of application
		 */
		
		XGDispatch::SendDispatch(KXGQuitApplication,0,NULL);
	}
#endif

#if OPT_WINOS == 1
	if ((fWindowList.Length() == 0) && (!_GMDIWindow)) {
		/*
		 *	SDI App and all document windows closed. Force exit 
		 *  of application
		 */
		
		XGDispatch::SendDispatch(KXGQuitApplication,0,NULL);
	}
#endif
	
	/*
	 *	Update the window list
	 */
	
	UpdateWindowMenu();
}

/*	XGAppMultiWindow::DoRenameWindow
 *
 *		handle the fact that this window was renamed
 */

void XGAppMultiWindow::DoRenameWindow(XGWindow *w)
{
	long len,off;
	
	/*
	 *	Screen out non-document windows
	 */
	
	if (w->GetWindowKind() != KWinDocument) return;
	if (w->GetWindowAttributes() & KAttrNoMenu) return;
	
	/*
	 *	Find window
	 */
	
	len = fWindowList.Length();
	for (off = 0; off < len; off++) {
		if (fWindowList[off].fWindow == w) break;
	}
	
	/*
	 *	Sort and update if it was there
	 */
	
	if (off >= len) return;
	SortWindowList();
	UpdateWindowMenu();
}

/************************************************************************/
/*																		*/
/*	Window Menu Construction											*/
/*																		*/
/************************************************************************/

/*	XGAppMultiWindow::SortWindowList
 *
 *		Sort this window list.
 */

void XGAppMultiWindow::SortWindowList(void)
{
	long len,off;
	XGLstRecord wl;
	
	/*
	 *	Resort this list. This does a simple bubble sort because
	 *	I assume my window is no more than about 20 elements or so.
	 */
	
	len = fWindowList.Length();
	for (;;) {
		for (off = 1; off < len; off++) {			
			if (WindowCompare(fWindowList[off-1].fWindow,
							  fWindowList[off  ].fWindow) > 0) {
				/* Switch */
				wl = fWindowList[off-1];
				fWindowList[off-1] = fWindowList[off];
				fWindowList[off] = wl;
			}
		}
		if (off >= len) break;
	}
}

/*	XGAppMultiWindow::UpdateWindowMenu
 *
 *		This updates all of the menus, wherever they are.
 */

void XGAppMultiWindow::UpdateWindowMenu()
{
#if OPT_XWIN == 1
	int i;
	int len;
	
	len = fWindowList.Length();
	for (i = 0; i < len; i++) {
		fEventOwner = fWindowList[i].fWindow;
		UpdateWindowMenu(fWindowList[i].fMenuBar);
		fEventOwner = NULL;
	}
#endif

#if OPT_MACOS == 1
	UpdateWindowMenu(fMyMenuBar);
#endif

#if OPT_WINOS == 1
	if (fMyMenuBar) UpdateWindowMenu(fMyMenuBar);
	else {
		int i;
		int len;
		
		len = fWindowList.Length();
		for (i = 0; i < len; i++) {
			fEventOwner = fWindowList[i].fWindow;
			UpdateWindowMenu(fWindowList[i].fMenuBar);
			fEventOwner = NULL;
		}
	}
#endif
}

/*	XGAppMultiWindow::UpdateWindowMenu
 *
 *		This updates the menu window (if it exists) with the new
 *	window list.
 */

void XGAppMultiWindow::UpdateWindowMenu(XGMenuBar *menuBar)
{
	long len;
	long off;
	XGMenu *menu;
	long lmenu;
	long loff;
	long id;
	short accel;
	char buffer[256];
	
	/*
	 *	First, scan the menu bar until I find the menu which 
	 *	contains either my placeholder or one of my windows
	 */
	
	len = menuBar->GetLength();
	for (off = 0; off < len; off++) {
		menu = menuBar->GetMenu(off);

		lmenu = menu->GetLength();
		for (loff = 0; loff < lmenu; loff++) {
			id = menu->GetItemID(loff);
			if (IsWindowMenuID(id)) break;
		}
		if (loff < lmenu) break;
	}
	if (off >= len) return;			/* Couldn't find it. */
	
	/*
	 *	if we get here, then
	 *
	 *		menu			Is the reference to the menu containing my
	 *						window menu list
	 *		loff			Is the first item which refers to my menu
	 *
	 *	Next, we delete what was there before
	 */
	
	for (;;) {
		lmenu = menu->GetLength();
		if (loff >= lmenu) break;
		id = menu->GetItemID(loff);
		if (!IsWindowMenuID(id)) break;
		
		menu->DeleteItem(loff);
	}
	
	/*
	 *	Update the accelerators in my menu list to assign those
	 *	where I have a free accelerator. (We only assign accelerator
	 *	keys '1' through '9'; we expect '0' is reserved.)
	 */
	
	len = fWindowList.Length();
	for (off = 0; off < len; off++) {
		if (fWindowList[off].menuAccel == -1) {
			/* No accelerator; scan for one */
			for (accel = 1; accel <= 9; accel++) {
				for (id = 0; id < len; id++) {
					if (fWindowList[id].menuAccel == accel) break;
				}
				if (id >= len) break;
			}
			if (accel >= 10) break;
			
			fWindowList[off].menuAccel = accel;
		}
	}
	
	/*
	 *	Update the menu identifier to assign an empty identifier
	 */
	
	for (off = 0; off < len; off++) {
		if (fWindowList[off].menuID == -1) {
			/* No accelerator; scan for one */
			for (accel = 0x7000; accel < 0x8000; accel++) {
				for (id = 0; id < len; id++) {
					if (fWindowList[id].menuID == accel) break;
				}
				if (id >= len) break;
			}
			if (accel >= 0x8000) break;
			
			fWindowList[off].menuID = accel;
		}
	}
	
	/*
	 *	Now insert the list into the menu bar
	 */
	
	if (len == 0) {
		menu->InsertItem(loff);
		menu->SetItemID(loff,KXGWindowList);
		menu->SetText(loff,"Empty");
	} else {
		for (off = 0; off < len; off++, loff++) {
			menu->InsertItem(loff);
			menu->SetItemID(loff,fWindowList[off].menuID);
			
#if (OPT_MACOS == 1) || (OPT_XWIN == 1)
			fWindowList[off].fWindow->GetTitle(buffer);
			menu->SetText(loff,buffer);
			if (fWindowList[off].menuAccel != -1) {
				menuBar->SetAccelerator(fWindowList[off].menuID,'0' + fWindowList[off].menuAccel);
			}
#endif

#if OPT_WINOS == 1
			if (fWindowList[off].menuAccel == -1) {
				fWindowList[off].fWindow->GetTitle(buffer);
			} else {
				buffer[0] = '&';
				buffer[1] = '0' + fWindowList[off].menuAccel;
				buffer[2] = ' ';
				fWindowList[off].fWindow->GetTitle(buffer+3);
			}
			menu->SetText(loff,buffer);
#endif
		}
	}
}

/************************************************************************/
/*																		*/
/*	Tiling/Cascading Windows (Macintosh Version)						*/
/*																		*/
/************************************************************************/

#if OPT_MACOS == 1

/*	XGAppMultiWindow::TileWindows
 *
 *		Tile the windows across the screen
 */
 
void XGAppMultiWindow::TileWindows()
{
	XRect r,s,tmp;
	short count;
	short i,mid;
	XPoint nsize;
	XGWindow *foo;
	
	count = fWindowList.Length();
	if (count == 0) return;				// Huh?  Something didn't compute
	XGWindow::GetScreenSize(&r);		// Get bounding of the screen
	
	if (count == 1) {
		InsetRect(&r,2,2);
		
		foo = fWindowList[0].fWindow;
		nsize.h = r.right - r.left;
		nsize.v = r.bottom - r.top;
		foo->LimitWindowSize(&nsize);
		r.right = r.left + nsize.h;
		r.bottom = r.top + nsize.v;
		foo->SizeToContent(&r);
		foo->SetWindowLocation(r);
	} else if (count == 2) {
		s = r;
		s.bottom = (r.top + r.bottom)/2;
		InsetRect(&s,1,1);
		foo = fWindowList[0].fWindow;
		nsize.h = s.right - s.left;
		nsize.v = s.bottom - s.top;
		foo->LimitWindowSize(&nsize);
		s.right = s.left + nsize.h;
		s.bottom = s.top + nsize.v;
		foo->SizeToContent(&s);
		foo->SetWindowLocation(s);
		
		s = r;
		s.top = (r.top + r.bottom)/2;
		InsetRect(&s,1,1);
		foo = fWindowList[1].fWindow;
		nsize.h = s.right - s.left;
		nsize.v = s.bottom - s.top;
		foo->LimitWindowSize(&nsize);
		s.right = s.left + nsize.h;
		s.bottom = s.top + nsize.v;
		foo->SizeToContent(&s);
		foo->SetWindowLocation(s);
	} else {
		mid = count/2;
		s = r;
		s.right = (r.right+r.left)/2;
		for (i = 0; i < mid; i++) {
			WaitCursor();
			tmp = s;
			tmp.top = ((r.bottom-r.top)*i)/mid + s.top;
			tmp.bottom = ((r.bottom-r.top)*(i+1))/mid + s.top;
			InsetRect(&tmp,1,1);
			
			foo = fWindowList[i].fWindow;
			nsize.h = tmp.right - tmp.left;
			nsize.v = tmp.bottom - tmp.top;
			foo->LimitWindowSize(&nsize);
			tmp.right = tmp.left + nsize.h;
			tmp.bottom = tmp.top + nsize.v;
			foo->SizeToContent(&tmp);
			foo->SetWindowLocation(tmp);
		}
		s = r;
		s.left = (r.right+r.left)/2;
		for (i = mid; i < count; i++) {
			WaitCursor();
			tmp = s;
			tmp.top = ((r.bottom-r.top)*(i-mid))/(count-mid) + s.top;
			tmp.bottom = ((r.bottom-r.top)*(i-mid+1))/(count-mid) + s.top;
			InsetRect(&tmp,1,1);

			foo = fWindowList[i].fWindow;
			nsize.h = tmp.right - tmp.left;
			nsize.v = tmp.bottom - tmp.top;
			foo->LimitWindowSize(&nsize);
			tmp.right = tmp.left + nsize.h;
			tmp.bottom = tmp.top + nsize.v;
			foo->SizeToContent(&tmp);
			foo->SetWindowLocation(tmp);
		}
		NormalCursor();
	}
}

/*	XGAppMultiWindow::CascadeWindows
 *
 *		Cascade the windows on the screen
 */

void XGAppMultiWindow::CascadeWindows()
{
	XRect r,s;
	XGWindow *foo;
	int count;
	XPoint size;
	XPoint nsize;
	int i;
	
	count = fWindowList.Length();
	if (count == 0) return;
	XGWindow::GetScreenSize(&r);
	r.bottom -= 2;
	
	if (count > 15) count = 13;
	size.h = (r.right - r.left - 10) - 5 * (count - 1);
	if (count > 10) count = 10;
	size.v = (r.bottom - r.top) - 20 * (count - 1);
	
	count = fWindowList.Length();
	for (i = 0; i < count; i++) {
		WaitCursor();
		
		nsize = size;
		foo = fWindowList[i].fWindow;
		foo->LimitWindowSize(&nsize);

		s.left = (i % 13) * 5 + r.left + 5;
		s.right = s.left + nsize.h;
		s.top = (i % 10) * 20 + r.top;
		s.bottom = s.top + nsize.v;
		
		foo->SizeToContent(&s);
		foo->SetWindowLocation(s);
	}
	NormalCursor();
}

#endif

/************************************************************************/
/*																		*/
/*	Tiling/Cascading Windows (Microsoft Windows Version)				*/
/*																		*/
/************************************************************************/

#if OPT_WINOS == 1

/*	XGAppMultiWindow::TileWindows
 *
 *		Tile the windows across the screen
 */
 
void XGAppMultiWindow::TileWindows()
{
	::SendMessage(_GMDIWindow,WM_MDITILE,(WPARAM)MDITILE_VERTICAL,NULL);
}

/*	XGAppMultiWindow::_CascadeWindows
 *
 *		Cascade the windows on the screen
 */

void XGAppMultiWindow::CascadeWindows()
{
	::SendMessage(_GMDIWindow,WM_MDICASCADE,0,NULL);
}

/*	XGAppMultiWindow::ArrangeWindows
 *
 *		Arrange the window icons at the bottom of the MDI window
 */

void XGAppMultiWindow::ArrangeIcons()
{
	::SendMessage(_GMDIWindow,WM_MDIICONARRANGE,0,NULL);
}

#endif

/************************************************************************/
/*																		*/
/*	Windows initialization support										*/
/*																		*/
/************************************************************************/

#if OPT_WINOS == 1

/*	XGAppMultiWindow::InitMDIApplication
 *
 *		Register the classes which are in me
 */

void XGAppMultiWindow::InitMDIApplication(void)
{
	WNDCLASSEX wc;
	static bool gInit = false;
		
	if (gInit) return;					// run this only once.
	gInit = true;
	
	/*
	 *	Initialize four instances: the main window which encapsulates
	 *	the MDI window, the child window, the dialog window and the 
	 *	view window
	 */
	 
	wc.cbSize = sizeof(wc);
	wc.style = 0;
	wc.lpfnWndProc = _GToolBarWindowProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = _GInstance;
	wc.hIcon = NULL;
	wc.hCursor = LoadCursor(NULL,IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = _GToolClass;
	wc.hIconSm = NULL;
	Assert(RegisterClassEx(&wc));
	
	wc.cbSize = sizeof(wc);
	wc.style = 0;
	wc.lpfnWndProc = _GMainMDIWindowProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = _GInstance;
	wc.hIcon = (HICON)LoadImage(_GInstance,"AppIcon",IMAGE_ICON,32,32,LR_SHARED);;
	wc.hCursor = LoadCursor(NULL,IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE+1);
	wc.lpszMenuName = MAKEINTRESOURCE(KXGDefaultMenu);
	wc.lpszClassName = _GMainClass;
	wc.hIconSm = (HICON)LoadImage(_GInstance,"AppIconSmall",IMAGE_ICON,16,16,LR_SHARED);
	Assert(RegisterClassEx(&wc));
	
	wc.cbSize = sizeof(wc);
	wc.style = CS_DBLCLKS;
	wc.lpfnWndProc = _GChildWindowProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = _GInstance;
	wc.hIcon = NULL;
	wc.hCursor = NULL;
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = _GWindowClass;
	wc.hIconSm = NULL;
	Assert(RegisterClassEx(&wc));
	
	wc.cbSize = sizeof(wc);
	wc.style = CS_DBLCLKS;
	wc.lpfnWndProc = _GSingleWindowProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = _GInstance;
	wc.hIcon = (HICON)LoadImage(_GInstance,"AppIcon",IMAGE_ICON,32,32,LR_SHARED);;
	wc.hCursor = NULL;
	wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = _GDlogClass;
	wc.hIconSm = (HICON)LoadImage(_GInstance,"AppIconSmall",IMAGE_ICON,16,16,LR_SHARED);
	Assert(RegisterClassEx(&wc));
}

/*	XGAppMultiWindow::InitMDIInstance
 *
 *		Initialize the instance of this thing. This builds the MDI frame
 *	of my window.
 */

void XGAppMultiWindow::InitMDIInstance(char *appName)
{
	if (appName == NULL) appName = "YAAF Application";
	
	_GMainWindow = CreateWindow(_GMainClass,appName,
					WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_MAXIMIZEBOX |
					WS_MINIMIZEBOX,
					CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
					NULL, NULL, _GInstance, NULL);
	Assert(_GMainWindow);
	
	ShowWindow(_GMainWindow,_GArgShow);
	UpdateWindow(_GMainWindow);
}

/*	XGAppMultiWindow::InitSDIApplication
 *
 *		Register the classes which are in me
 */

void XGAppMultiWindow::InitSDIApplication(void)
{
	WNDCLASSEX wc;
	static bool gInit = false;
		
	if (gInit) return;					// run this only once.
	gInit = true;
	
	/*
	 *	Initialize three instances: a class of class name "_XGWindow"
	 *	and a class of class name "_XGView", and "_XGDialog"
	 */
	
	wc.cbSize = sizeof(wc);
	wc.style = CS_DBLCLKS;
	wc.lpfnWndProc = _GSingleWindowProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = _GInstance;
	wc.hIcon = (HICON)LoadImage(_GInstance,"AppIcon",IMAGE_ICON,32,32,LR_SHARED);;
	wc.hCursor = NULL;
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = _GWindowClass;
	wc.hIconSm = (HICON)LoadImage(_GInstance,"AppIconSmall",IMAGE_ICON,16,16,LR_SHARED);
	Assert(RegisterClassEx(&wc));
	
	wc.cbSize = sizeof(wc);
	wc.style = CS_DBLCLKS;
	wc.lpfnWndProc = _GSingleWindowProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = _GInstance;
	wc.hIcon = (HICON)LoadImage(_GInstance,"AppIcon",IMAGE_ICON,32,32,LR_SHARED);;
	wc.hCursor = NULL;
	wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = _GDlogClass;
	wc.hIconSm = (HICON)LoadImage(_GInstance,"AppIconSmall",IMAGE_ICON,16,16,LR_SHARED);
	Assert(RegisterClassEx(&wc));
}

/*	XGAppMultiWindow::InitSDIInstance
 *
 *		Initialize the instance of this thing. This does nothing; we assume
 *	the window that is initialized elsewhere will provide me the UI.
 */

void XGAppMultiWindow::InitSDIInstance(void)
{
}

/*	XGAppMultiWindow::InitToolbar
 *
 *		Try to initialize the toolbar from the resource
 */
 
void XGAppMultiWindow::InitToolbar(short resID)
{
	try {
		_GToolView = new XGToolbarView(resID);
	}
	catch (...) {
		_GToolView = NULL;
		return;
	}

	/*
	 *	If we get here, we were able to create the toolbar because the
	 *	toolbar resources were present. Create the toolbar window
	 */
	
	_GToolWindow = ::CreateWindow(_GToolClass,
								  "MyToolbar",
								  WS_CHILDWINDOW | WS_VISIBLE,
								  0,
								  0,
								  10,
								  10,
								  _GMainWindow,
								  NULL,
								  _GInstance,
								  NULL);
	if (_GToolWindow) {
		/*
		 *	Force a resize so things are placed correctly
		 */
		
		SendMessage(_GMainWindow,WM_SIZE,0,0);
	}
}

#endif
