/*
 * $Source: $
 * $Revision: $
 * $Date: $
 * $State: $
 * $Author: $
 *
 *
 * $Log: $
 * Revision 3.1  3/10/92 wade
 * Added Baylor's changes
 * cmd_read_gif_file()
 * documentRec mods, some GIF code, some DEBUG code, some re-structuring
 *
 * Revision 3.1 3/31/92 wade
 * Added list_DeActive to fix list scroll bar bug
 * fixed the deactivate event for the getinfo dialog
 *
 * Revision 3.1 4/8/92 wade
 * Removed occurances of gGetInfoWindow and gCurrentItem
 * started to clean up routines that use wind_type() - e.g. added case statements
 *
 * 6/1/92 - mec
 * Removed ifdef's for DEBUG, replaced with DEBUGSTR - DEBUG was always defined as it is a
 * menu item.  See notes at top of GIFPictMgr.c
 *
 * 6/20/92 - wade
 * Added menu_show_popup(window)
 *
 * Revison 3.3 7/20/92 - wade
 * changed DoContentClick() to handle clicks in the custom control for
 * menu window's.
 *
 * Revision 3.4 8/14/92
 * A little more work on DoContentClick() to handle custom controls.
 *
 * 8/17/92 - mec
 * moved call to menu_adjust in DoEvent - it was getting called _after_ the call to
 * MenuKey, making MenuKey occasionally return an incorrect response.
 * 
 * Revision 3.5 - wade
 * Changed DoActivate() - replaced DrawGrowIcon with wind_draw_grow()
 * Added a call to sort_list() in DoContentClick()
 *
 * Revision 3.7 - wade
 * Added global variables - gKiosk, gInactiveTimeout
 * Addded initialize of gKiosk
 * Changed DoIdle() to handle inactivity timeouts for kiosk mode
 *
 * Modified 4/30/93 - edward
 * Added defines to compile with or without TE32K
 * Incorporated changes that I made to version 4.0b1:
 * Changed the TextEdit calls to TE32K calls and changed various types to long as needed
 *
 * Revision 4.1 6/7/92 - edward
 * Fixed DoEvent and DoKeyDown Event so that if a cmd key was pressed that didn't exist, it wasn't 
 * put into the text window.
 *
 * Revision 4.1 6/14/93 - edward
 * changed "rc = cmd_back()" to "cmd_back()" in DoKeyDown.  cmd_back doesn't return anything.
 *
 * Revision 4.1 6/15/93 - edward
 * In DoKeyDown, if front window is a full text window and key is an arrow key, page up/down, 
 *  home/end, or forward del, are still passed to TE(32K)Key.
 *
 * Revision 4.1 7/6/93 - edward
 * In DoEvent, if user clicks in drag region of list window, show popup only if list is front window
 *
 * Revision 4.1b2 7/22/93 - edward
 * Initialize calls ae_init to initialize AppleEvents
 * DoEvents handles AppleEvents
 * Initialize no longer shows the disclaimer dialog.  It is only shown in response to kAEOpenApplication
 * Included GestaltEqu.h and EPPC.h
 *
 * Revision 4.1b2 7/27/93 - edward
 * Added gDone, which is set to true in Terminate.  eventloop loops until gDone == true
 *
 * Revision 4.1b6 1/24/94 - edward
 * The event loop does it's first time through stuff at the first idle event
 *
 * Revision 4.2WWW 3/7/93 - edward
 * Replaced all references to list, text, gif, and binhex with NT_MENU, NT_ASCII, NT_GIF, 
 *  and NT_BINHEX
 * Included node.h
 *
 * Revision 4.2a3 6/8/94 - edward
 * Included StandardFile
 *
 * Revision 4.2a3 6/9/94 - edward
 * Removed TrapAvailable because TrapExists is in Utilities.c
 * Modified TrapAvailable call in Initialize to not pass ToolTrap
 * Wrapped the proc being passed to TrackControl in DoContentClick in UPP procs
 *
 * Revision 4.2a3 6/17/94 - edward
 * Included stdlib.h, which has the prototype for atoi
 * 
 * Revision 4.2a4 7/1/94 - edward
 * Made main void
 * Initialize calls InitRoutineDescriptors
 * Added InitRoutineDescriptors
 *
 * Revision 4.2a4 7/5/94 - edward
 * Porting to MW C/C++ 68K.  The headers are only included if we're not using MW
 *
 * Revision 4.2a5 8/10/94 - edward
 * Added testtracker
 */


/*------------------------------------------------------------------------------
 *	Copyright (c) 1991 by the Massachusetts Institute of Technology,
 *	For copying and distribution information, see the file
 *	"mit-copyright.h".
#
#	Information Systems
#	Network Services
#
#	TechInfo Version 1.0
#	WADE 9/1/89
#
#	Components:
#		techinfo.make					- makefile to produce pips module.
#		techinfo.r						- resource file in text format.

#		ldef.make
#		pips.h							-
#		prototype.h
#		
#		commandMgr.c	commandMgr.c.o	-
#		dataMgr.c		dataMgr.c.o		-
#		dialogMgr.c		dialogMgr.c.o	-
#		ldef.c			ldef.c.o		-
#		listMgr.c		listMgr.c.o		- where all the list manager routines are.
#		main.c			main.c.o		- main routine for pips module.
#		menuMgr.c 		menuMgr.c.o		-
#		networkMgr.c	networkMgr.c.o	-
#		printMgr.c		printMgr.c.o	-
#		textMgr.c		textMgr.c.o		-
#		utilMgr.c		utilMgr.c.o		-
#		windowMgr.c		windowMgr.c.o	-
#
#       The following routines are by SRZ.
#		net_stuff.c 	net_stuff.c.o	- contains low level MACTCP calls.
#		net_stuff.h
#		mtypes.h
#
#		TextEdit code extracted from TESample.c from MPW 3.0
#		pipsGlue.a						- AsmClikLoop procedur for TextEdit	
#			Copyright © 1988 Apple Computer, Inc.
#			All rights reserved.
------------------------------------------------------------------------------*/

/*  
 *	main.c
 *		The functions contained in this file (main.c) perform initialization and handle events,
 *		passing control to the appropriate functions.
 *
 *	main
 *	EventLoop
 *	DoEvent
 *	GetLocalUpdateRgn
 *	DoUpdate
 *	DoActivate
 *	DoContentClock
 *	DoKeyDown
 *	GetSleep
 *	DoIdle
 *	Terminate
 *	Initialize
 *	TrapAvailable
 */

// 7/5/94 edward - If we're compiling with MW all of these headers are precompiled in TIFast*
#ifndef __MWERKS__

#include <Types.h>
#include <QuickDraw.h>
#include <Fonts.h>
#include <Events.h>
#include <Controls.h>
#include <Windows.h>
#include <Menus.h>
#include <TextEdit.h>
#include <Dialogs.h>
#include <Desk.h>
#include <Scrap.h>
#include <ToolUtils.h>
#include <Memory.h>
#include <SegLoad.h>
#include <Files.h>
#include <OSUtils.h>
#include <Traps.h>
#include <Lists.h>
#include "pips.h"  
#include <resources.h>
#include <string.h>
#include <strings.h>
#include <OSEvents.h>
#include <FCntl.h>
#include <stdio.h>
#include <DiskInit.h>
#include <AppleEvents.h>
#include <StandardFile.h>
#include <StdLib.h>
#include "Utilities.h"
#ifdef TE32K
#include "TE32K.h"
#endif
#include "documentRec.h"
#include "prototype.h"
#include "node.h"

#endif

#include <Limits.h>
#include <GestaltEqu.h>
#include "mit-copyright.h"
#include <EPPC.h>
#include "testtrack.h"

/* The "g" prefix is used to emphasize that a variable is global. */

SysEnvRec	gMac;				/* 	GMac is used to hold the result of a SysEnvirons call. This
   									makes it convenient for any routine to check the 
									environment. It is global information, anyway. */	
								/* 	set up by Initialize */

// gHasWaitNextEcent and gInBackground are declared in Utilities.h
extern Boolean	gHasWaitNextEvent;	/* 	GHasWaitNextEvent is set at startup, 
									and tells whether the WaitNextEvent trap is available. 
									If it is false, we know that we must call GetNextEvent. */
								/* 	set up by Initialize */

extern Boolean	gInBackground;	/* 	GInBackground is maintained by our OSEvent handling 
									routines. Any part of the program can check it to find out
									if it is currently in the background. */
								/* 	maintained by Initialize and DoEvent */


short		gNumDocuments;		/* 	GNumDocuments is used to keep track of how many open 
									documents there are at any time. It is maintained by the
									routines that open and close documents. */
								/* maintained by Initialize, DoNew, and DoCloseWindow */
								
Boolean		gKiosk;				/* true if we are in kiosk mode, false otherwise */

Boolean		gDone = false;		/* true if eventloop is done */

WindowPtr		gListWindow;
WindowPtr		gTextWindow;
Boolean			gDebug;
DialogPtr		gMsgDialog;
ListHandle		glH;
int				gListSize;
Handle			gTextStr;
char			gMessage[MESSAGE_RECEIVE_SIZE];
Boolean			gBitBucket = false;
Boolean			gProvider = false;
unsigned long 	gInactiveTimeout;  	/* this is the next timeout period when in kiosk mode */
int				gKiosk_interval;

// Global UPPs
ControlActionUPP gVActionProc;
ControlActionUPP gHActionProc;
ControlActionUPP gGIFActionProc;

// edward - Do we really save anything by unloading A5init anymore?
// extern void _DataInit();			/* used to unload its segment %A5init. */

/* Local functions */
static void EventLoop(void);
static void Initialize(void);
static void track_it(void);
static void InitRoutineDescriptors(void);

#pragma segment Main

// edward 7/1/94 - made main void
void main(void)
{
	int		rc, start_rc;
	Ptr		stack_limit;
	Str255	cwis_server;
	Str255	cwis_port;
	char	kiosk[6];
	Str255	interval_str;

// edward	
//	UnloadSeg((Ptr) _DataInit);		/* note that _DataInit must not be in Main! */

	stack_limit = GetApplLimit();   /* do this to prevent stack overflow?? */
	SetApplLimit(stack_limit-6144);
	
	MaxApplZone();					/* expand the heap so code segments load at the top */

	Initialize();					/* initialize the program */
// edward
//	UnloadSeg((Ptr) Initialize);	/* note that Initialize must not be in Main! */
	
	/* determine if we are in kiosk mode */
	/* this will only happen if their was a hard shutdown - e.g. power hit */
	pref_get_string(KIOSK_FLAG, kiosk);
	if (!strcmp(kiosk,"true")) {
		gKiosk = true;
		/* get the interval we were using */
		pref_get_string(KIOSK_INTERVAL, interval_str);
		gKiosk_interval = atoi(interval_str);
	}
	else
		gKiosk = false;
		
	pref_get_string(SERVER_NAME, cwis_server);
	data_host(store,cwis_server);
	
	pref_get_string(SERVER_PORT, cwis_port);
	data_port(store,cwis_port);

	rc = net_open(true); 			/* establish connection to the server, get banner */
		
	switch(rc) {
		case 0:
		case DISPLAY_MSG:			/* DISPLAY_MSG == 101 */
			start_rc = cmd_startup();			/* display menu or document */
			break;
		case SHUTDOWN_MSG:			/* SHUTDOWN_MSG == 100, display it and allow user to quit */
			break;
	}
	
	EventLoop();					/* call the main event loop */
}

/*
 *	EventLoop:
 *
 * Get events forever, and handle them by calling DoEvent.
 * Also call AdjustCursor each time through the loop. 
 *
 */
#pragma segment Main
static void EventLoop(void)
{
  RgnHandle	cursorRgn;
  Boolean		gotEvent;
  EventRecord	event;
  int version_rc;
  Boolean		firstTimeThrough = true;
  
  cursorRgn = NewRgn();			/* weÕll pass WNE an empty region the 1st time thru */
  do
	{
	  /* use WNE if it is available */
	  if ( gHasWaitNextEvent )
		gotEvent = WaitNextEvent(everyEvent, &event, GetSleep(), cursorRgn);
	  else {
		SystemTask();
		gotEvent = GetNextEvent(everyEvent, &event);
	  }
	  if ( gotEvent ) {
		/* make sure we have the right cursor before handling the event */
		AdjustCursor(event.where, cursorRgn);
		DoEvent(&event);
	  }
	  else
		{
		  /* Do first time through stuff after all initial event have been processed */
		  if (firstTimeThrough)
			{
			  version_rc = cmd_get_version(BROWSER,true);
			  if (!version_rc)
				Terminate();  		/* user wants to exit the program */
			  firstTimeThrough = false;
			}
		  
		  DoIdle();				/* perform idle tasks when itÕs not our event */
		  /* change the cursor (and region) if necessary */
		  AdjustCursor(event.where, cursorRgn);
		}
	}
  while(!gDone);					/* edward 7/26/93 - loop until done */
} /*EventLoop*/

/*
 *	DoEvent:
 *
 * 	Do the right thing for an event. Determine what kind of event it is, and call
 *	the appropriate routines. 
 *
 */
#pragma segment Main
void DoEvent(EventRecord *event)
{
	short		part;
	WindowPtr	window;
	char		key;
	Point		aPoint;
	short		err;
	long		menuChoice;				/* edward 6/21/93 - menu and item chosen */
	
	switch ( event->what ) {
		case kHighLevelEvent:			/* edward 7/22/93 - Handle AppleEvents */
			AEProcessAppleEvent(event);
			break;
		case nullEvent:
			/* we idle for null/mouse moved events and for events which arenÕt ours */
			DoIdle();
			break;
		case mouseDown:
			part = FindWindow(event->where, &window);
			switch ( part ) {
				case inMenuBar:             /* process a mouse menu command (if any) */
					menu_adjust();	/* bring Õem up-to-date */
					menu_do_command(MenuSelect(event->where));
					break;
				case inSysWindow:           /* let the system handle the mouseDown */
					SystemClick(event, window);
					break;
				case inContent:
					if ( window != FrontWindow() ) {
						wind_set_cursor(5);  // set the cursor to the watch - it will
						SelectWindow(window);// get reset when we get an Idle event
					} else
						DoContentClick(window, event);
					break;
				case inDrag: 
					if (event->modifiers & cmdKey && wind_type(window) == NT_MENU && window == FrontWindow())
						menu_show_popup(window);
					else
						DragWindow(window, event->where, &qd.screenBits.bounds);
					break;
				case inGoAway:
					if ( TrackGoAway(window, event->where) )
						DoCloseWindow(window,hide_window); 
					break;
				case inGrow:
					if (!IsDialogEvent(event))
						DoGrowWindow(window, event);
					break;
				case inZoomIn:
				case inZoomOut:
				if (!IsDialogEvent(event))
					if ( TrackBox(window, event->where, part) )
						DoZoomWindow(window, part);
					break;
			}
			break;
		case keyDown:
		case autoKey:                       		/* check for menukey equivalents */
			key = event->message & charCodeMask;
			if (event->modifiers & cmdKey && event->what == keyDown)	/* Command key down */
			{
				menu_adjust();						/* enable/disable/check menu items properly */
				menuChoice = MenuKey(key);
				if (menuChoice)
					menu_do_command(menuChoice);
				else
					DoKeyDown(event);
			}
			else
				DoKeyDown(event);
			break;
		case activateEvt:
			if (!IsDialogEvent(event)) 
				DoActivate((WindowPtr) event->message, (event->modifiers & activeFlag) != 0);
			break;
		case updateEvt:
			DoUpdate((WindowPtr) event->message);
			break;
		case diskEvt:
			if ( HiWord(event->message) != noErr ) {
				SetPt(&aPoint, kDILeft, kDITop);
				err = DIBadMount(aPoint, event->message);
			}
		case kOSEvent:
			switch (event->message >> 24) {		/* high byte of message */
				case kMouseMovedMessage:
					DoIdle();					/* mouse-moved is also an idle event */
					break;
				case kSuspendResumeMessage:		/* suspend/resume is also an activate/deactivate */
					gInBackground = (event->message & kResumeMask) == 0;
					DoActivate(FrontWindow(), !gInBackground); 
					break;
			}
			break;
	}
} /*DoEvent*/

/*
 *	GetLocalUpdateRgn:
 *
 *	Returns the update region in local coordinates 
 *
 */
#pragma segment Main
void GetLocalUpdateRgn(WindowPtr window, RgnHandle localRgn)
{
	CopyRgn(((WindowPeek) window)->updateRgn, localRgn);	/* save old update region */
	OffsetRgn(localRgn, window->portBits.bounds.left, window->portBits.bounds.top);
	
} /* GetLocalUpdateRgn */

/*
 *	DoUpdate:
 *
 *	This is called when an update event is received for a window.
 *	It calls DrawWindow (if the visRgn is nonempy) to draw the contents 
 *	of an application window.
 *
 */
#pragma segment Main
void DoUpdate(WindowPtr window)
{
	
	if ( IsAppWindow(window) ) {
		switch( wind_type(window)) {
			case NT_MENU:
			BeginUpdate(window);			/* this sets up the visRgn */
			DrawWindow( window );
			EndUpdate(window);
			break;
			
			case NT_ASCII:
			BeginUpdate(window);			/* this sets up the visRgn */
			if ( ! EmptyRgn(window->visRgn) )	/* draw if updating needs to be done */
				DrawWindow( window );
			EndUpdate(window);
			break;
			
			case NT_GIF:
			BeginUpdate(window);			/* this sets up the visRgn */
			if ( ! EmptyRgn(window->visRgn)) 	/* draw if updating needs to be done */
				DrawWindow( window );
			EndUpdate(window);
			break;
			
			case getinfo:
			cmd_get_info(window);			/* let this routine do the drawing */
			break;
		}

	}
} /*DoUpdate*/

/*
 *	DoActivate:
 *
 *	This is called when a window is activated or deactivated.
 *	It calls TextEdit to deal with the selection. 
 */
#pragma segment Main
void DoActivate(WindowPtr window, Boolean becomingActive)
{
	RgnHandle	tempRgn, clipRgn;
	DocumentPeek 	doc;
		
	if ( IsAppWindow(window) ){
		switch(wind_type(window)) {
			case NT_ASCII:
			
				doc = (DocumentPeek) window;
				if ( becomingActive ) {
					/*	since we donÕt want TEActivate to draw a selection in an area where
						weÕre going to erase and redraw, weÕll clip out the update region
						before calling it. */
					SetPort( window );
					tempRgn = NewRgn();
					clipRgn = NewRgn();
					GetLocalUpdateRgn(window, tempRgn);			/* get localized update region */
					GetClip(clipRgn);
					DiffRgn(clipRgn, tempRgn, tempRgn);			/* subtract updateRgn from clipRgn */
					SetClip(tempRgn);
#ifdef TE32K
					TE32KActivate(doc->docTE);
#else
					TEActivate(doc->docTE);
#endif					
					SetClip(clipRgn);							/* restore the full-blown clipRgn */
					DisposeRgn(tempRgn);
					DisposeRgn(clipRgn);
			
					/* the controls must be redrawn on activation: */
					(*doc->docVScroll)->contrlVis = kControlVisible;
					(*doc->docHScroll)->contrlVis = kControlVisible;
					InvalRect(&(*doc->docVScroll)->contrlRect);
					InvalRect(&(*doc->docHScroll)->contrlRect);
				
					/* the growbox needs to be redrawn on activation: 
					growRect = window->portRect;
					/# adjust for the scrollbars #/
					growRect.top = growRect.bottom - kScrollbarAdjust;
					growRect.left = growRect.right - kScrollbarAdjust;
					*/
					DrawGrowIcon(window);
				
					/* make the vis region equal to the update region */
					/* InvalRect(&growRect); */
					/* InvalRgn(window -> visRgn ); */
					/* ValidRect(&growRect); */
				
				} /* becomingActive */
				else {	
#ifdef TE32K
					TE32KDeactivate(doc->docTE);
#else
					TEDeactivate(doc->docTE);
#endif

					/* the controls must be hidden on deactivation: */
					HideControl(doc->docVScroll);
					HideControl(doc->docHScroll);
					
					/* the growbox should be changed immediately on deactivation: */
					DrawGrowIcon(window);
				}
			
				break;
			
			case NT_MENU:
			
				SetPort( window );
				if ( becomingActive ) 
					list_DeActive(true);
					/* ShowControl((ControlHandle)list_get_vscroll()); */
				else
					list_DeActive(false);
					/* HideControl((ControlHandle)list_get_vscroll()); */
				
				/* change grow icon and scroll bar controls to on/off */
				wind_draw_grow(window);
			
				break;
			
			case NT_GIF:
		
				SetPort( window );
				doc = (DocumentPeek) window;
				if ( becomingActive ) {
					/* the controls must be redrawn on activation: */
					(*doc->docVScroll)->contrlVis = kControlVisible;
					(*doc->docHScroll)->contrlVis = kControlVisible;
					InvalRect(&(*doc->docVScroll)->contrlRect);
					InvalRect(&(*doc->docHScroll)->contrlRect);
				}
				else
				{
					/* the controls must be hidden on deactivation: */
					HideControl(doc->docVScroll);
					HideControl(doc->docHScroll);
				}
				
				/* change grow icon to on/off */
				DrawGrowIcon(window); 
			
				break;
			
			case getinfo:
				/* No window type (dialog), do nothing */
				break;
#ifdef DEBUGSTR	
			default:
				DEBUGSTR("\pUnknown WindowKind in DoActivate");
#endif
		}
		
	}	  /* appl test */
	
} /*DoActivate*/

/*
 *	DoContentClick:
 *
 *	This is called when a mouseDown occurs in the content of a window. 
 */
#pragma segment Main
void DoContentClick(WindowPtr window, EventRecord *event)
{
	Point			mouse;
	ControlHandle 	control;
	short			part, value;
	Boolean			shiftDown;
	DocumentPeek 	doc;
	Rect			teRect;
	GrafPtr			oldPort;
	Boolean			doubleClick;
	Boolean			mouse_down_click;
	short			mouse_down;
	Boolean			mouse_up_click;
	short			mouse_up;
	Boolean			rc;
	Str255			list_string;
	Str255			flag;
	Boolean			turn_off_hilighting = false;
	Str255			last_command;
	Boolean			reorder = false;

	if ( IsAppWindow(window) ) {
		GetPort ( &oldPort );
		SetPort(window);
		mouse = event->where;							/* get the click position */
		GlobalToLocal(&mouse);
		
		if ( wind_type(window) == NT_MENU ) {
		
			/* check to see if the click was in a control */
			part = FindControl(mouse,window,&control);
			
			switch(part) {
				case TITLE_CTRL:
				case DATE_CTRL:
				case SOURCE_CTRL:  
					/* turn off any existing highlghting */
					wind_cntl_value(window);
					SetCtlValue ( (ControlHandle)control, true );
					sort_list(part,window);
					break;
				default:
					/* a mouse down was in the content region */

					/* use the special reorder click loop if we are a provider  */
					if (gProvider) {
						/* don't allow reorder for search commands    */
						rc = util_query_stack(last_command);
						if (rc && *last_command != 'b')  {
							reorder = true;
				
							/* save anchor cell location */
							mouse_down_click = list_position(mouse,&mouse_down);
							if (mouse_down_click) 
								rc = data_anchor_cell(store,&mouse_down);
								
							/* switch to reorder click loop */
							cmd_reorder_state(on);
						}
					}
					
					doubleClick = list_click( mouse, event->modifiers );
					
					/* make sure that we are using the old click loop */
					if (reorder)
						cmd_reorder_state(off);
					
					if ( doubleClick )
						rc = cmd_show(NULL);
					else {
						/* determine how the current menu was created */
		
						/*  if we are using the provider click_loop, then
							determine if we need to reorder the menu */
						if ( reorder && mouse_down_click) {
						
							/* get local mouse position */
							GetMouse(&mouse);
							mouse_up_click = list_position(mouse,&mouse_up);
							
							/* if we have a bad mouse_up_click - give up */
							if ( !mouse_up_click )
								turn_off_hilighting = true;
							else {
								/* did we have a reorder? */
								if ( mouse_up != mouse_down )
									cmd_reorder(mouse_down,mouse_up);
								else {
									/*  we either had a single click or the user brought the cursor
										back to the starting cell */
									rc = list_get_buffer(list_string,mouse_down);
									if (rc) rc = get_field(list_string,FLAGS,flag);
									if (rc && (atoi(flag) & FRAME))
										turn_off_hilighting = true;    
								}
							}
							
							/* we are done - get rid of the anchor cell */
							mouse_down = -1;
							rc = data_anchor_cell(store,&mouse_down);
							
							if ( turn_off_hilighting ) {
								/* so, we aborted a reorder */
								list_remove_hilight();
								wind_set_cursor(0);  /* make sure cursor is an arrow */
							}
						}
					}
			}  /* end switch on part statement */
		}
		else if ( wind_type(window) == NT_ASCII ) {
			doc = (DocumentPeek) window;
			/* see if we are in the viewRect. if so, we wonÕt check the controls */
			GetTERect(window, &teRect);
			if ( PtInRect(mouse, &teRect) ) {
				/* see if we need to extend the selection */
				shiftDown = (event->modifiers & shiftKey) != 0;	/* extend if Shift is down */
#ifdef TE32K
				TE32KClick(mouse, shiftDown, doc->docTE);
#else
				TEClick(mouse, shiftDown, doc->docTE);
#endif
			} else {
				part = FindControl(mouse, window, &control);
				switch ( part ) {
					case 0:							/* do nothing for viewRect case */
						break;
					case inThumb:
						value = GetCtlValue(control);
						part = TrackControl(control, mouse, nil);
						if ( part != 0 ) {
							value -= GetCtlValue(control);
							/* value now has CHANGE in value; if value changed, scroll */
							if ( value != 0 )
#ifdef TE32K
								if ( control == doc->docVScroll )
									TE32KScroll(0L, (long)(value * (*doc->docTE)->lineHeight), doc->docTE);
								else
									TE32KScroll((long)value, 0L, doc->docTE);
#else
								if ( control == doc->docVScroll )
									TEScroll(0, value * (*doc->docTE)->lineHeight, doc->docTE);
								else
									TEScroll(value, 0, doc->docTE);
#endif
						}
						break;
					default:						/* they clicked in an arrow, so track & scroll */
						/* TechInfo 4.2a3 - added universal proc pointers */
						if ( control == doc->docVScroll )
							value = TrackControl(control, mouse, gVActionProc);
						else
							value = TrackControl(control, mouse, gHActionProc);
						break;
		
				}  /* end of switch */
			}	   /* end of else */
		}	   	   /* end of if (text) */
		else if ( wind_type(window) == getinfo ) {		/* No window type (dialog), do nothing */
		}
#ifdef GIFSTUFF
		else if ( wind_type(window) == NT_GIF ) {
			doc = (DocumentPeek) window;
			part = FindControl(mouse, window, &control);
			switch ( part ) {
				case 0:							/* do nothing for viewRect case */
					break;
				case inThumb:
					value = GetCtlValue(control);
					part = TrackControl(control, mouse, nil);
					if ( part != 0 ) {
						value -= GetCtlValue(control);
						/* value now has CHANGE in value; if value changed, scroll */
						if ( value != 0 )
							if ( control == doc->docVScroll )
								ScrollGIFWindow(window, value, 1);
							else if ( control == doc->docHScroll )
								ScrollGIFWindow(window, value, 2);
					}
					break;
				default:						/* they clicked in an arrow, so track & scroll */
					if (( control == doc->docVScroll ) || ( control == doc->docHScroll ))
						value = TrackControl(control, mouse, gGIFActionProc);
					break;
	
			}  /* end of switch */
		}	   	   /* end of if (gif) */
#endif	
#ifdef DEBUGSTR
		else {
			DEBUGSTR("\pUnknown WindowKind in DoContentClick");
		}
#endif
		SetPort ( oldPort );
	}			   /* end of IsAppWindow */
} /* DoContentClick */

/*
 *	DoKeyDown:
 *
 *	This is called for any keyDown or autoKey events, except when the
 *	Command key is held down. It looks at the frontmost window to decide what
 *	to do with the key typed. 
 *
 */
#pragma segment Main
void DoKeyDown(EventRecord *event)
{
	WindowPtr	window;
	char		key;
#ifdef TE32K
	TE32KHandle	te;
#else
	TEHandle	te;
#endif
	short		item;
	Boolean		rc;
	char		node_id[NODE_LENGTH];

	window = FrontWindow();
	if ( IsAppWindow(window) ) {
		switch( wind_type(window) ) {
			case NT_ASCII:
				if (event->modifiers & cmdKey)				/* edward 6/7/93 - ignore command keys */
					break;
				te = ((DocumentPeek) window)->docTE;
				key = event->message & charCodeMask;
				/* we have a char. for our window; see if we are still below TextEditÕs
					limit for the number of characters (but deletes are always rad) */
				if ( key == kDelChar || key == chUp || key == chDown || key == chLeft ||
					 key == chRight || key == chPageUp || key == chPageDown || key == chHome ||
					 key == chEnd || key == chFwdDelete ||
						(*te)->teLength - ((*te)->selEnd - (*te)->selStart) + 1 <
						kMaxTELength ) {
					if (key == kTabChar)
						/* add approprite number of spaces for a tab char */
						text_convert_tab(te); 
					else
#ifdef TE32K
						TE32KKey(key, te);
#else
						TEKey(key, te);
#endif
					AdjustScrollbars(window, false);
					AdjustTE(window);
				} else
					AlertUser(EXCEED_CHAR);
				break;
			case NT_MENU:
				key = event->message & charCodeMask;
				switch(key) {
					case chPageUp:
						list_scroll_page_up();
						break;
					case chPageDown:
						list_scroll_page_down();
						break;
					case chHome:
						list_scroll_home();
						break;
					case chEnd:
						list_scroll_end();
						break;
					case chUp:
						/* if cmd key - act like sys 7.0 finder */
						if (event->modifiers & cmdKey) {
							/* return to the previous menu */
							if (util_check_stack())
								cmd_back();
						}
						else
							list_up();
						break;
					case chDown:
						/* if cmd key - act like sys 7.0 finder */
						if (event->modifiers & cmdKey) {
							/* open the selected node item */
							if (list_get_list_select(&item))
							rc = cmd_show(NULL);
						}
						else
							list_down();
						break;
					case chRight:
						/* if cmd key - act like sys 7.0 finder */
						if (event->modifiers & cmdKey) {
							/* open the selected node item */
							if (list_get_list_select(&item)) {
								if (event->modifiers & optionKey)
									cmd_content(false); /* false = expand to a full depth -> 99 */
								else
									cmd_content(true);  /* true = use the default setting */
							}
						}
						break;
					case chLeft:
						/* if cmd key - act like sys 7.0 finder */
						if (event->modifiers & cmdKey) {
							/* get banner menu id to collapse */
							data_current_nodeid(retrieve,node_id);
							/* expand this node one level */
							rc = cmd_show(node_id);
						}
						break;
					case chReturn:
					case chEnter:
						/* open the selected node item */
						if (list_get_list_select(&item))
							rc = cmd_show(NULL);
						break;
				}
				break;
		}

	}
} /*DoKeyDown*/

/*
 *	GetSleep:
 *
 *	Calculate a sleep value for WaitNextEvent. This takes into account the things
 *	that DoIdle does with idle time. 
 */
#pragma segment Main
unsigned long GetSleep(void)
{
	long		sleep;
	WindowPtr	window;
#ifdef TE32K
	TE32KHandle	te;
#else
	TEHandle	te;
#endif
	sleep = LONG_MAX;					/* default value for sleep */
	if ( !gInBackground ) {
		window = FrontWindow();			/* and the front window is ours... */
		if ( IsAppWindow(window) ) {
										/* don't mess with a non document window */
			if ( wind_type(window) == NT_ASCII ) {
				te = ((DocumentPeek) (window))->docTE;	/* and the selection is an insertion point... */
				if ( (*te)->selStart == (*te)->selEnd )
					sleep = GetCaretTime();				/* blink time for the insertion point */
			}
		}
	}
	return sleep;
} /*GetSleep*/


/*
 *	DoIdle:
 *
 * 	This is called whenever we get a null event et al.
 *	It takes care of necessary periodic actions. For this program, it calls TEIdle. 
 */
#pragma segment Main
void DoIdle(void)
{
	WindowPtr			window;
	unsigned long		idle_time;
	int					start_rc;
		
	window = FrontWindow();
			
	if ( IsAppWindow(window) ) {
		if (gKiosk) {
			idle_time = TickCount();
			if (gInactiveTimeout > 0 && idle_time > gInactiveTimeout) {
			
				/* set the location and size of the list window */
				wind_place_list(gListWindow);
				
				/* now set the scrolling correctly after moving the window */
				list_Size(gListWindow);
				
				/* remove the stack - as if it's a new session */
				util_delete_stack();
				
				/* bring us back to the startup menu */
				start_rc = cmd_startup();
				
				/* Let's see if we can force the window to be large 
				DoZoomWindow(window, inZoomOut); */
				
				/* close existing connection so we don't keep it open */
				net_end();	
				
				/* don't let this conditional block get executed if the */
				/* user has not made any display change since we last   */
				/* executed this block. */
				gInactiveTimeout = 0;
			}
		}

		if (wind_type(window) == NT_ASCII)
#ifdef TE32K
			TE32KIdle(((DocumentPeek) window)->docTE);
#else
			TEIdle(((DocumentPeek) window)->docTE);
#endif
	}
	
} /*DoIdle*/

/*
 *	Terminate:
 *
 * 	Clean up the application and exit. We close all of the windows so that
 *	they can update their documents, if any. 
 *
 */
#pragma segment Main
void Terminate(void)
{
	WindowPtr	window;
	Boolean		closed;
	
	closed = true;
	do {
		window = FrontWindow();				/* get the current front window */
		if (window != nil)
			closed = DoCloseWindow(window,close_window);	/* close this window */	
	}
	while (closed && (window != nil));
	
	if (closed) {
		/* cmd_quit();							server does not send a "ok" message */
		net_end();								/* terminate any network connections */
	
	gDone = true;								/* edward - 7/27/93 don't exit to shell, just */
												/* set flag to say we're done */
	}
} /*Terminate*/

/*
 *	Initialize:
 *
 *	Set up the whole world, including global variables, Toolbox managers,
 *	menus, and a single blank document. 
 */
#pragma segment Initialize
static void Initialize(void)
{
	long		total, contig;
	EventRecord event;
	short		count;
	long		feature;			/* edward 7/22/93 - used for gestalt */
	
	InitGraf((Ptr) &qd.thePort);
	InitFonts();
	InitWindows();
	InitMenus();
#ifdef TE32K
	TE32KInit();
#endif
	TEInit();						/* edward - this remains because dialog boxes still use TE */
	InitDialogs(nil);
	InitCursor();
	InitUtilities();
	
	/*	Now this next bit of code may make you toss your cookies, but it is
		necessary to allow the default button of our alert be outlined. */
	for (count = 1; count <= 3; count++)
		GetNextEvent(everyEvent & ~highLevelEventMask, &event);
	
	/*	Ignore the error returned from SysEnvirons; even if an error occurred,
		the SysEnvirons glue will fill in the SysEnvRec. You can save a redundant
		call to SysEnvirons by calling it after initializing AppleTalk. */
	 
	SysEnvirons(SYS_ENVIRONS_VERSION, &gMac);
	
	/* Make sure that the machine has at least 128K ROMs. If it doesn't, exit. */
	if (gMac.machineType < 0) BigBadError(WRONG_MACHINE);
	
	/*	1.01 - We used to make a check for memory at this point by examining ApplLimit,
		ApplicZone, and StackSpace and comparing that to the minimum size we told
		MultiFinder we needed. This did not work well because it assumed too much about
		the relationship between what we asked MultiFinder for and what we would actually
		get back, as well as how to measure it. Instead, we will use an alternate
		method comprised of two steps. */
	 
	/*	It is better to first check the size of the application heap against a value
		that you have determined is the smallest heap the application can reasonably
		work in. This number should be derived by examining the size of the heap that
		is actually provided by MultiFinder when the minimum size requested is used.
		The derivation of the minimum size requested from MultiFinder is described
		in Sample.h. The check should be made because the preferred size can end up
		being set smaller than the minimum size by the user. This extra check acts to
		insure that your application is starting from a solid memory foundation. */
	 
	if ((long) GetApplLimit() - (long) ApplicZone() < MIN_HEAP) 
		BigBadError(SMALL_SIZE);
	
	/*	Next, make sure that enough memory is free for your application to run. It
		is possible for a situation to arise where the heap may have been of required
		size, but a large scrap was loaded which left too little memory. To check for
		this, call PurgeSpace and compare the result with a value that you have determined
		is the minimum amount of free memory your application needs at initialization.
		This number can be derived several different ways. One way that is fairly
		straightforward is to run the application in the minimum size configuration
		as described previously. Call PurgeSpace at initialization and examine the value
		returned. However, you should make sure that this result is not being modified
		by the scrap's presence. You can do that by calling ZeroScrap before calling
		PurgeSpace. Make sure to remove that call before shipping, though. */
	
	/* ZeroScrap(); */
	
	PurgeSpace(&total, &contig);
	if (total < MIN_SPACE)
		if (UnloadScrap() != noErr)
			BigBadError(NO_MEMORY);
		else {
			PurgeSpace(&total, &contig);
			if (total < MIN_SPACE)
				BigBadError(NO_MEMORY);
		}
	
	/*	The extra benefit to waiting until after the Toolbox Managers have been initialized
		to check memory is that we can now give the user an alert to tell him/her what
		happened. Although it is possible that the memory situation could be worsened by
		displaying an alert, MultiFinder would gracefully exit the application with
		an informative alert if memory became critical. Here we are acting more
		in a preventative manner to avoid future disaster from low-memory problems. */

	gNumDocuments = 0;
	gDebug = false;

	/* do other initialization here */
	
	InitRoutineDescriptors();		/* create rountine descriptors */

	menu_bar_init(BROWSER_BAR);		/* setup menu bar object */
	
	if (Gestalt(gestaltAppleEventsAttr, &feature) == noErr)
	{
		if (feature & (1L << gestaltAppleEventsPresent) )
			ae_init();						/* Install AppleEvent Handlers */
	}
	
	track_it();
		
	DoNew();						/* create a single empty document */
	
	wind_list_init(); 				/* create list window */
	
	DialogInit();					/* create message dialog object */
	
	
	NetworkInit();
} /*Initialize*/

/*
 * track_it
 *
 * This procedure gets the version number and calls the test tracker/version server
 *
 */
static void track_it(void)
{
	VersRecHndl versionh;
	
	UseResFile(gAppResRef);
	versionh = (VersRecHndl)GetResource('vers', VERSIONID);
	if (versionh != NULL)
	{
		HLock((Handle)versionh);
		p2cstr((**versionh).shortVersion);
		
		if (test_track("TechInfo-Mac", (**versionh).shortVersion, false, true, 1) != noErr)
			ExitToShell();
		c2pstr((**versionh).shortVersion);
		HUnlock((Handle)versionh);
	}
}

/*
 * InitRoutineDescriptors
 *
 * Calls *_int_upps procedures in other files to initialize upps in the other files
 *
 */
 
static void InitRoutineDescriptors(void)
{
	ae_init_upps();
	cmd_init_upps();
	dlog_init_upps();
	gif_init_upps();
	main_init_upps();
	text_init_upps();
}

/*
 * main_init_upps
 *
 * Initializes the UPPs used in this file
 *
 */
 
void main_init_upps(void)
{
	gVActionProc = NewControlActionProc(VActionProc);
	gHActionProc = NewControlActionProc(VActionProc);
	gGIFActionProc = NewControlActionProc(GIFActionProc);
}