
/*© copyright 1991-96 UserLand Software, Inc. All Rights Reserved.*/


#include <math.h>
#include <menusharing.h>

#include <iac.h>


#define sintoken			'sine'
#define costoken			'cos '
#define tantoken			'tan '

#define sinhtoken			'sinh'
#define coshtoken			'cosh'
#define tanhtoken			'tanh'

#define asintoken			'asin'
#define acostoken			'acos'
#define atantoken			'atan'

#define logtoken			'log '
#define log10token			'lg10'

#define exptoken			'exp '
#define abstoken			'abs '
#define sqrttoken			'sqrt'
#define ceiltoken			'ceil'
#define floortoken			'floo'
#define modtoken			'mod '

#define setmessagetoken		'smsg'

int errno; /*see math.c*/


#define applemenu 128 /*the resource id of the apple menu*/

#define aboutitem 1 /*the about command*/

#define filemenu 129 /*resource id of file menu, shared menus appear to right of this*/

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

#define quititem 1 /*the single item in the file menu -- this is a very simple program!*/

MenuHandle happlemenu, hfilemenu; /*the two fixed, non-shared menus in this program*/

WindowPtr mainwindow = nil; /*the menu sharing test window*/

Str255 windowmessage; /*the message that's displayed in the main window*/

short flexitmainloop = false; /*when true we fall thru the main event loop*/
	
	


static void copystring (Str255 source, Str255 dest) {

	/*
	create a copy of source in dest.  copy the length byte and
	all the characters in the source string.

	assume the strings are pascal strings, with the length byte in
	the first character of the string.
	*/

	register short i, len;
	
	len = (short) source [0];
	
	for (i = 0; i <= len; i++) 
		dest [i] = source [i];
	} /*copystring*/


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


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

	TextFont (fontnum);
	
	TextSize (fontsize);
	
	TextFace (fontstyle);
	} /*setfontsizestyle*/
	
	
static void updatemainwindow (void) {
	
	Rect r;
	Str255 s;
	
	r = (*mainwindow).portRect;
	
	EraseRect (&r);
	
	setfontsizestyle (geneva, 9, 0);
	
	centerstring (r, windowmessage);
	
	NumToString (FreeMem () / 1024, s);
	
	MoveTo (r.left + 3, r.bottom - 3);
	
	setfontsizestyle (geneva, 9, 0);
	
	DrawString (s);
	
	DrawString ("\pK");
	} /*updatemainwindow*/
	
	
static Boolean setwindowmessage (Str255 s) {
	
	copystring (s, windowmessage);
	
	SetPort (mainwindow);
	
	updatemainwindow ();
	
	return (true);
	} /*setwindowmessage*/


static Boolean initmainwindow (void) {
	
	register WindowPtr w;
	
	w = mainwindow = GetNewWindow (128, nil, (WindowPtr) -1);
	
	if (w == nil)
		return (false);
	
	ShowWindow (w);
	
	return (true);
	} /*initmainwindow*/


static void handleupdate (EventRecord *ev) {
	
	register WindowPtr w;
	
	w = (WindowPtr) (*ev).message;
	
	BeginUpdate (w);
	
	SetPort (w);
	
	updatemainwindow ();
	
	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) {
	
	register short idmenu, iditem;
	
	iditem = LoWord (codeword);
	
	idmenu = HiWord (codeword);
	
	if (SharedMenuHit (idmenu, iditem)) 
		goto exit;
	
	switch (idmenu) {
	
		case applemenu: 
			switch (iditem) {
				
				case aboutitem:
					Alert (262, nil);
					
					break;
			
				default: {
				
					Str255 s;
					
					GetItem (happlemenu, iditem, s);
					
					OpenDeskAcc (s);
					
					break;
					}
				} /*switch*/
			
			break; /*apple menu*/

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


static void handlemouse (EventRecord *ev) {

	register short part;
	WindowPtr w;
	
	part = FindWindow ((*ev).where, &w);
	
	if (w != nil) 
	
		if (w != FrontWindow ()) { /*just like all other Mac programs*/
			
			SelectWindow (w);
							
			return; /*the mouse click is consumed by the bringtofront operation*/
			}
	
	switch (part) {
	
		case inMenuBar: 
			handlemenu (MenuSelect ((*ev).where)); 
			
			break;
		
		case inSysWindow:
			SystemClick (ev, w); 
			
			break;
		
		case inDrag:
			handledrag (ev, w);
			
			break;
			
		} /*switch*/
	} /*handlemouse*/


static void handlekeystroke (EventRecord *ev) { 

	register char ch = (*ev).message & charCodeMask;
	
	if (SharedScriptRunning ()) { /*cmd-period terminates the script*/
	
		if (((*ev).modifiers & cmdKey) && (ch == '.')) { 
			
			CancelSharedScript (); /*cancel the shared menu script, if one is running*/
		
			return;
			}
		}
	
	handlemenu (MenuKey (ch)); /*not cmd-period, process the normal way*/
	} /*handlekeystroke*/


static void handleevent (EventRecord *ev) {
	
	switch ((*ev).what) {
	
		case keyDown: case autoKey: 
			handlekeystroke (ev);
			
			break;
			
		case mouseDown:
			handlemouse (ev);
			
			break;
		
		case updateEvt:
			handleupdate (ev);
			
			break;
			
		case kHighLevelEvent:
			AEProcessAppleEvent (ev);
			
			break;
		} /*switch*/
	} /*handleevent*/


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


static void initmenus (void) {
	
	/*
	set up our apple and file menus.  nothing fancy.
	*/
	
	happlemenu = GetMenu (applemenu); 
	
	AddResMenu (happlemenu, 'DRVR'); 
	
	InsertMenu (happlemenu, 0); 
	
	hfilemenu = GetMenu (filemenu); 
	
	InsertMenu (hfilemenu, 0);
	
	DrawMenuBar ();
	} /*initmenus*/


static void initmacintosh (void) {

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

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


static Boolean setmessageverb (void) {

	Str255 s;
	
	if (!IACgetstringparam ((OSType) keyDirectObject, s))
		return (false);
	
	IACreturnboolean (setwindowmessage (s));
	
	return (true);
	} /*setmessageverb*/
	

static Boolean sinverb (void) {
	
	double x;
	
	if (!IACgetdoubleparam ((OSType) keyDirectObject, &x))
		return (false);
	
	x = sin (x);

	IACreturndouble (x);
	
	return (true);
	} /*sinverb*/
	

static Boolean cosverb (void) {
	
	double x;
	
	if (!IACgetdoubleparam ((OSType) keyDirectObject, &x))
		return (false);
		
	x = cos (x);

	IACreturndouble (x);
	
	return (true);
	} /*cosverb*/
	

static Boolean tanverb (void) {
	
	double x;
	
	if (!IACgetdoubleparam ((OSType) keyDirectObject, &x))
		return (false);
		
	x = tan (x);

	IACreturndouble (x);
	
	return (true);
	} /*tanverb*/
	

static Boolean sinhverb (void) {
	
	double x;
	
	if (!IACgetdoubleparam ((OSType) keyDirectObject, &x))
		return (false);
		
	x = sinh (x);

	IACreturndouble (x);
	
	return (true);
	} /*sinhverb*/
	

static Boolean coshverb (void) {
	
	double x;
	
	if (!IACgetdoubleparam ((OSType) keyDirectObject, &x))
		return (false);
		
	x = cosh (x);

	IACreturndouble (x);
	
	return (true);
	} /*coshverb*/
	

static Boolean tanhverb (void) {
	
	double x;
	
	if (!IACgetdoubleparam ((OSType) keyDirectObject, &x))
		return (false);
		
	x = tanh (x);

	IACreturndouble (x);
	
	return (true);
	} /*tanhverb*/
	

static Boolean asinverb (void) {
	
	double x;
	
	if (!IACgetdoubleparam ((OSType) keyDirectObject, &x))
		return (false);
		
	x = asin (x);

	IACreturndouble (x);
	
	return (true);
	} /*asinverb*/
	

static Boolean acosverb (void) {
	
	double x;
	
	if (!IACgetdoubleparam ((OSType) keyDirectObject, &x))
		return (false);
		
	x = acos (x);

	IACreturndouble (x);
	
	return (true);
	} /*acosverb*/
	

static Boolean atanverb (void) {
	
	double x;
	
	if (!IACgetdoubleparam ((OSType) keyDirectObject, &x))
		return (false);
		
	x = atan (x);

	IACreturndouble (x);
	
	return (true);
	} /*atanverb*/
	

static Boolean logverb (void) {
	
	double x;
	
	if (!IACgetdoubleparam ((OSType) keyDirectObject, &x))
		return (false);
		
	x = log (x);

	IACreturndouble (x);
	
	return (true);
	} /*logverb*/
	

static Boolean log10verb (void) {
	
	double x;
	
	if (!IACgetdoubleparam ((OSType) keyDirectObject, &x))
		return (false);
		
	x = log10 (x);

	IACreturndouble (x);
	
	return (true);
	} /*log10verb*/
	

static Boolean modverb (void) {
	
	double x, y;
	
	if (!IACgetdoubleparam ((OSType) keyDirectObject, &x))
		return (false);
		
	if (!IACgetdoubleparam ((OSType) 'dvsr', &y))
		return (false);
		
	x = fmod (x, y);

	IACreturndouble (x);
	
	return (true);
	} /*modverb*/
	

static Boolean floorverb (void) {
	
	double x;
	
	if (!IACgetdoubleparam ((OSType) keyDirectObject, &x))
		return (false);
		
	x = floor (x);

	IACreturndouble (x);
	
	return (true);
	} /*floorverb*/
	

static Boolean ceilverb (void) {
	
	double x;
	
	if (!IACgetdoubleparam ((OSType) keyDirectObject, &x))
		return (false);
		
	x = ceil (x);

	IACreturndouble (x);
	
	return (true);
	} /*ceilverb*/
	

static Boolean sqrtverb (void) {
	
	double x;
	
	if (!IACgetdoubleparam ((OSType) keyDirectObject, &x))
		return (false);
		
	x = sqrt (x);

	IACreturndouble (x);
	
	return (true);
	} /*sqrtverb*/
	

static Boolean absverb (void) {
	
	double x;
	
	if (!IACgetdoubleparam ((OSType) keyDirectObject, &x))
		return (false);
		
	x = fabs (x);

	IACreturndouble (x);
	
	return (true);
	} /*absverb*/
	

static Boolean expverb (void) {
	
	double x;
	
	if (!IACgetdoubleparam ((OSType) keyDirectObject, &x))
		return (false);
		
	x = exp (x);

	IACreturndouble (x);
	
	return (true);
	} /*expverb*/
	

static pascal OSErr handlefastverb (AppleEvent *event, AppleEvent *reply, long refcon) {
	
	if (SharedScriptCancelled (event, reply)) 
		return (noErr);
		
	IACglobals.event = event; 
	
	IACglobals.reply = reply;
	
	IACglobals.refcon = refcon;
		
	switch (IACgetverbtoken ()) {
	
		case sintoken:
			sinverb (); break;
			
		case costoken:
			cosverb (); break;
			
		case tantoken:
			tanverb (); break;
		
		case sinhtoken:
			sinhverb (); break;
			
		case coshtoken:
			coshverb (); break;
			
		case tanhtoken:
			tanhverb (); break;
		
		case asintoken:
			asinverb (); break;
			
		case acostoken:
			acosverb (); break;
			
		case atantoken:
			atanverb (); break;
		
		case logtoken:
			logverb (); break;
			
		case log10token:
			log10verb (); break;
		
		case exptoken:
			expverb (); break;
			
		case abstoken:
			absverb (); break;
			
		case sqrttoken:
			sqrtverb (); break;
			
		case ceiltoken:
			ceilverb (); break;
			
		case floortoken:
			floorverb (); break;
			
		case modtoken:
			modverb (); break;
			
		default:
			IACnothandlederror (); break;
			
		} /*switch*/
		
	return (noErr);
	} /*handlefastverb*/
	

static pascal OSErr handleslowverb (AppleEvent *event, AppleEvent *reply, long refcon) {
	
	if (SharedScriptCancelled (event, reply)) 
		return (noErr);
		
	IACglobals.event = event; 
	
	IACglobals.reply = reply;
	
	IACglobals.refcon = refcon;
		
	switch (IACgetverbtoken ()) {
	
		case setmessagetoken:
			setmessageverb ();
			
			return (noErr);
		
		default:
			return (handlefastverb (event, reply, refcon));
			
		} /*switch*/
	} /*handleslowverb*/
	

static pascal OSErr handlequit (AppleEvent *event, AppleEvent *reply, long refcon) {
	
	flexitmainloop = true;
	
	return (noErr);
	} /*handlequit*/
	
	
static pascal OSErr handleopenapp (AppleEvent *event, AppleEvent *reply, long refcon) {
	
	return (noErr);
	} /*handleopenapp*/


static pascal Boolean openfilespec (FSSpec *fs) {

	return (true);
	} /*openfilespec*/
	

static pascal Boolean printfilespec (FSSpec *fs) {

	return (true);
	} /*printfilespec*/
	

static pascal OSErr handleopen (AppleEvent *event, AppleEvent *reply, long refcon) {
	
	IACglobals.event = event;
	
	IACglobals.reply = reply;
	
	IACglobals.refcon = refcon;
	
	return (IACdrivefilelist (&openfilespec));
	} /*handleopen*/
	
		
static pascal OSErr handleprint (AppleEvent *event, AppleEvent *reply, long refcon) {
	
	IACglobals.event = event;
	
	IACglobals.reply = reply;
	
	IACglobals.refcon = refcon;
	
	return (IACdrivefilelist (&printfilespec));
	} /*handleprint*/
	

static void magicIncantation (void) {
	
	/*
	6/27/92 DW:
	
	by calling one of the trig functions we force something to load. if we
	call one of the trig functions for the first time in a system event 
	handler it's crash time! current theory: something must happen with
	resources, and accessing resources is a no-no at system handler time.
	
	if you have to access resources, do it with a slow event.
	*/
	
	sqrt (1.0);
	} /*magicIncantation*/
	
		
static pascal void errordialog (Str255 s) {
	
	ParamText (s, "\p", "\p", "\p"); 
	
	Alert (263, nil);
	} /*errordialog*/
	
	
static pascal void eventfilter (EventRecord *ev) {	

	handleevent (ev);
	} /*eventfilter*/
	

void main (void) {
	
	initmacintosh ();
	
	magicIncantation ();
	
	if (!InitSharedMenus (&errordialog, &eventfilter)) 
		goto error;
	
	if (!IACinstallsystemhandler ('trig', typeWildCard, (ProcPtr) &handlefastverb))
		goto error;

	if (!IACinstallhandler ('TRIG', typeWildCard, (ProcPtr) &handleslowverb))
		goto error;

	if (!IACinstallhandler (kCoreEventClass, kAEOpenApplication, (ProcPtr) &handleopenapp))
		goto error;
	
	if (!IACinstallhandler (kCoreEventClass, kAEOpenDocuments, (ProcPtr) &handleopen))
		goto error;
	
	if (!IACinstallhandler (kCoreEventClass, kAEPrintDocuments, (ProcPtr) &handleprint))
		goto error;
	
	if (!IACinstallhandler (kCoreEventClass, kAEQuitApplication, (ProcPtr) &handlequit))
		goto error;

	initmenus ();
	
	initmainwindow ();
	
	maineventloop ();
	
	IACremovesystemhandlers ();
		
	ExitToShell ();
	
	error:
	
	Alert (261, nil); /*this application requires system 7.0 or higher*/
		
	ExitToShell ();
	} /*main*/


