/*
 * $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)
 */


/*------------------------------------------------------------------------------
 *	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
 */

#include <Limits.h>
#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 "prototype.h"
#include <resources.h>
#include <string.h>
#include <strings.h>
#include <OSEvents.h>
#include <FCntl.h>
#include <stdio.h>
#include <DiskInit.h>
#include "Utilities.h"
#include "mit-copyright.h"
#include "documentRec.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 */

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 */

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 */

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;

extern void _DataInit();			/* used to unload its segment %A5init. */

#pragma segment Main
main()
{
	int		rc, version_rc,start_rc;
	Ptr		stack_limit;
	Str255	cwis_server;
	Str255	cwis_port;
	char	doit[6];

	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 */
	UnloadSeg((Ptr) Initialize);	/* note that Initialize must not be in Main! */
	
	pref_get_string(SHOW_DISCLAIMER, doit);
	if (!strcmp(doit,"true"))
		rc = dlog_disclaimer();		/* put-up disclaimer dialog box */
		
	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 */
			version_rc = cmd_get_version(BROWSER);
			if (!version_rc)
				Terminate();  		/* user wants to exit the program */
				
			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
void EventLoop()
{
	RgnHandle	cursorRgn;
	Boolean		gotEvent;
	EventRecord	event;

	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
			DoIdle();				/* perform idle tasks when itÕs not our event */
		/* change the cursor (and region) if necessary */
		AdjustCursor(event.where, cursorRgn);
	} while ( true );	/* loop forever; we quit via ExitToShell */
} /*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(event)
	EventRecord	*event;
{
	short		part;
	WindowPtr	window;
	char		key;
	Point		aPoint;
	short		err;
	
	switch ( event->what ) {
		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) == list)
						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 && (HiWord(MenuKey(key)) > 0)) {		/* Command key down */
				if ( event->what == keyDown ) {
					if (key == 't')
						menu_add(TOYS);
					else {
						menu_adjust();					/* enable/disable/check menu items properly */
						menu_do_command(MenuKey(key));
					}
				}
			} 
			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(window,localRgn)
	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(window)
	WindowPtr	window;
{
	
	if ( IsAppWindow(window) ) {
		switch( wind_type(window)) {
			case list:
			BeginUpdate(window);			/* this sets up the visRgn */
			DrawWindow( window );
			EndUpdate(window);
			break;
			
			case text:
			BeginUpdate(window);			/* this sets up the visRgn */
			if ( ! EmptyRgn(window->visRgn) )	/* draw if updating needs to be done */
				DrawWindow( window );
			EndUpdate(window);
			break;
			
			case 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(window, becomingActive)
	WindowPtr	window;
	Boolean		becomingActive;
{
	RgnHandle	tempRgn, clipRgn;
	DocumentPeek 	doc;
		
	if ( IsAppWindow(window) ){
		switch(wind_type(window)) {
			case text:
			
				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);
					TEActivate(doc->docTE);
					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 {	
					
					TEDeactivate(doc->docTE);
					
					/* 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 list:
			
				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 */
				DrawGrowIcon(window); 
			
				break;
			
			case 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(window,event)
	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) == list ) {
		
			/* 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 */
					}
				}
			}
		}
		else if ( wind_type(window) == text ) {
			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 */
				TEClick(mouse, shiftDown, doc->docTE);
			} 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 )
								if ( control == doc->docVScroll )
									TEScroll(0, value * (*doc->docTE)->lineHeight, doc->docTE);
								else
									TEScroll(value, 0, doc->docTE);
						}
						break;
					default:						/* they clicked in an arrow, so track & scroll */
						if ( control == doc->docVScroll )
						value = TrackControl(control, mouse, (ProcPtr) VActionProc);
						else
							value = TrackControl(control, mouse, (ProcPtr) HActionProc);
						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) == 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, (ProcPtr) GIFActionProc);
					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(event)
	EventRecord	*event;
{
	WindowPtr	window;
	char		key;
	TEHandle	te;
	short		item;
	Boolean		rc;
	char		node_id[NODE_LENGTH];

	window = FrontWindow();
	if ( IsAppWindow(window) ) {
		switch( wind_type(window) ) {
			case text:
				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 ||
						(*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
						TEKey(key, te);
					AdjustScrollbars(window, false);
					AdjustTE(window);
				} else
					AlertUser(EXCEED_CHAR);
				break;
			case list:
				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())
								rc = 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))
								rc = cmd_content();
						}
						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()
{
	long		sleep;
	WindowPtr	window;
	TEHandle	te;

	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) == text ) {
				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()
{
	WindowPtr	window;
		
	window = FrontWindow();
			
	if ( IsAppWindow(window) && (wind_type(window) == text))
		TEIdle(((DocumentPeek) window)->docTE);
} /*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()
{
	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 */
		ExitToShell();							/* exit if no cancellation */
	}
} /*Terminate*/

/*
 *	Initialize:
 *
 *	Set up the whole world, including global variables, Toolbox managers,
 *	menus, and a single blank document. 
 */
#pragma segment Initialize
void Initialize()
{
	long		total, contig;
	EventRecord event;
	short		count;
	
	gHasWaitNextEvent = TrapAvailable(_WaitNextEvent, ToolTrap);
	gInBackground = false;

	InitGraf((Ptr) &qd.thePort);
	InitFonts();
	InitWindows();
	InitMenus();
	TEInit();
	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, &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 */
	
	menu_bar_init(BROWSER_BAR);		/* setup menu bar object */

	DoNew();						/* create a single empty document */
	
	wind_list_init(); 				/* create list window */
	
	DialogInit();					/* create message dialog object */
	
	NetworkInit();
	
} /*Initialize*/

/*
 *	TrapAvailable:
 *
 *	Check to see if a given trap is implemented. This is only used by the
 *	Initialize routine in this program, so we put it in the Initialize segment.
 *	The recommended approach to see if a trap is implemented is to see if
 *	the address of the trap routine is the same as the address of the
 *	Unimplemented trap. 
 */
#pragma segment Initialize
Boolean TrapAvailable(tNumber,tType)
	short		tNumber;
	TrapType	tType;
{
	/* Check and see if the trap exists. On 64K ROM machines, tType will be ignored. */

	return NGetTrapAddress(tNumber, tType) != GetTrapAddress(_Unimplemented);
} /*TrapAvailable*/


