/* 
Copyright (c) 1991-2000 UserLand Software, Inc. 

Permission is hereby granted, free of charge, to any person obtaining a 
copy of this software and associated documentation files (the 
"Software"), to deal in the Software without restriction, including 
without limitation the rights to use, copy, modify, merge, publish, 
distribute, sublicense, and/or sell copies of the Software, and to 
permit persons to whom the Software is furnished to do so, subject to the 
following conditions: 

The above copyright notice and this permission notice shall be 
included in all copies or substantial portions of the Software. 

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


#include <appletdefs.h>
#include <AppleEvents.h>
#include <uisharing.h>


#define idapplemenu 128 
#define aboutitem 1 

#define idfilemenu 129 
#define quititem 1 

#define ideditmenu 130
#define undoitem 1 
#define cutitem 3 
#define copyitem 4 
#define pasteitem 5
#define clearitem 6
#define selectallitem 8 

#define firstsharedmenu ideditmenu+1 /*resource id for first shared menu*/


#define ctwindows 3

Str255 messages [ctwindows];

WindowPtr windows [ctwindows];

Boolean flexitmainloop = false; 

Boolean flcurrentlyactive = true;




static void initmacintosh (void) {

	/*
	the magic stuff that every Macintosh application needs to do 
	before doing anything else.
	*/

	short i;
		
	MaxApplZone ();
	
	for (i = 0; i < 10; i++) 
		MoreMasters ();
	
	InitGraf (&qd.thePort);
	
	InitFonts ();
	
	FlushEvents (everyEvent, 0);
	
	InitWindows ();
	
	InitMenus ();
	
	TEInit ();
	
	InitDialogs (nil);
	
	InitCursor ();
	
	for (i = 0; i < 5; i++) { /*register with Multifinder*/
		
		EventRecord ev;
		
		EventAvail (everyEvent, &ev); /*see TN180 -- splash screen*/
		} /*for*/
	} /*initmacintosh*/


static Boolean optionkeydown (void) {
	
	KeyMap keys;
	
	GetKeys (keys);
	
	return (BitTst (&keys, 61));
	} /*optionkeydown*/
	
	
static void initmenus (void) {
	
	MenuHandle h = GetMenu (idapplemenu);
	
	AppendResMenu (h, 'DRVR'); 
	
	InsertMenu (h, 0); 
	
	InsertMenu (GetMenu (idfilemenu), 0);
	
	InsertMenu (GetMenu (ideditmenu), 0);
	
	DrawMenuBar ();
	} /*initmenus*/


static void copystring (Str255 source, void *dest) {
	
	/*
	copy a pascal formatted string.
	*/
	
	BlockMove (source, dest, source [0] + 1);
	} /*copystring*/


static void ellipsize (char *s, short width) {

	/*
	if the string fits inside the given number of pixels, fine -- do nothing
	and return.
	
	if not, return a string that does fit, with ellipses representing the 
	deleted characters.  ellipses are generated by pressing option-semicolon.
	*/
	
	char len;
	short newwidth;
	
	if ((newwidth = StringWidth ((ConstStr255Param) s)) <= width) /*nothing to do, the string fits*/
		return;
	
	len = s [0]; /* current length in characters*/
	
	width -= CharWidth (''); /* subtract width of ellipses*/
		
	do { /*until it fits (or we run out of characters)*/
	
		newwidth -= CharWidth (s [len]);
		
		--len;
	} while ((newwidth > width) && (len != 0));
	
	++len; /*make room for the ellipses*/
	
	s [len] = ''; 
	
	s [0] = (char) len;
	} /*ellipsize*/


static void centerstring (Rect r, char *s) {
	
	/*
	draw the string in the current font, size and style, centered inside
	the indicated rectangle.
	*/
	
	short rh = r.bottom - r.top;
	short rw = r.right - r.left;
	short h, v;
	FontInfo fi;
	
	GetFontInfo (&fi);
	
	ellipsize (s, rw); /*make sure it fits inside the rectangle, width-wise*/
	
	h = r.left + ((rw - StringWidth ((ConstStr255Param) s)) / 2);
	
	v = r.top + ((rh - (fi.ascent + fi.descent)) / 2) + fi.ascent;
	
	MoveTo (h, v);
	
	ClipRect (&r);
	
	DrawString ((ConstStr255Param) s);
	} /*centerstring*/


static void setfontsizestyle (short fontnum, short fontsize, short fontstyle) {

	TextFont (fontnum);
	
	TextSize (fontsize);
	
	TextFace (fontstyle);
	} /*setfontsizestyle*/
	
	
static void updatewindow (WindowPtr w) {
	
	Rect r;
	Str255 s;
	
	r = (*w).portRect;
	
	EraseRect (&r);
	
	setfontsizestyle (kFontIDHelvetica, 12, 0);
	
	centerstring (r, (char *) GetWRefCon (w));
	
	NumToString (FreeMem () / 1024, s);
	
	MoveTo (r.left + 3, r.bottom - 3);
	
	setfontsizestyle (kFontIDGeneva, 9, 0);
	
	DrawString (s);
	
	DrawString ("\pK");
	} /*updatewindow*/
	
	
static Boolean setwindowmessage (WindowPtr w, Str255 s) {
	
	if (w == nil)
		return (false);
		
	if (uisIsSharedWindow (w)) /*doesn't belong to us*/
		return (false);
		
	copystring (s, (char *) GetWRefCon (w)); 
	
	SetPort (w);
	
	updatewindow (w);
	
	return (true);
	} /*setwindowmessage*/


static Boolean newwindows (void) {
	
	short i;
	
	for (i = 0; i < ctwindows; i++) {
		
		Str255 windowtitle;
		WindowPtr w;
		
		w = GetNewWindow (128, nil, (WindowPtr) -1);
		
		windows [i] = w;
		
		copystring ("\pWindow #1", windowtitle);
		
		windowtitle [windowtitle [0]] = '0' + i + 1;
		
		SetWTitle (w, windowtitle);
		
		messages [i] [0] = 0;
		
		SetWRefCon (w, (long) &messages [i]);
		
		MoveWindow (w, 100 + (17 * i), 100 + (17 * i), false);
		
		ShowWindow (w);
		} /*for*/
	} /*newwindows*/


static void handleupdate (EventRecord *ev) {
	
	WindowPtr w;
	
	w = (WindowPtr) (*ev).message;
	
	BeginUpdate (w);
	
	SetPort (w);
	
	updatewindow (w);
	
	EndUpdate (w);
	} /*handleupdate*/


static void handledrag (EventRecord *ev, WindowPtr w) {
	
	Rect r;

	r = qd.screenBits.bounds; 
	
	r.top = r.top + GetMBarHeight (); 
	
	InsetRect (&r, 4, 4);
	
	DragWindow (w, (*ev).where, &r);
	} /*handledrag*/


static void handlemenu (long codeword) {
	
	short idmenu, iditem;
	
	iditem = LoWord (codeword);
	
	idmenu = HiWord (codeword);
	
	if (uisSharedMenuHit (idmenu, iditem))
		goto exit;
	
	switch (idmenu) {
	
		case idapplemenu: 
			switch (iditem) {
				
				case aboutitem:
					Alert (262, nil);
					
					break;
			
				default: {
					Str255 s;
					
					GetMenuItemText (GetMenu (idapplemenu), iditem, s);
					
					OpenDeskAcc (s);
					
					break;
					}
				} /*switch*/
			
			break; /*apple menu*/

		case idfilemenu: 
			switch (iditem) {
				
				case quititem:
				
					flexitmainloop = true;
					
					break;
				} /*switch*/
			
			break; /*file menu*/
			
		case ideditmenu: 
			switch (iditem) {
				
				case undoitem:
					break;
				} /*switch*/
			
			break; /*edit menu*/
			
		} /*switching on which menu was invoked*/
		
	exit:
	
	HiliteMenu (0);
	} /*handlemenu*/
	

static void closewindow (WindowPtr w) {
	
	/*
	close one of our own windows, not a shared window.
	*/
	
	if (w != nil) {
	
		short i;
	
		CloseWindow (w);
		
		for (i = 0; i < ctwindows; i++) {
		
			if (windows [i] == w)
				windows [i] = nil;
			} /*for*/
		}
	} /*closewindow*/
	
	
static void closeallwindows (void) {
	
	short i;
	
	for (i = 0; i < ctwindows; i++)
		closewindow (windows [i]);			
	} /*closeallwindows*/
	
	
static void handlemouse (EventRecord *ev) {

	short part;
	WindowPtr w;
	
	part = FindWindow ((*ev).where, &w);
	
	if (w != nil) 
	
		if (w != FrontWindow ()) { 
			
			SelectWindow (w);
							
			return; 
			}
	
	switch (part) {
	
		case inMenuBar: 
			handlemenu (MenuSelect ((*ev).where)); 
			
			break;
		
		case inSysWindow:
			SystemClick (ev, w); 
			
			break;
		
		case inDrag:
			handledrag (ev, w);
			
			break;
			
		case inGoAway:
			if (TrackGoAway (w, (*ev).where)) {
			
				if (optionkeydown ()) { /*close all windows*/
				
					uisCloseAllSharedWindows (); 
					
					closeallwindows ();
					}
				else
					closewindow (w);
				}
				
			break;
		} /*switch*/
	} /*handlemouse*/


static void handleevent (EventRecord *ev) {

	Boolean flcloseallwindows;
	
	if (uisHandleEvent (ev, &flcloseallwindows)) { /*event was handled*/
		
		if (flcloseallwindows)
			closeallwindows ();
			
		return;
		}
	
	switch ((*ev).what) {
	
		case keyDown: case autoKey: 
			handlemenu (MenuKey ((*ev).message & charCodeMask)); 
			
			break;
			
		case mouseDown:
			handlemouse (ev);
			
			break;
		
		case updateEvt:
			handleupdate (ev);
			
			break;
		
		case osEvt:
			flcurrentlyactive = ((*ev).message & resumeFlag) != 0;
			
			break;
		
		case kHighLevelEvent:
			AEProcessAppleEvent (ev);
			
			break;
		} /*switch*/
	} /*handleevent*/


static void maineventloop (void) {
	
	EventRecord ev;
	Boolean flcloseallwindows;
	
	while (!flexitmainloop) {
		
		WaitNextEvent (everyEvent, &ev, 1, nil);
		
		handleevent (&ev);
		} /*while*/
	} /*maineventloop*/


static pascal OSErr handleopenapp (AEDescList *event, AEDescList *reply, long refcon) {
	
	/*DebugStr ("\popenapp");*/
	
	return (noErr);
	} /*handleopenapp*/


static pascal OSErr handleopen (AppleEvent *event, AppleEvent *reply, long refcon) {
	
	/*DebugStr ("\phandleopen");*/
	
	return (noErr);
	} /*handleopen*/
	
		
static pascal OSErr handleprint (AEDescList *event, AEDescList *reply, long refcon) {
	
	/*DebugStr ("\phandleprint");*/
	
	return (noErr);
	} /*handleprint*/
	
		
static pascal OSErr handlequit (event, reply, refcon) AEDescList *event, *reply; long refcon; {
	
	/*DebugStr ("\pquitapp");*/
	
	flexitmainloop = true;
	
	return (noErr);
	} /*handlequit*/
	
	
static pascal OSErr setmessageverb (AppleEvent *event, AppleEvent *reply, long refcon) {

	OSErr ec;
	AEDesc result;
	Str255 s;
	Handle htext;
	long lentext;
	Boolean fl;
	
	ec = AEGetParamDesc (event, keyDirectObject, typeChar, &result);
	
	if (ec != noErr) 
		return (ec);
		
	htext = result.dataHandle;
	
	if (htext == nil)
		return (noErr);
		
	lentext = GetHandleSize (htext);
	
	if (lentext > 255)
		lentext = 255;
		
	s [0] = (unsigned char) lentext;
	
	BlockMove (*htext, &s [1], lentext);
		
	setwindowmessage (FrontWindow (), s);
	
	fl = true;
	
	ec = AEPutParamPtr (event, keyDirectObject, typeBoolean, (Ptr) &fl, sizeof (Boolean));
	
	return (ec);
	} /*setmessageverb*/
	

static void installhandlers (void) {

	AEInstallEventHandler ('test', 'smsg', (EventHandlerProcPtr) &setmessageverb, 0, false);
		
	AEInstallEventHandler (kCoreEventClass, kAEOpenApplication, (ProcPtr) &handleopenapp, 0, false);
	
	AEInstallEventHandler (kCoreEventClass, kAEOpenDocuments, (ProcPtr) &handleopen, 0, false);
	
	AEInstallEventHandler (kCoreEventClass, kAEPrintDocuments, (ProcPtr) &handleprint, 0, false);
	
	AEInstallEventHandler (kCoreEventClass, kAEQuitApplication, (ProcPtr) &handlequit, 0, false);
	} /*installhandlers*/
	

static void opentestwindows (void) {
	
	short i;
	Handle h;
	Str255 windowtitle;
	
	for (i = 128; i <= 131; i++) {
		
		h = GetResource ('CARD', i);
		
		if (h == nil)
			continue;
		
		DetachResource (h);
		
		copystring ("\pCard #1", windowtitle);
		
		windowtitle [windowtitle [0]] = '0' + (i - 128) + 1;
		
		uisOpenHandle (h, true, windowtitle, 0, 0, nil);
		} /*for*/
	} /*opentestwindows*/
	
	
#ifdef iowaRuntimeInApp

	extern boolean iowaRuntimeOpen (void); /*prototype*/
	
	extern void iowaRuntimeClose (void); /*prototype*/
	
#endif


#ifdef IOAinsideApp
	
	extern boolean IOAregistercomponents (void); /*prototype*/

	extern void IOAunregistercomponents (void); /*prototype*/
	
#endif


void alertdialog (Str255); /*prototype*/


void alertdialog (Str255 s) {
	
	ParamText (s, "\p", "\p", "\p"); 
	
	Alert (263, nil);
	} /*alertdialog*/
	
	
void main (void) {
	
	initmacintosh ();
	
	#ifdef IOAinsideApp
		IOAregistercomponents ();
	#endif
	
	#ifdef iowaRuntimeInApp
		iowaRuntimeOpen ();
	#endif
	
	newwindows ();
	
	initmenus ();
	
	installhandlers ();
	
	if (!uisInit (nil, firstsharedmenu, 0, 0))
		return;
	
	opentestwindows ();
	
	maineventloop ();
	
	uisClose (); 
	
	#ifdef iowaRuntimeInApp
		iowaRuntimeClose ();
	#endif
	
	#ifdef IOAinsideApp
		IOAunregistercomponents ();
	#endif
	} /*main*/