/* 
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 "iowacore.h"
#include <applet.h>
#include <appletmain.h>
#include "iowaobject.h"
#include "iowamenu.h"
#include "iowapack.h"
#include "iowadialogs.h"
#include "iowaicons.h"
#include "iowaattributes.h"
#include "iowainit.h"
#include "iowafrontier.h"
#include "iowascript.h"
#include "iowawindowatts.h"
#include "iowainfowindow.h"
#include "iowawires.h"

#ifdef flstatusbar

	#include "iowastatusbar.h"

#endif

#define initialgridunits 12

#define selectinsidegroup false /*set true to allow selection within a group*/

boolean flprocessingeditevent = false; /*can be set externally*/



static boolean iowasetglobals (void) {
	
	iowadata = (hdlcard) app.appdata;
	
	if (iowadata != nil) {
	
		(**iowadata).updaterect = (**app.appwindow).updaterect;
	
		(**iowadata).drawwindow = (**app.appwindow).macwindow;
		
		(**iowadata).macevent = appletevent;
	
		/*iowavalidate (false);*/ /*the card is being edited, not running*/
		}
	
	return (true);
	} /*iowasetglobals*/
	
	
static void iowaselectpaletteitem (short itemnum) {

	hdlcard hc = iowadata;
	hdleditinfo he = (hdleditinfo) (**hc).refcon;
	
	apppostcallback ();
	
	paletteselectitem ((**app.appwindow).hpalette, itemnum);
	
	appprecallback ();
	
	(**he).mode = itemnum;
	} /*iowaselectpaletteitem*/
	
	
static boolean iowahaveselection (void) {
	
	return (!nothingselected ());
	} /*iowahaveselection*/
	
	
static boolean iowaselectall (void) {
	
	hdlcard hc = iowadata;
	hdleditrecord hedit;
	
	hedit = getactiveeditrecord ();
	
	if (hedit != nil) 
		editselectall (hedit);
	else 
		selectallobjects ();
		
	return (true);
	} /*iowaselectall*/
	
	
static boolean dragtogrow (hdlobject x, Rect *rselected) {
	
	Rect r;
	Point pt, origpt, lastpt;
	boolean flfirstloop = true;
	
	r = *rselected;
	
	/*GetMouse (&origpt);*/
	
	origpt = mousestatus.localpt; /*1.0b15*/
	
	pushpen ();
	
	PenPat (&quickdrawglobal (gray)); 
		
	PenMode (patXor);
	
	pushhighlightcolor (&(**iowadata).backcolor);
	
	lastpt = origpt;
	
	while (StillDown ()) {
		
		GetMouse (&pt);
		
		if (pointdist (pt, lastpt) > 0) {
		
			waitfornexttick (); /*heuristic to avoid flicker*/
			
			if (!flfirstloop) /*erase*/
				FrameRect (&r);
				
			r.right = pt.h;
			
			r.bottom = pt.v;
			
			FrameRect (&r);
			
			lastpt = pt;
			
			flfirstloop = false;
			}
		} /*while*/
		
	if (!flfirstloop) /*erase*/
		FrameRect (&r);
	
	popforecolor ();
	
	poppen ();
	
	*rselected = r;
	
	return (!emptyrect (r));
	} /*dragtogrow*/
	
	
static void checkborderrect (Rect *r) {
	
	#define minbordersize 25
	
	if ((*r).right < minbordersize)
		(*r).right = minbordersize;
		
	if ((*r).bottom < minbordersize)
		(*r).bottom = minbordersize;
	} /*checkborderrect*/
	
	
static boolean dragborders (void) {
	
	hdlcard hc = iowadata;
	short rb = (**hc).rightborder;
	short bb = (**hc).bottomborder;
	Point pt, origpt, lastpt;
	boolean flfirstloop = true;
	Rect r, rorig;
	
	/*GetMouse (&origpt);*/
	
	origpt = mousestatus.localpt; /*1.0b15*/
	
	r.bottom = bb;
	
	r.right = rb;
	
	r.top = bb - nubsize;
	
	r.left = rb - nubsize;
	
	if (!PtInRect (origpt, &r)) /*not pointing in the border nub*/
		return (false);
	
	pushpen ();
	
	PenPat (&quickdrawglobal (gray)); 
		
	PenMode (patXor);
	
	pushforecolor (&blackcolor);
	
	pushbackcolor (&whitecolor);
	
	lastpt = origpt;
	
	r.top = r.left = -1;
	
	r.right = rb;
	
	r.bottom = bb;
	
	rorig = r;
	
	while (StillDown ()) {
		
		GetMouse (&pt);
		
		if (pointdist (pt, lastpt) > 0) {
		
			waitfornexttick (); /*heuristic to avoid flicker*/
			
			if (!flfirstloop) /*erase*/
				FrameRect (&r);
			
			r.right = pt.h;
			
			r.bottom = pt.v;
			
			checkborderrect (&r);
			
			FrameRect (&r);
			
			lastpt = pt;
			
			flfirstloop = false;
			}
		} /*while*/
		
	if (!flfirstloop) /*erase*/
		FrameRect (&r);
	
	popbackcolor ();
	
	popforecolor ();
	
	poppen ();
	
	if ((**hc).flgrid) {
	
		short gridunits = (**hc).gridunits;
		
		r.right = (r.right / gridunits) * gridunits;
			
		r.bottom = (r.bottom / gridunits) * gridunits;
		}
	
	checkborderrect (&r);
				
	(**hc).rightborder = r.right;
	
	(**hc).bottomborder = r.bottom;
	
	madechanges ();
	
	resetwindowattributes (); /*if Window Info dialog is running, force reload on next null event*/
	
	UnionRect (&r, &rorig, &r);
	
	r.right++; r.bottom++;
	
	iowainvalrect (&r);
	
	return (true);
	} /*dragborders*/
	
	
static void drawfordragtoselect (Rect r) {
	
	hdlcard hc = iowadata;
	hdleditinfo he = (hdleditinfo) (**hc).refcon;
	
	switch (modetoobjecttype ((**he).mode)) {
		
		case ovaltype:
			FrameOval (&r);
			
			break;
			
		case linetype:
			MoveTo (r.left, r.top);
			
			LineTo (r.right, r.bottom);
			
			break;
			
		default:
			FrameRect (&r);
			
			break;
		} /*switch*/
	} /*drawfordragtoselect*/
	
	
static boolean dragtoselect (Rect *rselected) {
	
	/*
	returns true if a non-empty rectangle was selected.
	*/
	
	Rect r;
	Point pt, origpt, lastpt;
	boolean flfirstloop = true;
	
	if (!StillDown ()) {
	
		zerorect (rselected);
		
		return (false);
		}
		
	/*GetMouse (&origpt);*/
	
	origpt = mousestatus.localpt; /*1.0b15*/
	
	pushpen ();
	
	PenPat (&quickdrawglobal (gray)); 
		
	PenMode (patXor);
	
	pushhighlightcolor (&(**iowadata).backcolor);
	
	lastpt = origpt;
	
	zerorect (&r);
	
	while (StillDown ()) {
		
		GetMouse (&pt);
		
		if (pointdist (pt, lastpt) > 0) {
		
			waitfornexttick (); /*heuristic to avoid flicker*/
			
			if (!flfirstloop) /*erase*/
				drawfordragtoselect (r);
			
			if (pt.v < origpt.v) {
				
				r.top = pt.v;
				
				r.bottom = origpt.v;
				}
			else {
				r.top = origpt.v;
				
				r.bottom = pt.v;
				}
				
			if (pt.h < origpt.h) {
				
				r.left = pt.h;
				
				r.right = origpt.h;
				}
			else {
				r.left = origpt.h;
				
				r.right = pt.h;
				}
				
			drawfordragtoselect (r);
			
			lastpt = pt;
			
			flfirstloop = false;
			}
		} /*while*/
		
	if (!flfirstloop) /*erase*/
		FrameRect (&r);
		
	popforecolor ();
	
	poppen ();
	
	*rselected = r;
	
	return (!emptyrect (r));
	} /*dragtoselect*/
	
	
void enterarrowmode (void) {

	hdlcard hc = iowadata;
	hdlobject h;
	
	h = (**hc).activetextobject;
	
	if (h != nil) {
	
		clearactivetextobject ();
		
		addtoselection (h);
		
		iowaselectpaletteitem (arrowmode);
		
		(**app.appwindow).selectioninfo.fldirty = true; /*to enable commands in Edit menu*/
		}
	} /*enterarrowmode*/
	
	
static boolean entertextmode (void) {

	hdlcard hc = iowadata;
	hdlobject hobj;
	
	if ((**hc).activetextobject != nil) /*already in text mode, nothing to do*/
		return (true);
		
	if (!getsingleselectedobject (&hobj)) 
		goto error;
		
	if (!editobject (findeditableobject ())) 
		goto error;
		
	(**app.appwindow).selectioninfo.fldirty = true; /*to disable commands in Edit menu*/
	
	iowaselectpaletteitem (textmode);
	
	makeselectionempty ();
	
	return (true);
	
	error:
	
	/*sysbeep;*/
	
	return (false);
	} /*entertextmode*/
	 
	
static boolean iowakeystroke (void) { 
	
	/*
	1.0b20 dmb: set changes flag on edittext keystroke
	*/
	
	hdlcard hc = iowadata;
	char ch = keyboardstatus.chkb;
	tydirection dir = keyboardstatus.keydirection;
	hdleditrecord hedit;
	
	#ifdef flstatusbar 
	
		if (statusbarkeystroke ()) /*keystroke consumed by the status bar*/
			return (true);
	
	#endif
	
	if (ch == chreturn) {
		
		if (newreturnkeyobject ())
			return (true);
		}
	
	if ((keyboardstatus.floptionkey) && (dir != nodirection)) {
		
		alignselectedobjects (dir);
		
		return (true);
		}
	
	if (ch == chenter) {
	
		if ((**hc).activetextobject != nil) 
			enterarrowmode ();
		else
			entertextmode ();
		
		return (true);
		}
	
	hedit = getactiveeditrecord ();
	
	if (hedit == nil) { /*no active text object*/
	
		if (ch == chbackspace) 		
			deleteselectedobjects ();

		if (ch == chtab) 			
			moveobjectcursor (keyboardstatus.flshiftkey);
		
		iowaupdatenow (); /*11/7/92 DW: zippy performance*/
		
		#ifdef flstatusbar 

			iowachecknameandscriptdisplay (); /*11/7/92 DW: zippy performance*/
		
		#endif
		
		return (true);
		}
	else { /*there is an active text object*/
		
		if (ch == chtab) { /*new feature in 1.0b15 -- tab key when in edit mode*/
			
			hdlobject h;
				
			h = (**hc).activetextobject;
			
			clearactivetextobject ();
			
			addtoselection (h);
			
			while (true) {
				
				moveobjectcursor (keyboardstatus.flshiftkey);
			
				h = (**hc).selectionlist;
			
				makeselectionempty ();

				if (setactivetextobject (h)) {
					
					editselectall (getactiveeditrecord ());
					
					validobject (h);
					
					return (true);
					}
				} /*while*/
			}
		else {
			editkeystroke (ch, hedit);
			
			resizeactivetextobject ();
			
			madechanges (); /*1.0b20 dmb*/
			
			return (true);
			}
		}
	} /*iowakeystroke*/
	

static boolean iowagetpicture (PicHandle *hpicture) {
	
	hdlcard hc = iowadata;
	hdlappwindow ha = app.appwindow;
	WindowPtr w = (**ha).macwindow;
	Rect r = (**hc).pictrect;
	Point pt;
	
	*hpicture = nil; /*return value if error*/
	
	if (hc == nil) /*defensive driving*/
		return (false);
	
	pushmacport (w);
	
	apppushclip (r);
	
	*hpicture = OpenPicture (&r);
	
	pt = (**ha).scrollorigin; /*save so we can restore it*/
	
	(**ha).scrollorigin.h = 0;
	
	(**ha).scrollorigin.v = 0;
	
	iowaupdate ();
	
	(**ha).scrollorigin = pt;
	
	ClosePicture ();
	
	apppopclip ();
	
	popmacport ();
	
	return (true);
	} /*iowagetpicture*/
	
	
static boolean iowaputpicture (PicHandle hpict) {
	
	tyobject obj;
	hdlobject h;
	
	if (getsingleselectedobject (&h)) {
		
		if ((**h).objecttype == picturetype) { /*pasting into a picture object*/
			
			if ((**h).objectdata != nil)
				KillPicture ((PicHandle) (**h).objectdata);
			
			(**h).objectdata = (Handle) hpict;
			
			goto exit;
			}
		}
	
	defaultobject (&obj); /*create a new picture object*/
	
	obj.objecttype = picturetype;
	
	obj.objectrect = (**hpict).picFrame;
	
	obj.objectdata = (Handle) hpict;
	
	if (!newobject (obj, &h)) 
		return (false);
	
	makeselectionempty ();
	
	addtoselection (h);
	
	exit:
	
	invalobject (h);
		
	return (true);
	} /*iowaputpicture*/
	
	
static boolean iowagettext (Handle *htext) {
	
	hdleditrecord hedit;
	
	*htext = nil;
	
	hedit = getanyactiveeditrecord ();
	
	if (hedit == nil)
		return (false);
	
	return (editgetselectedtexthandle (hedit, htext));
	} /*iowagettext*/


static boolean iowaputtext (Handle htext) {
	
	hdlcard hc = iowadata;
	hdleditinfo he = (hdleditinfo) (**hc).refcon;
	hdleditrecord hedit;
	
	hedit = getanyactiveeditrecord ();
	
	if (hedit != nil) {
		
		boolean flstatusbar = hedit == (**he).heditbuffer;
		
		if (flstatusbar)
			apppostcallback ();
		
		editreplacehandle (htext, hedit);
		
		madechanges (); /*dmb 1.0b20*/
		
		if (flstatusbar) {
		
			appprecallback ();
		
			#ifdef flstatusbar 

				invalstatusbar ();
			
			#endif
			}
		else
			resizeactivetextobject ();
		
		return (true);
		}
	
	/*create a new static text object*/ {
	
		Rect r;
		
		r.left = r.right = (**he).lastclickpoint.h;
	
		r.top = r.bottom = (**he).lastclickpoint.v;
	
		if (newdraggedobject (statictexttype, true, r)) {
			
			editreplacehandle (htext, getactiveeditrecord ());
			
			resizeactivetextobject ();
			
			enterarrowmode ();
			
			return (true);
			}
		}
	
	return (false);
	} /*iowaputtext*/
	
	
static boolean iowaclear (void) {

	hdlcard hc = iowadata;
	hdleditinfo he = (hdleditinfo) (**hc).refcon;
	hdleditrecord hedit;
	
	hedit = getanyactiveeditrecord ();
	
	if (hedit != nil) {
	
		boolean flstatusbar = hedit == (**he).heditbuffer;
		
		if (flstatusbar)
			apppostcallback ();
			
		editreplace ("\p", hedit);
		
		madechanges (); /*dmb 1.0b20*/
		
		if (flstatusbar) {
		
			appprecallback ();
		
			#ifdef flstatusbar 
			
				invalstatusbar ();
			
			#endif
			}
		else
			resizeactivetextobject ();

		return (true);
		}
	
	/*not editing text*/
	
	if (nothingselected ()) 
		return (false);
		
	deleteselectedobjects (); 
	
	iowaupdatenow (); /*4/11/93 DW: zippy performance*/
	
	#ifdef flstatusbar 

		iowachecknameandscriptdisplay (); /*4/11/93 DW: zippy performance*/
		
	#endif
	
	return (true);
	} /*iowaclear*/


static void afternewsetpaletteitem (void) {
	
	/*
	special little deal for after you create a new object. we automatically
	switch the palette and the mode to reflect the fact that you're either
	editing or not editing.
	*/
	
	if (getactiveeditrecord () == nil) /*not in edit mode*/
		iowaselectpaletteitem (arrowmode);
	else
		iowaselectpaletteitem (textmode);
	} /*afternewsetpaletteitem*/
	

static void iowatrackmouse (hdlobject x) {
	
	hdlcard hc = iowadata;
	hdleditinfo he = (hdleditinfo) (**hc).refcon;
	Point origpt, lastpt, pt;
	Rect r;
	boolean flfirstloop = true;
	unsigned long origticks;
	short hdiff = 0, vdiff = 0;
	short oldhdiff = 0, oldvdiff = 0;
	boolean vertenabled = true, horizenabled = true;
	boolean flshifted;
	Rect runion, rnewunion;
	boolean inarrowmode;
	
	inarrowmode = (**he).mode == arrowmode;
		
	/*GetMouse (&origpt);*/
	
	origpt = mousestatus.localpt; /*1.0b15*/
	
	flshifted = shiftkeydown ();
	
	if (x == nil) { /*mouse not pointing at any object*/
	
		clearactivetextobject (); /*if we were in edit mode, unload the text and go to arrow mode*/
		
		if (dragborders ())
			return;
		
		if (!flshifted) {
		
			makeselectionempty ();
			
			iowaupdatenow ();
			}
	
		if (dragtoselect (&r)) {
			
			if (inarrowmode)
				selectinrect (r);
			else {
				newdraggedobject (modetoobjecttype ((**he).mode), false, r);
				
				afternewsetpaletteitem ();
				}
			
			return;
			}

		if (!inarrowmode) { /*single-clicked to make a new object*/
		
			r.top = r.bottom = origpt.v;
			
			r.left = r.right = origpt.h;
			
			newdraggedobject (modetoobjecttype ((**he).mode), true, r);
			
			afternewsetpaletteitem ();
			}
		
		return;
		}
		
	if (!flshifted) {
	
		if (!inselection (x)) {
		
			makeselectionempty ();
			
			iowaupdatenow ();
			}
		}
		
	/*GetMouse (&origpt);*/
	
	origpt = mousestatus.localpt; /*1.0b15*/
	
	origticks = TickCount ();
	
	while (true) { /*wait until it qualifies as a dragging operation*/
		
		Point newpt;
		
		if (!StillDown ()) { /*user let up on mouse button, finished tracking*/
			
			/*
			unhighlight original selection, highlight new selection
			*/ 
				
			if (flshifted) { /*implement shift-click*/
				
				if (inselection (x))
					unlinkfromselection (x);
				else
					addtoselection (x);
				}
			else {
				setselectionto (x); /*un-shifted click*/
				
				/*makefrontobject (x);*/ /*7/1/92 DW*/
				}
			
			return;
			}
			
		GetMouse (&newpt);
		
		if (pointdist (origpt, newpt) > draggingpix) /*mouse moved far enough to call it a drag*/
			break;
			
		if ((TickCount () - origticks) > draggingticks) /*enough time has passed to call it a drag*/
			break;
		} /*while*/
	
	mousestatus.fldoubleclickdisabled = true;
	
	if (!inselection (x)) {
	
		addtoselection (x);
		
		iowaupdatenow ();
		}
		
	getobjectrect (x, &r);
	
	if (pointinnub (x, origpt)) {
		
		if (dragtogrow (x, &r)) /*r holds the new size of the object*/
			growselectedobjects (r);
		
		return;
		}
		
	pushpen ();
	
	PenPat (&quickdrawglobal (gray)); 
		
	PenMode (patXor);
	
	pushhighlightcolor (&(**iowadata).backcolor);
	
	lastpt = origpt;
	
	while (StillDown ()) { /*user is moving the selected objects*/
		
		GetMouse (&pt);
		
		if (pointdist (pt, lastpt) < 1) 
			continue;
			
		oldhdiff = hdiff;
		
		oldvdiff = vdiff;
		
		hdiff = pt.h - origpt.h;
		
		vdiff = pt.v - origpt.v;
		
		if (!flshifted)
			if (shiftkeydown ())
				flshifted = true;
		
		if (flshifted) {
			
			if (horizenabled && vertenabled) { /*haven't decided yet*/
				
				if (hdiff > vdiff) { /*horiz only*/
					
					horizenabled = true;
					
					vertenabled = false;
					}
				else {
					horizenabled = false; /*vert only*/
					
					vertenabled = true;
					}
				}
			}
		
		if (!vertenabled)
			vdiff = 0;
			
		if (!horizenabled)
			hdiff = 0;
			
		unionselectedrects (hdiff, vdiff, &rnewunion);
			
		if (equalrects (rnewunion, runion)) /*avoid unsightly flicker*/
			continue;
			
		waitfornexttick (); /*heuristic to avoid flicker*/
		
		if (!flfirstloop) /*erase 'em*/
			frameselectedobjects (oldhdiff, oldvdiff);
						
		flfirstloop = false;
		
		frameselectedobjects (hdiff, vdiff);
		
		unionselectedrects (hdiff, vdiff, &runion);
		
		/*rectstatus (runion);*/
		
		lastpt = pt;
		} /*while*/
	
	if (!flfirstloop) /*erase the gray frame*/
		frameselectedobjects (hdiff, vdiff);
	
	popforecolor ();
	
	poppen ();
	
	moveselectedobjects (hdiff, vdiff);
	} /*iowatrackmouse*/


static boolean textclick (hdlobject hclickedobject) {
	
	/*
	return true if the click was consumed by a text editing operation.
	*/
	
	hdlcard hc = iowadata;
	hdlobject hactive = (**hc).activetextobject;
	Rect r;
	
	if (hactive == nil) /*no active text object*/
		return (false);
		
	if (hactive != hclickedobject) {
	
		if (!geteditrect (hclickedobject, &r)) { /*clicked-on object can't be edited*/
			
			sysbeep;
			
			return (true);
			}
	
		clearactivetextobject ();
	
		setactivetextobject (hclickedobject);
		}
	
	editclickbottleneck (mousestatus.localpt, keyboardstatus.flshiftkey, (hdleditrecord) (**hclickedobject).objecteditrecord);
	
	return (true); /*consume the mouse click*/
	} /*textclick*/
	
	
static boolean iowamouse (void) {
	
	/*
	the mouse clicked in the content area of an Iowa window.
	*/
	
	hdlcard hc = iowadata;
	hdleditinfo he = (hdleditinfo) (**hc).refcon;
	hdlobject clickedobject;
	
	#ifdef flstatusbar 
		closestatusbareditor (); 
	#endif
	
	if (findobject (mousestatus.localpt, (**hc).objectlist, &clickedobject)) {
		
		if (!textclick (clickedobject)) { /*mouse click not consumed by editing operation*/
		
			if (mousestatus.fldoubleclick)
				maininfowindow ();
			else
				iowatrackmouse (clickedobject);
			}
		}
	else {
		iowatrackmouse (nil); /*mouse not pointing at any object*/
		
		(**he).lastclickpoint = mousestatus.localpt;
		}
	
	return (true);
	} /*iowamouse*/
	

static short modetocursor (short mode) {
	
	/*
	11/10/92 DW: turned off this feature -- it was very annoying having to
	learn and remember how all these different cursors work. saved a copy 
	in iowaoldcode.c.
	
	instead, when you're creating a new object you get a cross-hair cursor,
	otherwise you get an arrow or an ibeam.
	*/
	
	short x;
	
	switch (mode) {
		
		case arrowmode:
			x = cursorisarrow; break;
			
		case textmode:
			x = cursorisibeam; break;
			
		default:
			x = crosshaircursor; break;
		} /*switch*/
		
	return (x);
	} /*modetocursor*/
	
	
static void iowasetcursor (void) {

	hdlcard hc = iowadata;
	hdleditinfo he = (hdleditinfo) (**hc).refcon;
	short mode = (**he).mode;
	short cursor = cursorisarrow;
	hdlobject hparent, h;
	Point pt;
	
	#ifdef flstatusbar 
	
		if (iowacursorinstatusbar ())
			return;
			
	#endif
		
	GetMouse (&pt);
	
	if (!findterminalobject (pt, (**hc).objectlist, &hparent, &h)) {
		
		Rect rcontent = (**app.appwindow).contentrect;
		
		pt.h += rcontent.left;
		
		pt.v += rcontent.top;
		
		if (PtInRect (pt, &rcontent)) /*pointing at empty drawing space*/
			cursor = modetocursor (mode);
		
		goto exit;
		}
		
	if ((mode == textmode) && caneditobjecttext (h))
		cursor = cursorisibeam;
	
	exit:
	
	setcursortype ((tycursortype) cursor);
	} /*iowasetcursor*/
	
	
static boolean iowaidle (void) {
	
	hdlcard hc = iowadata;
	hdleditinfo he;
	
	if (hc == nil) { /*rest of routine applies only if a window is open*/

		iowaadjustmenus (); /*menus reflect fact that no windows are open*/

		return (true);
		}
		
	he = (hdleditinfo) (**hc).refcon;
		
	if ((**hc).needsupdate) {
		
		(**hc).needsupdate = false; /*consume it*/
		
		iowapreupdate (); /*actually inval everything that's been logically inval'd*/
		}
	
	if ((**hc).fullupdate) {
		
		(**hc).fullupdate = false; /*consume it*/
		
		zapcardwindow ();
		}
		
	if (!emptyrect ((**hc).deferredinvalrect)) {
		
		Rect r = (**hc).deferredinvalrect;
		
		iowainvalrect (&r);
		
		zerorect (&r);
		
		(**hc).deferredinvalrect = r;
		}
	
	if ((**hc).flactive) {
	
		iowaadjustmenus (); /*the active window owns the menus*/
	
		iowasetcursor ();
	
		editidle (getactiveeditrecord ());
		}
	
	if ((**he).initcalculatedobjects) {
		
		hdlobject x = (**iowadata).objectlist;
	
		(**he).initcalculatedobjects = false;
		
		frontSetRuntimeCard (true, false);
	
		visitobjects (x, &majorrecalcvisit);
	
		visitobjects (x, &initexclusiveobjectsvisit);
		
		turnonfirstexclusiveobject (x);
		}
	else 
		checkrecalc (); /*all windows can recalc, even if they're not the frontmost*/
	
	if (showinvisiblesloop ())
		iowaupdatenow ();
	
	return (true);
	} /*iowaidle*/
	

static boolean iowaactivate (boolean flactive) {
	
	hdlcard hc = iowadata;
	
	(**hc).flactive = flactive;
	
	#ifdef flstatusbar 
		
		iowaactivatestatusbar (flactive); /*deal with editable name/script fields*/
	
	#endif
	
	editactivate (getactiveeditrecord (), flactive);
	
	if (flactive)
		frontSetRuntimeCard (true, false); /*make this window's table global for scripts*/
	
	return (true);
	} /*iowaactivate*/
	
	
typedef struct tysetselect {
	
	short font, size, style, justification;
	
	boolean flhavefont, flhavesize, flhavestyle, flhavejustification;
	
	boolean flfontaware;
	} tysetselect;
	
tysetselect setselect;


static boolean setselectvisit (hdlobject h) {
	
	if (!setselect.flfontaware) 
		setselect.flfontaware = callfontaware (h);
		
	if (setselect.font == -1) { /*this is the first node visited*/
		
		setselect.font = (**h).objectfont;
		
		setselect.size = (**h).objectfontsize;
		
		setselect.style = (**h).objectstyle;
		
		setselect.justification = (**h).objectjustification;
		
		return (true);
		}
	
	if ((**h).objectfont != setselect.font) 
		setselect.flhavefont = false;
		
	if ((**h).objectfontsize != setselect.size) 
		setselect.flhavesize = false;
		
	if ((**h).objectstyle != setselect.style) 
		setselect.flhavestyle = false;
	
	if ((**h).objectjustification != setselect.justification) 
		setselect.flhavejustification = false;
	
	if ((!setselect.flhavefont) && (!setselect.flhavesize) && 
	   (!setselect.flhavestyle) && (!setselect.flhavejustification))
		return (false);
		
	return (true);
	} /*setselectvisit*/
	
	
static boolean iowasetselectioninfo (void) {

	hdlcard hc = iowadata;
	tyselectioninfo x;
	
	if ((**hc).activetextobject != nil) { /*we're editing an object*/
		
		x.flcansetfont = false;
	
		x.flcansetsize = false;
	
		x.flcansetstyle = false;
	
		x.flcansetjust = false;
		
		(**app.appwindow).selectioninfo = x;
		
		return (true);
		}
	
	setselect.font = -1;
	
	setselect.size = -1;
	
	setselect.style = -1;
	
	setselect.justification = -1;
	
	setselect.flhavefont = true;
	
	setselect.flhavesize = true;
	
	setselect.flhavestyle = true;
	
	setselect.flhavejustification = true;
	
	setselect.flfontaware = false; /*turns true if one of the selected objects cares about font/size/siyle*/
	
	flatvisitselectedobjects (&setselectvisit);

	clearbytes (&x, longsizeof (x));
	
	x.flcansetfont = setselect.flfontaware;
	
	x.flcansetsize = setselect.flfontaware;
	
	x.flcansetstyle = setselect.flfontaware;
	
	x.flcansetjust = setselect.flfontaware;
	
	if (setselect.flhavefont)
		x.fontnum = setselect.font;
	else
		x.fontnum = -1; /*indicate no uniform font*/
	
	if (setselect.flhavesize)
		x.fontsize = setselect.size;
	else
		x.fontsize = -1;
	
	if (setselect.flhavestyle) {
		
		short style = setselect.style;
		
		if (style != -1) {
		
			x.flplain = style == 0;
				
			x.flbold = style & bold;
			
			x.flitalic = style & italic;
			
			x.flunderline = style & underline;
			
			x.floutline = style & outline;
			
			x.flshadow = style & shadow;
			}
		}
		
	if (setselect.flhavejustification)
		x.justification = (tyjustification) setselect.justification;
	else
		x.justification = unknownjustification;
	
	(**app.appwindow).selectioninfo = x;
	
	#ifdef flstatusbar 

		iowachecknameandscriptdisplay (); /*change the display for object name, linked script*/
	
	#endif
	
	return (true);
	} /*iowasetselectioninfo*/
	

static boolean setfontvisit (hdlobject h) {
	
	tyselectioninfo x = (**app.appwindow).selectioninfo;
	
	if (!callfontaware (h)) /*it's not font-aware, keep visiting*/
		return (true);
	
	if (x.fontnum != -1)
		(**h).objectfont = x.fontnum;
	
	if (x.fontsize != -1)
		(**h).objectfontsize = x.fontsize;

	invalobject (h);
	
	return (true);
	} /*setfontvisit*/
	
	
static boolean iowasetfont (void) {

	flatvisitselectedobjects (&setfontvisit);
	
	return (true);
	} /*iowasetfont*/


static boolean setstylevisit (hdlobject h) {
	
	tyselectioninfo x = (**app.appwindow).selectioninfo;
	short style = 0;
	
	if (!callfontaware (h)) /*it's not font-aware, keep visiting*/
		return (true);
	
	if (x.flbold)
		style += bold;
	
	if (x.flitalic)
		style += italic;
	
	if (x.flunderline)
		style += underline;
	
	if (x.floutline)
		style += outline;
	
	if (x.flshadow)
		style += shadow;
		
	if (x.flplain) /*over-rides all others*/
		style = 0;
		
	(**h).objectstyle = style;
	
	invalobject (h);
	
	return (true);
	} /*setstylevisit*/
	
	
static boolean iowasetstyle (void) {

	flatvisitselectedobjects (&setstylevisit);
	
	return (true);
	} /*iowasetstyle*/


static boolean setjustificationvisit (hdlobject h) {
	
	if (!callfontaware (h)) /*it's not font-aware, keep visiting*/
		return (true);
	
	(**h).objectjustification = (**app.appwindow).selectioninfo.justification;
	
	invalobject (h);
	
	return (true);
	} /*setjustificationvisit*/
	
	
static boolean iowasetjustification (void) {

	flatvisitselectedobjects (&setjustificationvisit);
	
	return (true);
	} /*iowasetjustification*/


static boolean iowagetcontentsize (void) {
	
	hdlcard hc = iowadata;
	hdlappwindow ha = app.appwindow;
	short height, width;
	
	iowasetpictrect ();
	
	height = (**hc).pictrect.bottom;
	
	if (height < (**hc).bottomborder)
		height = (**hc).bottomborder;
	
	width = (**hc).pictrect.right;
	
	if (width < (**hc).rightborder)
		width = (**hc).rightborder;
	
	(**ha).zoomheight = height;
	
	(**ha).zoomwidth = width;
	
	return (true);
	} /*iowagetcontentsize*/
	
	
static boolean iowapalettehit (void) {
	
	hdlappwindow ha = app.appwindow;
	hdlpaletterecord hpal = (hdlpaletterecord) (**ha).hpalette;
	
	appprecallback ();
			
	switch ((**hpal).itemselected) {
		
		case arrowmode:
			enterarrowmode ();
			
			break;
			
		case textmode:
			if (!entertextmode ())
				goto exit;
			
			break;
		} /*switch*/
	
	iowaselectpaletteitem ((**hpal).itemselected);
			
	exit:
	
	apppostcallback ();
	
	return (true);
	} /*iowapalettehit*/
	
	
static void appletupdate (void) {
	
	apppostcallback ();
	
	updateappwindow (app.appwindow);
	
	appprecallback ();
	} /*appletupdate*/
	

static boolean iowanewrecord (void) {
	
	hdlappwindow ha = app.appwindow;
	hdlpaletterecord hpal = (hdlpaletterecord) (**ha).hpalette;
	hdleditinfo he;
	hdlcard hc;
	tyobject defaults;
	
	if (!newclearhandle (longsizeof (tycard), (Handle *) &iowadata))
		return (false);
		
	if (!newclearhandle (longsizeof (tyeditinfo), (Handle *) &he)) {
		
		disposehandle ((Handle) iowadata);
		
		iowadata = nil;
		
		return (false);
		}
		
	app.appdata = (Handle) iowadata;
	
	hc = iowadata;
	
	(**hc).refcon = (long) he;
	
	initIOAcallbacks ();
	
	(**he).initcalculatedobjects = true; /*DW 12/28/92 -- set to true, eliminated race condition*/
	
	(**he).flnubonborder = true;
	
	(**hc).backcolor = whitecolor;
	
	(**hc).updatecallback = &appletupdate;
	
	(**hc).flgrid = true;
	
	(**hc).gridunits = initialgridunits;
	
	(**hc).flautoname = true;
	
	(**hc).idwindow = 1024; /*basic modal dialog*/
	
	/*set up defaultnewobjectlocation*/ {
		
		Point pt;
		
		pt.h = pt.v = initialgridunits;
		
		(**hc).defaultnewobjectlocation = pt;
		}
	
	/*set up the initial borders*/ {
		
		Rect r = (**ha).contentrect;
		short scrollbarwidth;
		
		scrollbarwidth = /*getscrollbarwidth ()*/ 0; 
	
		(**hc).rightborder = r.right - r.left - scrollbarwidth;
	
		(**hc).bottomborder = r.bottom - r.top - scrollbarwidth;
		}
	
	/*initialize lastclickpoint*/ {
	
		Point pt;
		
		pt.h = (**hc).rightborder / 2;
		
		pt.v = (**hc).bottomborder / 2;
		
		(**he).lastclickpoint = pt;
		}
		
	/*hpal = newpalette (ha, ctpaletteicons);*/
	
	(**hpal).sicnresource = 132;
	
	(**hpal).itemhitcallback = &iowapalettehit;
	
	(**hpal).item [textmode].breakafter = true;
	
	/*
	(**hpal).item [formulamode].breakafter = true;
	
	(**hpal).item [picturemode].breakafter = true;
	*/
	
	/*(**ha).hpalette = (Handle) hpal;*/
	
	(**ha).scrollorigin.h = -(**hpal).palettewidth;
	
	(**ha).scrollorigin.v = -(**ha).contentrect.top; 
	
	#ifdef flstatusbar 

		iowainitstatusbar ();
	
	#endif
	
	clearbytes (&defaults, longsizeof (defaults)); /*used in newdraggedobject*/
	
	defaults.objectfillcolor = whitecolor;
	
	defaults.objecttextcolor = blackcolor;
	
	defaults.objectframecolor = blackcolor;
	
	(**hc).defaults = defaults;
	
	if (!app.openingfile)
		if (findfrontiercomponent ())
			frontStartCard (nil); /*set up a table for the new card*/
	
	return (true);
	} /*iowanewrecord*/


static boolean iowawindowresize (void) {
	
	#ifdef flstatusbar 

		apppostcallback ();
		
		invalstatusbar ();
		
		appprecallback ();
		
	#endif
	
	return (true);
	} /*iowawindowresize*/
	

static boolean iowapagesetup (void) {
	
	return (true);
	} /*iowapagesetup*/


static boolean iowaopenprint (void) {
	
	hdlcard hc = iowadata;
	Rect rpict, rpaper;
	PicHandle hpict;
	short ctpages, vpictpixels;
	
	iowagetpicture (&hpict);
	
	(**hc).macpicture = hpict;
	
	rpict = (**hpict).picFrame;
	
	rpaper = app.printinfo.paperrect;
	
	vpictpixels = rpict.bottom - rpict.top;
	
	ctpages = vpictpixels / app.printinfo.vpagepixels;
	
	if (vpictpixels > (ctpages * app.printinfo.vpagepixels))
		ctpages++;
	
	app.printinfo.ctpages = ctpages;
	
	return (true);
	} /*iowaopenprint*/


static boolean iowaprintpage (pagenumber) short pagenumber; {
	
	hdlcard hc = iowadata;
	PicHandle macpicture = (**hc).macpicture;
	Rect rpict, rpaper;
	short hoffset, voffset;
	
	rpict = (**macpicture).picFrame;
	
	rpaper = app.printinfo.paperrect;
	
	hoffset = rpaper.left - rpict.left;
	
	voffset = (rpaper.top - rpict.top) - ((pagenumber - 1) * app.printinfo.vpagepixels) - 1;
	
	OffsetRect (&rpict, hoffset, voffset);
	
	DrawPicture (macpicture, &rpict);
	
	return (true);
	} /*iowaprintpage*/
	
	
static boolean iowacloseprint (void) {
	
	hdlcard hc = iowadata;
	PicHandle hpict = (**hc).macpicture;
	
	if (hpict != nil) {
		
		KillPicture (hpict);
		
		(**hc).macpicture = nil;
		}
	
	return (true);
	} /*iowacloseprint*/


static boolean iowafilefilter (tyfileinfo *info) {
	
	/*
	if we return false, the file appears in the dialog.
	*/
	
	OSType c = (*info).filecreator;
	OSType t = (*info).filetype;
	
	if (t == app.filetype) /*1.0b20 dmb -- don't check: (c == app.creator) */
		return (false); 
		
	if ((c == 'DRPb') && (t == 'APPL')) 
		return (false); 
		
	return (true);
	} /*iowafilefilter*/
	
	
static boolean getlinefromhandle (Handle h, unsigned long lnum, bigstring bs) { /*DW 12/31/95*/
	
	unsigned long ix = 0;
	unsigned long ct = gethandlesize (h);
	unsigned long linecount = 0;
	unsigned char ch;
	short lenstring;
	
	while (true) {
		
		if (linecount >= (lnum - 1))
			break;
			
		if (ix >= ct) /*ran out of chars, there aren't that many lines*/
			return (false);
		
		ch = (*h) [ix++];
		
		if (ch == '\r') 
			linecount++;
		} /*while*/
	
	lenstring = 0;
	
	bs [0] = 0;
	
	while (true) {
		
		if (ix >= ct)
			break;
			
		ch = (*h) [ix++];
		
		if (ch == '\r') 
			break;
		
		if (lenstring < 255) {
			
			lenstring++;
			
			bs [0] = lenstring;
			
			bs [lenstring] = ch;
			}
		} /*while*/
	
	return (true);
	} /*getlinefromhandle*/
	
	
static boolean macbirdunpack (Handle hpacked) { /*DW 12/31/95 -- handle net open from MacBird server*/ 
	
	/*
	return true if we processed it
	
	the server returns a handle that begins with two lines:
	
	MacBird 1.0 -- 12/31/95\r
	run maconly\r
	
	followed by a packed card.
	
	we look for this kind of packed handle.
	*/
	
	bigstring line1, line2;
	
	if (!getlinefromhandle (hpacked, 1, line1))
		return (false);
	
	if (!getlinefromhandle (hpacked, 2, line2))
		return (false);
	
	if (!equalstrings (line1, "\pMacBird 1.0 -- 12/31/95"))
		return (false);
	
	if (!equalstrings (line2, "\prun maconly"))
		return (false);
	
	deleteinhandle (hpacked, 0, stringlength (line1) + stringlength (line2) + 2);
	
	if (flprocessingeditevent) { /*handling an Apple Event that explicitly asks us to edit this file not run it*/
		
		flprocessingeditevent = false; /*consume it*/
		
		return (false);
		}
	
	appruncard (hpacked);
	
	return (true); /*we handled it*/
	} /*macbirdunpack*/
	

static boolean iowaunpackcallback (Handle hpacked) {
	
	/*
	interface for card editor to iowaunpack
	
	DW 1/14/95: Iowa must run even if Frontier is not
	running.
	
	dmb 1.0b19: moved check for 'LAND' component and control of 
	tablestoredinfrontier flag into frontStartCard
	*/
	
	if (macbirdunpack (hpacked)) /*DW 12/31/95*/
		return (false);
	
	if (!iowaunpack (hpacked))
		return (false);
	
	if (findfrontiercomponent ())
		frontStartCard (nil);
	
	return (true);
	} /*iowaunpackcallback*/
	

static boolean iowapackcallback (Handle *hpacked) {
	
	/*
	interface for card editor to iowapack
	
	1.0b20 dmb: if Frontier disappeared on us, we repack the original 
	table and not fail with an error.
	*/
	
	if ((**iowadata).tablestoredinfrontier) {
	
		if (findfrontiercomponent () && !frontGetTable ())
			return (false);
		}
	
	return (iowapack (hpacked));
	} /*iowapackcallback*/
	
	
static boolean iowaafteropen (void) {
	
	fslastfileopened = (**app.appwindow).filespec;
	
	return (true);
	} /*iowaafteropen*/
	
	
static void iowafillapprecord (void) {

	clearbytes (&app, longsizeof (app)); /*init all fields to 0*/
	
	app.creator = 'IOWA';
	
	app.filetype = 'CARD';
	
	app.filefiltercallback = iowafilefilter;
	
	app.usetempmemory = true;
	
	app.resizeable = true;
	
	app.eraseonresize = false;
	
	app.minwindowwidth = 300;
		
	app.minwindowheight = 150;
	
	app.haswindowmenu = true;
	
	#ifdef flstatusbar
	
		app.statuspixels = 24; /*height of status bar at top of window*/
		
	#endif
			
	app.usecolor = true;
	
	app.defaultfont = systemFont;
	
	app.defaultsize = 12;
	
	app.defaultstyle = 0;
	
	app.setglobalscallback = &iowasetglobals;
	
	app.newrecordcallback = &iowanewrecord;
	
	app.disposerecordcallback = &iowadisposerecord;
	
	app.idlecallback = &iowaidle;
	
	app.activatecallback = (tyappbooleancallback) &iowaactivate;
	
	app.updatecallback = &iowaupdate;
	
	app.preupdatecallback = &iowapreupdate;
	
	app.windowresizecallback = &iowawindowresize;
	
	app.iacmessagecallback = &handleverb;
	
	app.iacfastmessagecallback = &handlefastverb;
	
	app.packcallback = &iowapackcallback;
	
	app.unpackcallback = &iowaunpackcallback;
	
	app.openprintcallback = &iowaopenprint;
	
	app.pagesetupcallback = &iowapagesetup;
	
	app.printpagecallback = (tyappshortcallback) &iowaprintpage;
	
	app.closeprintcallback = &iowacloseprint;
	
	app.haveselectioncallback = &iowahaveselection;
	
	app.selectallcallback = &iowaselectall;
	
	app.keystrokecallback = &iowakeystroke;
	
	app.mousecallback = &iowamouse;
	
	app.putpictcallback = (tyapphandlecallback) &iowaputpicture;
	
	app.gettextcallback = &iowagettext;
	
	app.puttextcallback = &iowaputtext;
	
	app.menucallback = (tymenucallback) &iowamenu;
	
	app.insertmenucallback = &iowainsertmenus;
	
	app.setselectioninfocallback = &iowasetselectioninfo;
	
	app.setfontcallback = &iowasetfont;
	
	app.setsizecallback = &iowasetfont;
	
	app.setstylecallback = &iowasetstyle;
	
	app.setjustifycallback = &iowasetjustification;
	
	app.copycallback = &iowacopy;
	
	app.clearcallback = &iowaclear; 
	
	app.pastecallback = &iowapaste;
	
	app.getcontentsizecallback = &iowagetcontentsize;
	
	#ifdef flstatusbar 

		app.updatestatuscallback = &iowadrawstatusbar;
		
		app.mouseinstatuscallback = &iowamouseinstatus;
	
	#endif
	
	app.alertcallback = &alertdialog;
	
	app.afteropenwindowcallback = &iowaafteropen;
	} /*iowafillapprecord*/
	
	
void main (void) {
	
	iowafillapprecord (); /*fill in the fields of the app record*/
	
	iowaInit (); /*set up components for Iowa Runtime*/
	
	appletinitmanagers ();
		
	app.haspalette = true;
	
	app.ctpaletteicons = iconsinpalette;
	
	displaydebug = false;
	
	installattributeshandler ();
	
	iowaloadprefs ();
	
	runapplet ();
	
	iowasaveprefs ();
	
	iowaClose ();
	} /*main*/