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


/*
Drives Frontier's component "do-script" handler. You can send any valid Frontier 
script across this channel. 

This program starts by sending a script with a syntax error, to test the error
reporting mechanism.

This is a very bare bones program with one window and no menu bar. To quit
just close the window.

It's much faster than the Apple Event-based version!
*/


#include <AppleEvents.h>
#include <Components.h>
#include <GestaltEqu.h>
#include <OSA.h>

#define stringlength(s) (s [0])

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

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


static void initmacintosh (void) {

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

	register short i;
		
	MaxApplZone ();
	
	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*/
		
	DrawMenuBar ();
	} /*initmacintosh*/


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 Boolean pushstring (Str255 bssource, Str255 bsdest) {

	register short lensource = bssource [0];
	register short lendest = bsdest [0];
	register char *psource, *pdest;
	
	if ((lensource + lendest) > 255)
		return (false);
		
	pdest = (char *) bsdest + (char) lendest + 1;
	
	psource = (char *) bssource + 1;
	
	bsdest [0] += (char) lensource;
	
	while (lensource--) *pdest++ = *psource++;
	
	return (true);
	} /*pushstring*/
	
	
static Boolean pushlong (long num, Str255 bsdest) {

	Str255 bsint;
	
	NumToString (num, bsint);
	
	return (pushstring (bsint, bsdest));
	} /*pushlong*/
	

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 (helvetica, 12, 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 void setwindowmessage (Str255 script, Str255 result) {
	
	copystring (script, windowmessage);
			
	pushstring ("\p returns ", windowmessage);
			
	pushstring (result, windowmessage);
	
	pushstring ("\p", windowmessage);
			
	SetPort (mainwindow);
	
	updatemainwindow ();
	} /*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 Boolean stringtotext (Str255 s, Handle *htext) {
	
	register long len;
	register Handle h;
	
	len = s [0];
	
	h = NewHandle (len);
	
	if (h == nil)
		return (false);
		
	BlockMove (&s [1], *h, len);
	
	*htext = h;
	
	return (true);
	} /*stringtotext*/
	
	
static Boolean texttostring (Handle htext, Str255 s) {
	
	long len;
	
	len = GetHandleSize (htext);
	
	if (len > 255)
		len = 255;
		
	BlockMove (*htext, &s [1], len);
	
	s [0] = (char) len;
	
	return (true);
	} /*texttostring*/
	

static Boolean FrontierDoScript (Str255 script, Str255 returns) {
	
	/*
	Executes a Do Script message, using Frontier's UserTalk component 
	interface. The first parameter contains a short script to be run. 
	The second parameter is the string that Frontier returned as the value 
	generated by running the script. 
	
	Returns true if we were able to send the message to Frontier, and Frontier
	was able to compile and run the script and return a value. FrontierDoScript 
	returns false if Frontier isn't running, or if the script didn't compile or 
	if there was a communications error.
	
	Frontier is not limited to running 255-character scripts or returning 
	255-character returned values. This routine can easily be enhanced to handle 
	larger scripts and returned values.
	
	Note that this sample code tests for the Component Manager and connects to 
	the script server each time a script is executed. In production code, better 
	performance can be obtained by keeping the component instance open until 
	you're done with it.
	*/
	
	register OSErr ec;
	ComponentInstance scriptcomp;
	AEDesc scriptdesc;
	AEDesc resultdesc = {typeNull, nil};
	long result;
	
	ec = Gestalt (gestaltComponentMgr, &result);
	
	if ((ec != noErr) || (result == 0)) {
		
		copystring ("\pThe Component Manager isnŐt present. This application requires System 7.1 or QuickTime.", returns);
		
		return (false);
		}
	
	ec = AECreateDesc (typeChar, (Ptr) &script [1], stringlength (script), &scriptdesc);
	
	if (ec != noErr)
		return (false);
	
	scriptcomp = OpenDefaultComponent (kOSAComponentType, 'LAND');
	
	if (scriptcomp == nil)  { /*couldn't connect to component*/
		
		AEDisposeDesc (&scriptdesc);
		
		copystring ("\pCouldnŐt open the UserTalk scripting component.", returns);
		
		return (false);
		}
	
	ec = OSADoScript (scriptcomp, &scriptdesc, kOSANullScript, typeChar, kOSANullMode, &resultdesc);
	
	AEDisposeDesc (&scriptdesc);
	
	switch (ec) {
		
		case noErr: /*script ran without error*/
			
			texttostring (resultdesc.dataHandle, returns);
			
			break;
		
		case errOSAScriptError: /*error during script execution*/ 
			
			if (OSAScriptError (scriptcomp, kOSAErrorMessage, typeChar, &resultdesc) == noErr)
				texttostring (resultdesc.dataHandle, returns);
			else
				copystring ("\pThe script generated an error, but no message was provided.", returns);
			
			break;
		
		default: /*error trying to run the script*/
		
			copystring ("\pComponent Manager error. Its code number is ", returns);
			
			pushlong (ec, returns);
			
			pushstring ("\p.", returns);
			
			break;
		}
	
	AEDisposeDesc (&resultdesc);
	
	CloseComponent (scriptcomp);
	
	return (ec == noErr);
	} /*FrontierDoScript*/
	
	
static void handledrag (EventRecord ev, WindowPtr w) {
	
	Rect r;

	r = qd.screenBits.bounds; 
   
	r.top = r.top + GetMBarHeight (); 
               
	r.left = r.left + 4;  
               
	r.right = r.right - 4;
               
	r.bottom = r.bottom - 4;
             
	DragWindow (w, ev.where, &r);   
	} /*handledrag*/
	
	
static Boolean exitprogram (void) {
	
	/*
	returns true if the user clicks in the go-away box.
	*/
	
	EventRecord ev;
	WindowPtr w;
	
	if (!WaitNextEvent (everyEvent, &ev, 1, nil))
		return (false);
	
	if (ev.what != mouseDown)
		return (false);
		
	switch (FindWindow (ev.where, &w)) {
	
		case inGoAway: /*click in go-away box to exit program*/
			return (TrackGoAway (w, ev.where));
		
		case inSysWindow:
			SystemClick (&ev, w); 
			
			return (false);
		
		case inDrag:
			handledrag (ev, w);
			
			return (false);
			
		} /*switch*/
		
	return (false); /*don't exit the program*/
	} /*exitprogram*/


static void waitseconds (long n) {
	
	register long endticks;
	
	endticks = TickCount () + (n * 60);
	
	while (TickCount () < endticks)
		SystemTask ();
	} /*waitseconds*/


void main (void) {
	
	Str255 script, result;
	Boolean error;
	register short i;
	
	initmacintosh ();
	
	initmainwindow (); 
	
	copystring ("\p94 + ", script);
	
	if (FrontierDoScript (script, result))
		copystring ("\pFrontierDoScript should have returned false.", result);
	
	setwindowmessage (script, result); /*should say "Can't compile this script..."*/
	
	waitseconds (1); /*give user a chance to see error message*/
	
	for (i = 1; i <= 50; ++i) {
		
		if (exitprogram ()) /*returns true if user clicks in go away box*/
			return;
		
		copystring ("\pstates.nthState (", script);
		
		pushlong (i, script);
		
		pushstring ("\p)", script);
		
		error = !FrontierDoScript (script, result);
		
		setwindowmessage (script, result);
		
		if (error)
			break;
		} /*while*/
	
	waitseconds (2); /*give user a chance to see the final result*/
	} /*main*/


