
/* These are the common routines for MT1 and MT2&3 on the Macintosh*/

#define TTHREE
#ifdef TONE
#include "macteach1.h"
#else
#include "macteach23.h"
#endif
#include <Sound.h>
//#include <SegLoad.h>
#define SDInumber		0xAA68
#define WNEnumber		0xA860
#define Unimplemented		0xA89F
#define ENTER 3

Boolean AppleEventsInstalled ()
{
	OSErr err;
	long  result;

	// THINK C's MacTraps library provides glue for Gestalt, so
	// it can be called safely under System 6. If an error is
	// returned, then Gestalt for the AppleEvents Selector is
	// not available (this also means that Apple Events are
	// not available)
	
	err = Gestalt (gestaltAppleEventsAttr, &result);
	return (!err && ((result >> gestaltAppleEventsPresent) & 0x0001));
	// return TRUE if there is no
	// error and the proper bit of
	// result is set
}

void	InitToolbox(void)

{
	InitGraf(&qd.thePort);		// Standard initialization calls
	InitFonts();
	FlushEvents(everyEvent, 0);
	InitWindows();
	InitMenus();
	TEInit();
//	InitDialogs();
	InitCursor();
}

void initMac(void)

{	Boolean aEvents;
	Boolean		gDone = FALSE;
//	short 		doWhat,itemHit;
//	short 		fileCnt;
//	short		i;
//	EventRecord	*theEvent;
	OSErr		err;
	
	InitToolbox();
	aEvents = AppleEventsInstalled();
	gSDI = TrapAvailable (SDInumber);
	if (aEvents) {
			err = AEInstallEventHandler (kCoreEventClass, kAEOpenDocuments,
					NewAEEventHandlerProc(MyHandleODoc),0, FALSE);
			err = AEInstallEventHandler (kCoreEventClass, kAEOpenApplication,
					NewAEEventHandlerProc(MyHandleOApp),0, FALSE);
			err = AEInstallEventHandler (kCoreEventClass, kAEPrintDocuments,
					NewAEEventHandlerProc(MyHandlePDoc),0, FALSE);
			err = AEInstallEventHandler (kCoreEventClass,kAEQuitApplication,
					NewAEEventHandlerProc(MyHandleQUIT),0,FALSE);
						}
/*	else {
		//Get number of files double-clicked on by the user
		CountAppFiles ( &doWhat, &fileCnt );
		if (fileCnt > 0) {	// if the user selected one or more files
			if (doWhat == appOpen)	{
				for (i = 1; i <= fileCnt; i++) {
					GetAppFiles ( i, &fileStuff );
					PStrCpy( filename, fileStuff.fName);	// JPJ: Try to keep fName a valid pascal string 
					PtoCstr((StringPtr)filename);		// JPJ 
					PStrCpy(gInReply.fName,fileStuff.fName);
					doopen();
					}
				}
			else if (doWhat == appPrint) {
				printf ("Files to print:\n");
				for (i = 1; i <= fileCnt; i++) {
					GetAppFiles ( i, &fileStuff );
					printf ("%s\n", PtoCstr (fileStuff.fName));
					}
				}
			for (i = 0; i < fileCnt; i++) ClrAppFiles(i);
			}
		else {
			if (!gInReply.fName[0]) {
	#ifdef TONE
				i = 999;
	#else
				i = 997;
	#endif
				gAbtDialog = GetNewDialog(i, &gAbtRecord, (DialogPtr)-1L);
				outlin(1,gAbtDialog);
				if (gSDI) SetDialogCancelItem (gAbtDialog,2) ;
				ModalDialog(0L,&itemHit);
				CloseDialog(gAbtDialog);
				if (itemHit==2) dohelp();
				else doopen();
				}
			}

		}*/
	gDragRect = (*GetGrayRgn())->rgnBBox;
	InsetRect(&gDragRect, 4, 4);
	}

void alert(short n, short m)

/* Display an alert dialog when an abnormal condition arises. On entry 
n is the alert type (note, warning, fatal), and m the message number */

{	const short alertID = 998;	//Resource number for Alert dialog
	message(n,m,0L,alertID);
	}

void checkCursor(CursHandle iBeam)

/* This routine examines to see if the cursor is present in an data entry 
type dialog box, and if so changes it to an I-bar. Otherwise it is the 
standard arrow cursor*/

{	Point temp;
	static short i;
	if (gHelp) return;
	if (FrontWindow() == gMainDialog) {
		GetMouse(&temp);
		if (PtInRect(temp,&gReptBox) || PtInRect(temp,&gTimeBox)){
			SetCursor(*iBeam);
			return;
			}
		else SetCursor(&qd.arrow);
		}
	}
	
void demote(short n)

/* After an error response, show card n, sound an alarm, and place
 the card in the error box */

{	if (!reviewflag) {
		if (strcmp(instring,"TIME'S UP!!!!")) {
			SetDialogItemText(gItemHdl[fAns],(unsigned char *)"\pIncorrect!");
#ifdef TONE
			HiliteControl ((ControlHandle) gItemHdl[CANERR],0);
#endif
			}
#ifdef TONE
		else HiliteControl ((ControlHandle) gItemHdl[CANERR],255);
#endif
		SetDialogItemText(gItemHdl[fAns],(unsigned char *)"\pIncorrect!");
		SysBeep(6);
		flasharray[n].flag[curflag] = '5';
#ifdef TONE
// Do not do this reset for MT 4
		if (prog != '5') flasharray[n].flag[rrflag -'1'+2] = '0';
#endif	
		}
	}

void display(void)

/* Display the current file, configuration, distribution of cards,and statistics 
of the latest test, if any */

{	short i,j;
	char mystr[40];
	SetPort(gMainDialog);
#ifdef TTHREE
	curflag = rrflag + 3 - 2 * affixflag + '0';/*Determine configuration*/
#else
	curflag = rrflag - '1';
#endif
	countflag(); /* Obtain the card distribution */
	/* JPJ: changed \p strings to c-strings in sprintf: no null byte
	 corrupts heap */
	sprintf(mystr,"%03d In deck",r[0]);
	SetDialogItemText(gMainHdl[mInDeck],CtoPstr(mystr));
	sprintf(mystr,(char*)"%03d in Rung 1",r[1]);
	SetControlTitle((ControlHandle)gMainHdl[mRung1],CtoPstr(mystr));
	sprintf(mystr,"%03d in Rung 2",r[2]);
	SetControlTitle((ControlHandle)gMainHdl[mRung2],CtoPstr(mystr));
	sprintf(mystr,"%03d in Rung 3",r[3]);
	SetControlTitle((ControlHandle)gMainHdl[mRung3],CtoPstr(mystr));
	sprintf(mystr,"%03d on Top",r[4]);
	SetControlTitle((ControlHandle)gMainHdl[mTop],CtoPstr(mystr));
	sprintf(mystr,"%03d in Error Box",r[5]);
	SetControlTitle((ControlHandle)gMainHdl[mErrorBox],CtoPstr(mystr));
	if (tested) {
		sprintf(mystr,"Tested: %d   Correct: %d, %d%%", tested, right, (100*right)/tested);
		SetDialogItemText(gMainHdl[mPcent],CtoPstr(mystr));
		showstat(); /*Max and min times, standard deviation and timeouts*/
		}
	for (i=1; i<6; ++i) {
		j = r[i] ? 0 : 255;
		HiliteControl((ControlHandle)gMainHdl[i+2],j); //Rungx buttons
		}
	if (!r[0]) HiliteControl((ControlHandle)gMainHdl[mSelect],255);
	else HiliteControl((ControlHandle)gMainHdl[mSelect],0);
	while((!r[rungflag-'0'] || rungflag=='4') && rungflag !='1')
		setrung(rungflag-1);
	if (rungflag==1 && !r[rungflag]) rungflag = '5';
	while((!r[rungflag-'0'] || rungflag=='4') && rungflag !='1')
		setrung(rungflag-1);
	ShowWindow(gMainDialog);
	SelectWindow(gMainDialog);
	}

void doabout(void)

 /* Display the about dialog from the apple menu. If the gHelp button is pushed, 
 show the gHelp window*/

 {	short rsrc,itemHit;
 	const short aboutID = 999;
 	const short aboutID2 = 998;
	rsrc = (prog == '2' || prog == '4') ? aboutID2 : aboutID;
	gAbtDialog = GetNewDialog(rsrc,&gAbtRecord,(DialogPtr) -1L);
	outlin(1,gAbtDialog);
	if (gSDI) SetDialogCancelItem (gAbtDialog,2) ;
	SetPort(gAbtDialog);
	ShowWindow(gAbtDialog);
	ModalDialog(0L, &itemHit);
	CloseDialog(gAbtDialog);
	if(itemHit==2) dohelp();
	}

short doClose(void) 

/* If the file has been changed, ask if it should be saved. If yes, save the file
 and statistics, close the files and fix the menu highlighting.*/

{	short theType;
	const short dType = 4;	// A dialog window instead of alert
	const short dMess = 2;	// Save ladder and statistics before closing?
	if (inpfile) {		
		if (dirty) {
			theType = message(dType,dMess,NULL,402);
			switch(theType) {
			case 1: 
				SetCursor(*gWatch);
				savedo();
				SetCursor(&qd.arrow);
				if (testtotl) savestat();
			case 2: dirty = FALSE; break;
			default: return theType;
				}					
			}
		fclose(inp);
#ifdef TONE
		fclose(cmt);
#endif
		inpfile = FALSE;
		gInReply.fName[0] = NUL;
		EnableItem(gMyMenus[1],flOpen);
		DisableItem(gMyMenus[1],flClose);
		DisableItem(gMyMenus[1],flSave);
		DisableItem(gMyMenus[1],flSaveAs);
		}
	return theType;
	}

void docommand(unsigned long mResult)

/*This routine handles mouse down in the menu. mResult is the menu and item hit */

{	Str255 name;
	short theMenu, theItem,theType;
	short resval;
//	long ticks;

/*There are a individual gHelp items for each of the menu items.
  As the menus are not identical for the two programs, the value
  of the gHelp item is adjusted according to the program. A  group
  of the higher number items have the same gHelp item.  If the
  gHelp flag is on, show the gHelp instead of doing the menu 
  action */

	theMenu = mResult >> 16;
	theItem = mResult;
	resval = (theMenu-256)*16 + theItem;
	if (resval>50) resval = 52;
#ifdef TONE
	if (resval>37 && resval<48) ++resval;
#endif
	if (gHelp) {
		if(resval== 43) dohelp();
		showhelp(resval);
		return;
		}
	switch(theMenu) {
	case appleMenu:
		GetMenuItemText(gMyMenus[0], theItem, name);
		if(theItem==1) doabout(); /*apple menu About... item*/
		else {
			EnableItem(gMyMenus[2],0);
			DrawMenuBar();
			OpenDeskAcc( name);
			}
		break;

	case fileMenu:
		switch(theItem) {
		case flOpen:
			doopen();
			break;
		case flQuit:
			theType = doClose();			/* JPJ: did not match prototype */
#ifdef IBM
			free (gMyPtr);
#else
			DisposePtr(gMyPtr);
#endif
			if (theType != 3) exit(0);
			break;
		case flClose:
			doClose();			/* JPJ: did not match prototype */
			break;
		case flSave:
			SetCursor(*gWatch); /*Activate the delay cursor*/
			savedo();
			SetCursor(&qd.arrow); /*Restore regular cursor*/
			break;
		case flSaveAs:
			SetCursor(*gWatch);
			saveasdo();
			SetCursor(&qd.arrow);
			break;
		default: break;
			}
		break;
	case editMenu:
		break;
	case commandMenu:
		switch(theItem) {
		case cReset:
			restore();
			display();
			break;
		case cReview:
			HiliteControl ((ControlHandle) gMainHdl[mShuffle],0);
			EnableItem(gMyMenus[3],cShuffle);
			reviewflag = 1;
			dotest(cReview);
			reviewflag = 0;
			break;
		case cSelect:
#ifdef TTHREE
			if (prog=='2') {
				HiliteControl ((ControlHandle) gMainHdl[mShuffle],255);
				DisableItem(gMyMenus[4],moRecall);
				DisableItem(gMyMenus[3],cShuffle);
				}
			else {
				HiliteControl ((ControlHandle) gMainHdl[mShuffle],0);
				EnableItem(gMyMenus[3],cShuffle);
				}
#endif
			doselect();
			display();
			break;
		case cTest:
			HiliteControl ((ControlHandle) gMainHdl[mShuffle],0);
			EnableItem(gMyMenus[3],cShuffle);
			reviewflag = 0;
			dotest(cTest);
			if (testtotl) 	{
				HiliteControl((ControlHandle) gMainHdl[mStats],0);
				EnableItem (gMyMenus[3],cStats);
				}
			break;
#ifdef TTHREE
		case cLookup:
			doseek();
			break;
#endif
		case cBrush:
			brush();
			setshuf(1);
			break;
		case cHelp:
			dohelp();
			break;
		case cStats:
			sessions();
			break;
		case cShuffle:
			shufflag ^= 1;
			setshuf(shufflag);
			break;
		default:
			break;
			}
		break;
	case modeMenu:
		switch(theItem) {
		case moRecog:
			if(testtotl) {
				savestat();
				}
			setrr('1');
			break;
		case moRecall:
			if(testtotl) savestat();
			setrr('2');
			break;
		case moRung1:
		case moRung2:
		case moRung3:
		case moTop:
		case moErrBox:
			setrung(theItem-moRung1+'1');
			break;
		default: break;
			}
		break;
	default:
		break;
		}
	HiliteMenu(0);
	}
	
void dodrill(void)

{	char cnt[4];
	GetDialogItemText(gMainHdl[mDrill],(unsigned char *)cnt);
	drillflg = cnt[1]-'0';
	sprintf(instring,"%d",drillflg);
	SetDialogItemText(gMainHdl[mDrill],CtoPstr(instring));
	}

void dohelp(void)

/* Provide gHelp to the user. Toggle the gHelp flag. If it is now on set the cursor 
to a ?, toggle the menu item, get the main gHelp description, adjust the scroll 
bars, and enable all menu items. If gHelp now off and no input file yet, 
request one, then reverse the above changes. */

{	CursHandle	Query;		/*Question mark cursor for help*/
	gHelp ^= 1;
	if (gHelp) {
		Query = GetCursor(128);
		if (FrontWindow() != gMainDialog) ShowWindow(gMainDialog);
		ShowWindow(gHelpWindow);
		SetCursor(*Query);
		SelectWindow(gHelpWindow);
		SetControlTitle((ControlHandle)gMainHdl[mHelp],(unsigned char *)"\pHelp Off");
		SetMenuItemText(gMyMenus[3],cHelp,(unsigned char *)"\pHelp Off");
		SelectWindow(gHelpWindow);
		(*ghTE)->hText = GetResource('TEXT',(textID));
		(*ghTE)->teLength = GetResourceSizeOnDisk((*ghTE)->hText);
		TECalText(ghTE);
		setscroll();
		SetControlValue(gvScroll,0);
		EnableItem (gMyMenus[3],0);
		EnableItem (gMyMenus[3],cStats);
		EnableItem (gMyMenus[3],cShuffle);
		EnableItem (gMyMenus[4],0);
		EnableItem (gMyMenus[1],flOpen);
		EnableItem (gMyMenus[1],flClose);
		EnableItem (gMyMenus[1],flSave);
		EnableItem (gMyMenus[1],flSaveAs);
		EnableItem (gMyMenus[1],flQuit);
		DrawMenuBar();
		}
	else {
		if (!inpfile) {
			DisableItem(gMyMenus[3],0);
			DisableItem(gMyMenus[4],0);
			doopen();
			}
		if (!testtotl) DisableItem(gMyMenus[3],cStats);
		SetControlTitle((ControlHandle)gMainHdl[mHelp],(unsigned char *)"\pHelp?");
		SetMenuItemText(gMyMenus[3],cHelp,(unsigned char *)"\pHelp");
		HideWindow(gHelpWindow);
		SelectWindow(gMainDialog);
		SetPort(gMainDialog);
		SetCursor(&qd.arrow);
		}
	}

void domouse(WindowPtr whichWindow)

/*This is the mouse handling routine. It is fairly standard*/

{	short code;
	short myControl;	/*current control handle*/

	code = FindWindow(gMyEvent.where, &whichWindow);
	if (code != inSysWindow) { /*Not a desk accessory operation*/
		DisableItem (gMyMenus[2],0);
		DrawMenuBar();
		}
	switch (code) {
	case inMenuBar: /*Process a menu event*/
		docommand(MenuSelect(gMyEvent.where));
		break;
	case inSysWindow: /*In a desk accessory. Enable the edit menu*/
		SystemClick(&gMyEvent, whichWindow);
		EnableItem (gMyMenus[2],0);
		DrawMenuBar();
		break;
	case inDrag: /*In the title bar. Move the window*/
		DragWindow(whichWindow,gMyEvent.where, &gDragRect);
		break;
	case inGoAway: /*Close the window.  Mouse clicked in close box*/
		if (TrackGoAway(whichWindow, gMyEvent.where)) {
			HideWindow(whichWindow);
			if (inpfile) { /*If an input file active, show the main dialog*/
				display();
				DisableItem(gMyMenus[1],flOpen);
				}
			else { /*Open an input if not one open*/
				SetPort(gMainDialog);
				doopen();
				DisableItem(gMyMenus[1],flClose);
				DisableItem(gMyMenus[1],flSave);
				DisableItem(gMyMenus[1],flSaveAs);
				DisableItem(gMyMenus[3],0);
				DisableItem(gMyMenus[4],0);
				DrawMenuBar();
				}
			}
		break;
	case inGrow: /*In Size Box.  Change the window size if active, else select it.*/
		if (whichWindow == FrontWindow() && whichWindow==gHelpWindow)
			GrowWnd(whichWindow);
		else
			SelectWindow(whichWindow);
		break;
	case inContent:/*Select if not active, else handle scroll or content*/
		if (whichWindow != FrontWindow()) {
			SelectWindow(whichWindow);
			SetPort(whichWindow);
			}
		else if (whichWindow == gHelpWindow) {
			GlobalToLocal(&gMyEvent.where);
			ResizePRect(whichWindow);
			if (!PtInRect(gMyEvent.where,&gpRect)) {
				myControl = FindControl(gMyEvent.where,
					whichWindow, &gWhichControl);
				switch(myControl) { /*Handle scrolling*/
				case kControlUpButtonPart:
//					TrackControl(gWhichControl,gMyEvent.where,
//						(ProcPtr)ScrollUp);
				case kControlDownButtonPart:
//					TrackControl(gWhichControl,gMyEvent.where,
//						(ProcPtr)ScrollDown);
					break;
				case kControlPageUpPart:
					PageScroll(myControl, 1-winht);
					break;
				case kControlPageDownPart:
					PageScroll(myControl, winht-1);
					break;
				case kControlIndicatorPart:
//					if (TrackControl(gWhichControl,gMyEvent.where,
//						(ProcPtr)-1L)) ScrollBits();
					break;
					}
				}
			}
		break;
	default: /*No other action valid*/
		break;
		}
	}

void domulti(void)

// These are standard borrowed routines for ensuring multifinder compatibility

{	DialogPtr ResumeWindow,SuspendWindow;
//	short sysresult;
	if (gMyEvent.message & 1) { /*Resume*/
		ResumeWindow = FrontWindow();
		}
	else { /*Suspend*/
		SuspendWindow = FrontWindow();
		}
	}

void doopen(void)

/*Get a file name, open it and read it. Fix the shuffle menu and dialog item.  
Initialize the cursor, set the appropriate rung and mode flags, threshold time 
and error box settings and dialog title*/

{	
	opentext();
	readfile();
#ifdef TTHREE
	if (prog=='2') {
		HiliteControl ((ControlHandle) gMainHdl[mShuffle],255);
		DisableItem(gMyMenus[3],cShuffle);
		}
	else {
		HiliteControl ((ControlHandle) gMainHdl[mShuffle],0);
		EnableItem(gMyMenus[3],cShuffle);
		}
#endif
	SetCursor(&qd.arrow);
	setrr(rrflag);
#ifdef TTHREE
	setaffix(affixflag);
#endif
	setrung(rungflag);
	sprintf(instring,"%d",drillflg);
	SetDialogItemText(gMainHdl[mDrill],CtoPstr(instring));
	sprintf(instring,"%1d.%1d",threshld/factor,
		((threshld%factor)*10)/factor);
	SetDialogItemText(gMainHdl[mTime],CtoPstr(instring));
	setshuf(shufflag);
	EnableItem (gMyMenus[3],0);
	EnableItem (gMyMenus[4],0);
	HiliteControl((ControlHandle) gMainHdl[mStats],255);
	switch(prog) {
	case '2':
	case '4':
		SetWTitle(gMainDialog,(unsigned char *)"\pMacTeach 3: Learning Loglan Affixes");
		break;
	default: 
		break;
		}	
	DrawMenuBar();
	}

pascal Boolean Timeget(DialogPtr theDialog, EventRecord *theEvent, short *item)

/*This routine is necessary as a user hook to enable getting a user time to the 
first keystroke, and notifying if the time exceeds the threshold alloted. 
It has to handle the CR and Enter buttons since this part of the normal routine 
is replaced by the user routine*/

{	short k;
	if (theEvent->what != keyDown && !ind1) {
		if ((TickCount()-macticks) >threshld) {
			ticks = threshld;
			SetDialogItemText(gItemHdl[3],(unsigned char *)"\pTIME'S UP!!!!");
			++tmouts;
			*item = 1;
			return TRUE;
			}
		else ticks = TickCount()-macticks;
		return FALSE;
		}
	else {
		ind1 = 1;
		k = theEvent->message&0xFF;
		if(k==CR || k==ENTER) {
			*item = 1;
			return TRUE;
			}
		}
	return FALSE;
	}


void dotimeout(void)

/* This is Macintosh specific for setting the maximum time permitted for response. 
The response in seconds and fractions is converted to sixtieths of a second. It 
is reconverted, clipped and reentered in the dialog box. */

{	char thno[256];	/* JPJ: increased size: better safe than sorry */
	GetDialogItemText(gMainHdl[mTime],(unsigned char *)thno);
	PtoCstr((unsigned char *)thno);
	if (isdigit(thno[0])) {
		switch(strlen(thno)) {
		case 3:
			if (thno[1] != '.' || !isdigit(thno[2])) break;
			thno[1] = thno[2];
			thno[2] = '\0';
			ind1 = atoi(thno) * (factor/10);
			break;
		case 1:
			ind1 = atoi(thno) * factor;
			break;
		default:
			ind1 = threshld;
			break;
			}
		}
	if (ind1>1 && ind1<(93 * factor/10)) threshld = ind1;
	sprintf(instring,"%1d.%1d",threshld/factor,
		((threshld%factor)*10)/factor);
	SetDialogItemText(gMainHdl[mTime],CtoPstr(instring));
	}

void DrawWindow(WindowPtr theWindow)

/*On entry theWindow is the window pointer of the window to be drawn. This 
routine handles drawing of the window.  If it is gHelp, the scroll bars 
must be drawn and the gHelp text clipped and updated, otherwise the main 
dialog window is drawn*/

{
	//short i,vpos;
	if (theWindow==gHelpWindow) {
		ClipRect(&theWindow->portRect);
		EraseRect(&theWindow->portRect);
		ResizePRect(theWindow);
		DrawGrowIcon(theWindow);
		TEUpdate(&theWindow->portRect, ghTE);
		SetControlValue(gvScroll,0);
		DrawControls(theWindow);
		}
	else {
		if (inpfile) display();
		}
	}

void eventloop(void)

/* This is a standard event-handling loop for the teach programs. It is 
multifinder compatible */

{	long uptime,sleep;
	WindowPtr		whichWindow;	/*current active window*/
	CursHandle		iBeam;	
	GrafPtr	savePort;	/*Temporary saving of grafport*/
	Boolean WNE;
	short itemHit;
	WNE = TrapAvailable(WNEnumber);
	sleep = 10;
	iBeam = GetCursor(iBeamCursor);
	for (;;) { /*For Multifinder*/
		checkCursor(iBeam);
		if (WNE) WaitNextEvent(everyEvent,&gMyEvent,sleep,0L);
		else {
			SystemTask();
			GetNextEvent(everyEvent, &gMyEvent);
			}
		itemHit = 0;
		if (IsDialogEvent(&gMyEvent)) { /*Check dialog first*/
			if (gMyEvent.what == keyDown) {
				itemHit = keyequiv(1); /*For CR and Enter keys*/
				if (gMyEvent.modifiers & cmdKey) continue;
				}
			if(!itemHit) DialogSelect(&gMyEvent,&gMainDialog,&itemHit);
			switch(itemHit) {
			case 1: 
			/* This is an OK button. Set the error-box repeat count and 
			threshold time*/
				dodrill();
				dotimeout();
				break;
			case 2:/*Toggle the shuffle item. This is the select button*/
				if (gHelp) {
					showhelp(33);
					break;
					}
#ifdef TTHREE
				if (curflag==2) {
					HiliteControl ((ControlHandle) gMainHdl[mShuffle],255);
					DisableItem(gMyMenus[3],cShuffle);
					}
				else {
					HiliteControl ((ControlHandle) gMainHdl[mShuffle],0);
					EnableItem(gMyMenus[3],cShuffle);
					}
#endif
				doselect();
				display();
				break;
			case 3: /*Rung radio buttons*/
			case 4:
			case 5:
			case 6:
			case 7:
				if (gHelp) {
					showhelp(52);
					break;
					}
				setrung(itemHit + '.'); /* itemHit -2 + '0' */
				break;
			case 9: /*Test button*/
				if (gHelp) {
					showhelp(35);
					break;
					}
				HiliteControl ((ControlHandle) gMainHdl[mShuffle],0);
				EnableItem(gMyMenus[3],cShuffle);
				reviewflag = 0;
				dodrill();
				dotimeout();
				dotest(cTest);
				if (testtotl) 	{
					HiliteControl((ControlHandle) gMainHdl[mStats],0);
					}
				break;
			case 10: /*Review button*/
				if (gHelp) {
					showhelp(34);
					break;
					}
				HiliteControl ((ControlHandle) gMainHdl[mShuffle],0);
				EnableItem(gMyMenus[3],cShuffle);
				reviewflag = 1;
				dotest(cReview);
				reviewflag = 0;
				break;
			case 11: /*Statistics button*/
				if (gHelp) {
					showhelp(40);
					break;
					}
				sessions();
				break;
			case 12:
				if (gHelp) showhelp(9);
				break;
			case 15:
				if (gHelp) showhelp(8);
				break;
			case 16: /*Shuffle check box*/
				if (gHelp) {
					showhelp(37);
					break;
					}
				shufflag ^= 1;
				setshuf(shufflag);
				break;
			case 18:
				if (gHelp) {
					showhelp(49);
					break;
					}
			case 19:  /*Quit button*/
				if (gHelp) {
					showhelp(50);
					break;
					}
				if(testtotl) {
					savestat();
					}
				setrr(itemHit-17+'0');
				break;
			case 26: /*Help button*/
				dohelp();
				break;
			default: break;
				}			
			}
		else { /*Not a dialog event*/
			switch(gMyEvent.what) {
			case mouseDown:
				domouse(whichWindow);
				break;
			case mouseUp:
				uptime = gMyEvent.when;
				break;
			case keyDown:
			case autoKey:
				if (gMyEvent.modifiers & cmdKey)
					docommand(MenuKey((char)gMyEvent.message));
				break;
			case MFEVENT:
				domulti();
				break;
			case kHighLevelEvent:
				if (AEProcessAppleEvent(&gMyEvent)!=noErr) alert(0,151); 	//Apple Event Error
				break;
			case activateEvt:
				whichWindow = (WindowPtr) gMyEvent.message;
				if (whichWindow == gHelpWindow) {
					SetPort(gHelpWindow);
					DrawGrowIcon(gHelpWindow);
					if (gMyEvent.modifiers&1) {
						TEActivate(ghTE);
						ShowControl(gvScroll);
						}
					else {
						TEDeactivate(ghTE);
						HideControl(gvScroll);
						HideWindow(gHelpWindow);
						}
					}
				break;

			case updateEvt:
				whichWindow = (WindowPtr) gMyEvent.message;
				GetPort(&savePort);
				SetPort(whichWindow);
				InvalRect(&whichWindow->portRect);
				BeginUpdate(whichWindow);
				DrawWindow(whichWindow);
				EndUpdate(whichWindow);
				SetPort(savePort);	
				break;
			
			case diskEvt:
				if (gMyEvent.message)
					DIBadMount(gOrigin,gMyEvent.message);
				break;
			default: break;
				}
			}
		}
	}

void GrowWnd(WindowPtr whichWindow)

/* On entry whichWindow is the Window pointer. The window is changed to the new 
size and the scroll bars adjusted.  This is fairly standard Macintosh stuff, 
largely borrowed.*/

{	long longResult;
	short height, Width;
	Rect growRect;
//	Rect temrect;
	Rect screen;
	screen = qd.screenBits.bounds;
	SetRect(&growRect,screen.left + 30,screen.top+51,
		screen.right - 8,screen.bottom - 8);
	SetPort(whichWindow);
	longResult = GrowWindow(whichWindow, gMyEvent.where, &growRect);
	if (longResult == 0 || longResult == -1)
		return;
	height = longResult >> 16;
	Width = longResult;
	SizeWindow(whichWindow, Width, height, TRUE);
	if (whichWindow==gHelpWindow) {
		MoveScrollBars(gHelpWindow,gvScroll,0L);
		SetControlValue(gvScroll,0);
		ResizePRect(gHelpWindow);
		(*ghTE)->viewRect = gpRect;
		(*ghTE)->destRect.right = gpRect.right;
		TECalText(ghTE);
		setscroll();
		}
	}


void initteach(void)

/* These are the startup items required for the teach programs.  If a 
file has been double clicked it loads it and proceeds immediately, 
otherwise it gives the About Dialog window. The various cursors, 
scroll bars, text sizes and fonts, and text edit (for Help) are initialized. */

{	short i,theType;
	const short pwindID = 128;	// Resource number of main window
	const short hwindID	= 128;	/*Resource number of help window*/
	setupmenu();
	DisableItem (gMyMenus[3],cStats);
	inpfile = gHelp = 0;
	factor = 60; /* for time conversion*/
	gWatch = GetCursor(watchCursor);
	gHelpWindow = GetNewWindow(hwindID,&gHlpRecord,(WindowPtr)-1L);
	SetPort(gHelpWindow);
	TextFont(2);
	TextSize(12);
	gvScroll = GetNewControl(256, gHelpWindow);
	MoveScrollBars(gHelpWindow,gvScroll,0L);
	ResizePRect(gHelpWindow);
	ghTE = TENew(&gpRect, &gpRect);
	gMainDialog = GetNewDialog(pwindID,&gMainRecord,(DialogPtr)-1L);
	HMSetDialogResID(128);
	for (i=1; i<MAXITEMS; ++i) {
		GetDialogItem(gMainDialog,i,&theType,&gMainHdl[i],&gItemBox);
		if (i==mTime) SetRect(&gReptBox,gItemBox.left,gItemBox.top,gItemBox.right,gItemBox.bottom);
		if (i==mDrill)SetRect(&gTimeBox,gItemBox.left,gItemBox.top,gItemBox.right,gItemBox.bottom);
		}
	SetPort(gMainDialog);
	init();
	gOrigin.h = 50;
	gOrigin.v = 50;
#ifdef THINK_C
	/* JPJ: Tell Think C what creator type to use */
	#ifdef TTHREE
		_fcreator = 'LOGN';
	#else
		_fcreator = 'Log3';
	#endif
#endif
	}

void IOCheck(OSErr code)

/* When the system gives an IO error of code, this routine displays the appropriate 
alert message.  All IO operations are checked */

{	if(!code) return;
	if(code<0) code = -code;
	alert(0,(short) code);
	}

short keyequiv(short button)

/* On entry, button is the button number for which it is desired to make 
Return and Enter equivalent. In addition it checks for Menu Key equivalents, 
and tab for which it switches timeout and drill boxes and selects the 
text in the box*/

{	short k,itemHit;
	if (val !=12 && val !=15) val = 12;
	if (gMyEvent.modifiers & cmdKey)
		docommand(MenuKey((char)gMyEvent.message));
	else {
		k = gMyEvent.message&0xFF;
		if(k==CR || k==ENTER) itemHit = button;
		else if (k==(char)27 || k=='`') itemHit = STOPF;
		else if (k==(char)24) itemHit = PASSF;
		else if (k=='\t') {
			val ^= 3;
			SelectDialogItemText(gMainDialog,val,0,1000);
			itemHit = 0;
			}
		else itemHit = 0;
		}
	return itemHit;
	}

short message(short n, short m, unsigned char *txt, short ID)

/* On entry n is the type of alert to be displayed -- note, warning, or fatal --, 
m is the message number, text is any additional text to be displayed with the message
 and ID is the alert resource ID*. There are two resource IDs used, one for the Alert,
  which returns either one for continue, or two for quit, and the Savestats message,
  which returns 1 (yes), 2(no), 3(cancel) */

{	Handle strtext;
	short theType;
	strtext = GetResource('STR ',m);
	ParamText((unsigned char *)*strtext,txt, NULL,NULL);
	switch(n) {
	case 0:
		theType = StopAlert(ID, NULL);
		theType = 2;
		break;
	case 1:
		theType = NoteAlert(ID, NULL);
		break;
	case 2:
		theType = CautionAlert(ID, NULL);
		break;
	default:
		theType = Alert(ID,NULL);
		}
	if (theType==2 && n<4) {
		doClose();
		exit(0);
		}
	ReleaseResource(strtext);
	return theType;
	}

void MoveScrollBars(WindowPtr thiswindow, ControlHandle tgvScroll, ControlHandle thscroll)

/* This is a fairly standard borrowed Macintosh routine for moving the scroll bars 
when the window is resized */

{
	register Rect *rp;

	rp = &thiswindow->portRect;
	if (tgvScroll) {
		HideControl(tgvScroll);
		MoveControl(tgvScroll, rp->right-15, rp->top-1);
		SizeControl(tgvScroll, 16, rp->bottom-rp->top-13);
		ShowControl(tgvScroll);
		}
	if (thscroll) {
		HideControl(thscroll);
		MoveControl(thscroll, rp->left, rp->bottom-15);
		SizeControl(thscroll, rp->right-rp->left-13,16);
		ShowControl(thscroll);
		}
	}


void mycpy(char *stra, char *strb)

/* copy strb to stra, removing control punctuation such as :,&,\.
'/' is inserted but a space added, Sp,=,+ and - become just space,
'|' becomes a double return and indent, otherwise the character is
   copied as is. */

{	char *s1,*s2,ch;
	s1 = stra;
	s2 = strb;
	while (ch = *s2++) {
		switch(ch) {
		case ':':
		case '&': break;
		case '/':
			*s1++ = ch;
			*s1++ = SP;
			break;
		case '\\': 
			*s1++ = *(s2++); 
			break;
		case ' ':
		case '=':
		case '-':
		case '+':
			*s1++ = SP;
			break;
		case '|':
			*s1++ = '\r';
			*s1++ = '\r';
			*s1++ = SP;
			*s1++ = SP;
			break;
		default:
			*s1++ = ch;
			}
		}
	*s1++ = SP;
	*s1 = '\0';
	}

void opentext(void)

/* This routine calls the standard file package to select a file
 name, and then opens the selected file. */

{	FInfo theInfo;
//	OSType stype;
	OSErr resultCode;	/*Result code from I/O operations*/
//	short n;
	SFTypeList myType;
	myType[0] = 'TEXT';
	if (!gInReply.fName[0]) {
		SFGetFile (gOrigin,NULL,NULL,(short)3,myType,NULL,&gInReply);
		if (!gInReply.good) exit(0);
		resultCode = GetFInfo(gInReply.fName,gInReply.vRefNum, &theInfo);
		IOCheck(resultCode);
#ifdef TTHREE
		theInfo.fdCreator = 'LOGN';
				/* JPJ: changed fName[0] to fName[1] (its a p-string) */
#else
		theInfo.fdCreator = 'Log3';
#endif
		theInfo.fdType = 'TEXT';
		resultCode = SetFInfo(gInReply.fName,gInReply.vRefNum, &theInfo);
		IOCheck(resultCode);		
		PStrCpy( filename, gInReply.fName);	/* JPJ */
		PtoCstr((StringPtr)filename);	/* JPJ */
		}
	if (!(inp = fopen(filename,"r+"))) alert(1,3);
	SetDialogItemText(gMainHdl[mFile],gInReply.fName);  
	/* JPJ: used to be CtoPstr(filename) */
	DisableItem(gMyMenus[1],flOpen);
	EnableItem(gMyMenus[1],flClose);
	EnableItem(gMyMenus[1],flSave);
	EnableItem(gMyMenus[1],flSaveAs);
	}

void outlin(short no, DialogPtr dialog)

/* This routine outlines button no in dialog dialog*/

{	short theType;
	SetPort(dialog);
	GetDialogItem(dialog,no,&theType,&gItemHdl[0],&gItemBox);
	PenSize(3,3);
	InsetRect(&gItemBox,-4,-4);
	FrameRoundRect(&gItemBox,16,16);
	}

void PageScroll(short code, short amount)

/*This is one of the standard scrolling routines for the Macintosh, and scrolls 
when the mouse is between the thumb and one of the arrows. Code is the control 
part where the mouse is, and amount is the amount required for a page scroll*/

{	short maxht;
	Point myPt;

	do {
		GetMouse(&myPt);
		if (TestControl(gWhichControl,myPt) == code) {
			maxht = (*ghTE)->nLines;
			SetControlValue(gWhichControl, GetControlValue(gWhichControl)
				+ (amount * maxht)/(maxht-winht));
			ScrollBits();
		}
	} while (StillDown());
}

void ResizePRect(WindowPtr theWindow)

/*This creates the usable window by insetting the portRect by the scroll bar 
widths and allowing a slight margin*/

{
	gpRect = theWindow->portRect;
	gpRect.left += 4;
	gpRect.right -= 20;
	gpRect.bottom -= 20;
}

void saveasdo(void) 

/* Allow a file to be saved under a different name.  Uses the
 Standard File Package to get the name. Sets the file and user 
 type appropriately for each programme */

{	//short i;
	FInfo theInfo;
	OSErr resultCode;	/*Result code from I/O operations*/
//	OSType stype;
	SFReply	outReply;	/*standard reply for output files*/
	char *outReplyFile[256]; /* JPJ: c-string version of outReply.fName */
	
	dirty = TRUE;
	SFPutFile(gOrigin,(unsigned char *)"\pSave output in what file?",
		(unsigned char *)"\pStudy.fil", NULL, &outReply);
	if (outReply.good) {
		if (fclose(inp)) alert(1,6);
		inpfile = FALSE;
		resultCode = GetFInfo(outReply.fName,outReply.vRefNum, &theInfo);
		PStrCpy( outReplyFile, outReply.fName); /* JPJ: make copy of the file name */
		PtoCstr((StringPtr)outReplyFile);	/* JPJ: make it a c-string */
		
		switch (resultCode) {
		case noErr: break;
		case fnfErr:
		/*	PtoCstr(outReply.fName); JPJ: need to convert outside the switch() */
#ifdef TTHREE
						/* JPJ: changed fName[0] to fName[1]: this is a p-string */
			resultCode = Create (outReply.fName,outReply.vRefNum,'LOGN',
				'TEXT');
#else
			resultCode = Create (outReply.fName,outReply.vRefNum,'LOG3',
			'TEXT');
#endif
			IOCheck(resultCode);
			break;
		default: IOCheck(resultCode);
			}
#ifdef THINK_C /* JPJ : Think C doesn't do the vRefNum filename trick */
		sprintf(filename,"%s",outReplyFile);
#else
		sprintf(filename,"%d:%s",outReply.vRefNum,outReplyFile);
#endif
		if (!(inp = fopen(filename,"w"))) alert(1,3);
		inpfile = TRUE;
		writefile(inp);
		}
	}

void savestat(void) 

/* appends the session statistics to the appropriate statistics
 file, dependent on programme and mode, and creates the file if 
 it does not already exist*/

{//	short i;
	sessions();
#ifdef TTHREE
	if (rrflag == '1' && affixflag == '1')
		if (!(stats = fopen("Stats3.paf","a"))) alert(1,3);
	if (rrflag == '1' && affixflag == '2')
		if (!(stats = fopen("Stats2.le","a"))) alert(1,3);
	if (rrflag == '2' && affixflag == '1')
		if (!(stats = fopen("Stats3.afp","a"))) alert(1,3);
	if (rrflag == '2' && affixflag == '2')
		if (!(stats = fopen("Stats2.el","a"))) alert(1,3);
#else
	if (rrflag == '1') {
		if (!(stats = fopen("Stats1.le","a"))) alert(1,3);
		}
	else
		if (!(stats = fopen("Stats1.el","a"))) alert(1,3);
#endif
	statline();
	testtotl = righttot = tested = maximum = 0;
	sqtotal = ticktotl = timetotl = 0L;
	minimum = 10000;
	DisableItem (gMyMenus[3],cStats);
	HiliteControl((ControlHandle) gMainHdl[mStats],255);
	}

void ScrollBits(void)

/* A standard borrowed Macintosh routine for doing the actual scrolling of 
a Text Edit record */

{	Point oldOrigin;
	short dh, dv,maxht;
	maxht = (*ghTE)->nLines;
	oldOrigin = gTheOrigin;
	gTheOrigin.v = (GetControlValue(gvScroll) * (maxht-winht))/maxht;
	gTheOrigin.v *= (*ghTE)->lineHeight;
	dh = oldOrigin.h - gTheOrigin.h;
	dv = oldOrigin.v - gTheOrigin.v;
	TEScroll(dh, dv, ghTE);
}

pascal void ScrollDown(ControlHandle WhichControl, short theCode)

/*These are standard routines for scrolling when the mouse is in one of 
the arrow buttons of a scroll bar*/

{	if (theCode == (short)kControlDownButtonPart) {
		SetControlValue(gWhichControl, GetControlValue(WhichControl)+1);
		ScrollBits();
	}
}

pascal void ScrollUp(ControlHandle WhichControl, short theCode)

/*These are standard routines for scrolling when the mouse is in one of the 
arrow buttons of a scroll bar*/

{	if (theCode == (short)kControlUpButtonPart) {
		SetControlValue(WhichControl, GetControlValue(WhichControl)-1);
		ScrollBits();
	}
}

/* Calculate and write the session statistics */

void sessions(void)

/* Calculate and write the session statistics in a screen window */

{//	long temp;
	short i,item,theType;
	DialogPtr statDialog;	/*Dialog pointer for statistics record*/
	DialogRecord	statRecord;	
	Handle	itemHdl[9];	/*Storage for handles of Test/Review dialog*/
	setstat();
	getend();
	statDialog = GetNewDialog(statID,&statRecord,(DialogPtr)-1L);
	ShowWindow(statDialog);
	outlin(1,statDialog);
	for (i=2; i<9; ++i) GetDialogItem(statDialog,i,&theType,&itemHdl[i],&gItemBox);
	sprintf (instring,"%2d/%02d/%02d",stime->tm_mon+1,stime->tm_mday,
		stime->tm_year);
	SetDialogItemText(itemHdl[2],CtoPstr(instring));
	sprintf(instring,"%2d:%02d",shour,smin);
	SetDialogItemText(itemHdl[3],CtoPstr(instring));
	sprintf(instring,"%2d:%02d",etime->tm_hour,etime->tm_min);
	SetDialogItemText(itemHdl[4],CtoPstr(instring));
	sprintf (instring,"%d",testtotl);
	SetDialogItemText(itemHdl[5],CtoPstr(instring));
	sprintf(instring,"%d",righttot);
	SetDialogItemText(itemHdl[6],CtoPstr(instring));
	sprintf(instring,"%d%%",percent);
	SetDialogItemText(itemHdl[7],CtoPstr(instring));
	sprintf (instring,"%d.%02d ± %d.%02d secs",average/60,
		((average%60)*5)/3,sdevn/60,((sdevn%60)*5)/3);
	SetDialogItemText(itemHdl[8],CtoPstr(instring));
	GetDialogItem(statDialog,17,&theType,&itemHdl[1],&gItemBox);
	sprintf (instring,"%d",tmouts);
	SetDialogItemText(itemHdl[1],CtoPstr(instring));
	ModalDialog(0L,&item);
	DisposeDialog(statDialog);
	SetPort(gMainDialog);
	}

void setrr(short val)

/* Set the recognition-recall flag to value val and check the appropriate menu item */

{	short j;
	switch(prog) {
	case '2':
	case '4':
		j = (prog=='2') ? 255 : 0;
		HiliteControl((ControlHandle)gMainHdl[mRecall],j);
		break;
	default:
		break;
		}
	CheckItem(gMyMenus[4],rrflag-'0',FALSE); /*uncheck current item */
	SetControlValue((ControlHandle)gMainHdl[rrflag + 17 -'0'],BtnOff); //Recognize or Recall
	rrflag = val; /*Reset the flag*/
	CheckItem(gMyMenus[4],rrflag-'0',TRUE); /*and check the new value*/
	SetControlValue((ControlHandle)gMainHdl[rrflag + 17 - '0'],BtnOn);//Recognize or Recall
	tested = 0;
	display();
	}

void setrung(short val)

/* Set the rung flag to value val and check the appropriate menu item */

{	CheckItem(gMyMenus[4],rungflag+moRung1-'1',FALSE);/*uncheck current item */
	SetControlValue((ControlHandle)gMainHdl[rungflag-'.'],BtnOff); //Old Rungx button
	rungflag = val; /*Reset the flag*/
	CheckItem(gMyMenus[4],rungflag+moRung1-'1',TRUE);/*and check the new value*/
	SetControlValue((ControlHandle)gMainHdl[rungflag-'.'],BtnOn);//New Rungx button
	}

void setscroll(void)

/* Adjust the scroll bar range according to the text length and window height*/

{	short maxl;
	winht = (gHelpWindow->portRect.bottom - gHelpWindow->portRect.top - 30)
		/(*ghTE)->lineHeight;
	maxl = (*ghTE)->nLines;
	if (maxl<=winht) {
		HiliteControl(gvScroll,255);
		}
	else HiliteControl(gvScroll,0);
	SetControlMaximum(gvScroll,maxl);
	ScrollBits();
	}

void setshuf(short val)

/* Set the shuffle flag to value val and check the appropriate menu item */

{	shufflag = val;
	SetControlValue((ControlHandle) gMainHdl[mShuffle],val);
	SetMenuItemText(gMyMenus[3],cShuffle,(unsigned char *)(shufflag)?"\pNo Shuffle":"\pShuffle");
	}

void setupmenu(void)

/*Get the various menu resources, insert them and draw the menu bar*/

{	short i;

	gMyMenus[0] = GetMenu((short)appleMenu);
	AppendResMenu(gMyMenus[0], 'DRVR');
	gMyMenus[1] = GetMenu((short)fileMenu);
	gMyMenus[2] = GetMenu((short)editMenu);
	gMyMenus[3] = GetMenu((short)commandMenu);
	gMyMenus[4] = GetMenu((short)modeMenu);
	for (i=0;i<lastMenu;i++)
		InsertMenu(gMyMenus[i], 0);
	DisableItem(gMyMenus[2],0);
	DisableItem(gMyMenus[3],0);
	DisableItem(gMyMenus[4],0);
	for (i=2; i<7; ++i) DisableItem(gMyMenus[1],i);
	DrawMenuBar();
	}

void showcard(short n)

/* Display card n by getting the appropriate resource and hiding 
unwanted buttons if present and outlining the button activated by 
CR/ENTER */

{	short i,dlgno,j,theType;
	const short testID = 4;
	const short reviewID = 3;	/* Resource # of review dialog */
	dlgno = (n==cTest)? testID : reviewID;
	HideWindow(gMainDialog);
	gTestDialog = GetNewDialog(dlgno,&gTestRecord,(DialogPtr)-1L);
	if (gSDI) SetDialogCancelItem (gTestDialog,2) ;
#ifdef TTHREE
	j = 13;
#else
	j = 10;
#endif
	for (i=1; i<j;++i) 
		GetDialogItem(gTestDialog,i,&theType,&gItemHdl[i],&gItemBox);
	if (n != cSelect && n!=cTest) {
		HideControl((ControlHandle)gItemHdl[PASSF]);
		}
	ShowWindow(gTestDialog);
	outlin(1,gTestDialog);	// Flag the default button
	}

void showhelp(short n)

/* Display the gHelp message contained in resource n. Set the scroll bars 
appropriately */

{	if (FrontWindow() != gHelpWindow) ShowWindow(gHelpWindow);
	(*ghTE)->hText = GetResource('TEXT',n);
	if (!(*ghTE)->hText) return;
	(*ghTE)->teLength = GetResourceSizeOnDisk((*ghTE)->hText);
	TECalText(ghTE);
	setscroll();
	InvalRect(&gHelpWindow->portRect);
	SelectWindow(gHelpWindow);
	HiliteMenu(0);
	return;
	}

void showstat(void)

/* Calculate and display the statistics for a single test */

{	char mystr[40];
	long temp;
	testav = ticktotl/tested;
	temp = testav - moment;
	tsd = isqrt(labs(temp*temp - square/tested));
	sprintf (mystr,"Average time: %d.%02d ± %d.%02d secs",
		testav/60,((testav%60)*5)/3,tsd/60,((tsd%60)*5)/3);
	SetDialogItemText(gMainHdl[mAvTim],CtoPstr(mystr));
	sprintf (mystr,"Range: %d.%02d - %d.%02d, Overtime: %d",
		minimum/60,((minimum%60)*5)/3,maximum/60,((maximum%60)*5)/3,tmouts);
	SetDialogItemText(gMainHdl[mConfL],CtoPstr(mystr));
	}

OSErr MyGotRequiredParams (AppleEvent *theAppleEvent)
{
	DescType	returnedType;
	Size	actualSize;
	OSErr	err;

	err = AEGetAttributePtr (theAppleEvent, keyMissedKeywordAttr,
									typeWildCard, &returnedType, nil, 0,
									&actualSize);
	if (err == errAEDescNotFound)	// you got all the required parameters
			return noErr;
	else if (!err)			// you missed a required parameter
			return errAEEventNotHandled;
	else					// the call to AEGetAttributePtr failed
			return err;
}

pascal OSErr
MyHandleOApp (AppleEvent *theAppleEvent, AppleEvent *reply, long Refcon)

{	OSErr	err;
	short i,itemHit;
	if (err = MyGotRequiredParams(theAppleEvent)) return err;
	if (!gInReply.fName[0]) {
	#ifdef TONE
		i = 999;
	#else
		i = 997;
	#endif
		gAbtDialog = GetNewDialog(i, &gAbtRecord, (DialogPtr)-1L);
		outlin(1,gAbtDialog);
		SetDialogCancelItem (gAbtDialog,2) ;
		ModalDialog(0L,&itemHit);
		CloseDialog(gAbtDialog);
		if (itemHit==2) dohelp();
		else doopen();
		}
	return noErr;

	}

pascal OSErr
MyHandleODoc (AppleEvent *theAppleEvent, AppleEvent* reply, long
														RefCon)
{
	FSSpec	myFSS;
	AEDescList	docList;
	OSErr	err;
	long	index,itemsInList;
	Size	actualSize;
	AEKeyword	keywd;
	DescType	returnedType;

	// get the direct parameter--a descriptor list--and put it into a docList
	err = AEGetParamDesc (theAppleEvent, keyDirectObject, typeAEList,
			&docList);
	if (err) return err;

	// check for missing parameters
	err = MyGotRequiredParams (theAppleEvent);
	if (err) return err;

	// count the number of descriptor records in the list
	err = AECountItems (&docList, &itemsInList);
	if (err) return err;

	// now get each descriptor record from the list, coerce the returned
	// data to an FSSpec record, and open the associated file
	//	printf ("Files to open:\n");
	for (index = 1; index <= itemsInList; index++) {

			err = AEGetNthPtr (&docList, index, typeFSS, &keywd,
							&returnedType, (Ptr) &myFSS, sizeof(myFSS), 				
&actualSize);
			if (err) return err;

			PStrCpy(filename,myFSS.name);
			PtoCstr((StringPtr)filename);
			PStrCpy(gInReply.fName,myFSS.name);
			doopen();
			
	}

	err = AEDisposeDesc (&docList);
	return noErr;
}



pascal OSErr  
MyHandlePDoc(AppleEvent *theAppleEvent, AppleEvent *reply, long
														handlerRefCon)
{
	FSSpec	myFSS;
	AEDescList	docList;
	OSErr	err;
	long	index,
					itemsInList;
	Size	actualSize;
	AEKeyword	keywd;
	DescType	returnedType;

	// get the direct parameter--a descriptor list--and put it into a docList
	err = AEGetParamDesc (theAppleEvent, keyDirectObject, typeAEList,
									&docList);
	if (err)
			return err;

	// check for missing parameters
	err = MyGotRequiredParams (theAppleEvent);
	if (err)
			return err;

	// count the number of descriptor records in the list
	err = AECountItems (&docList, &itemsInList);
	if (err)
			return err;

	// now get each descriptor record from the list, coerce the returned
	// data to an FSSpec record, and open the associated file
	printf ("Files to print:\n");
	for (index = 1; index <= itemsInList; index++) {

			err = AEGetNthPtr (&docList, index, typeFSS, &keywd,
							&returnedType, (Ptr) &myFSS, sizeof(myFSS), 				
&actualSize);
			if (err)
					return err;

			printf ("%s\n", PtoCstr(myFSS.name));
	}

	err = AEDisposeDesc (&docList);
	return noErr;
}

	
pascal OSErr
MyHandleQUIT (AppleEvent *theAppleEvent, AppleEvent *reply, long Refcon)
{	
	OSErr iErr;
	
	if ( iErr = MyGotRequiredParams(theAppleEvent)) 
		return iErr;
	else {
		doClose();
		exit(1);
		}
	return noErr;
	}

Boolean TrapAvailable(short theTrap)

{	TrapType tType;
	tType = GetTrapType(theTrap);
	if (tType==ToolTrap) 
		if ((theTrap&&0x7ff)>=NumToolboxTraps()) theTrap=Unimplemented;
	return NGetTrapAddress(theTrap,tType) != 
		NGetTrapAddress(Unimplemented,ToolTrap);	
	}

short NumToolboxTraps(void)

{	// const short  _InitGraf = 0xA86E;	/* JPJ: added for THINK C */
	return(NGetTrapAddress(0xA86E, ToolTrap) == 
		NGetTrapAddress(0xaa6e,ToolTrap)) ? 0x200 : 0x400;
	}
	

TrapType GetTrapType(short theTrap)

{	return ((theTrap&&0x800)>0)? ToolTrap : OSTrap;
	}
	
