/*	File:		InlineInputSample.c	Contains:	C source file for InlineInputSample	Copyright:	© 1989-1994 Apple Computer, Inc. All rights reserved.*//* Segmentation strategy:   There isn't any. Depending on which compiler you use, and whether you build for   Mac-OS 68K, A/UX, or PowerPC, segments may look different or not exist at all.   And with an application this small, segments don't really matter any more these   days. So we don't try to figure out segmentation and segment unloading strategies.   If you want to find out how to segment your application, look at some other   sample code.*//* SetPort strategy:   Toolbox routines do not change the current port. In spite of this, in this   program we use a strategy of calling SetPort whenever we want to draw or   make calls which depend on the current port. This makes us less vulnerable   to bugs in other software which might alter the current port (such as the   bug (feature?) in many desk accessories which change the port on OpenDeskAcc).   Hopefully, this also makes the routines from this program more self-contained,   since they don't depend on the current port setting.*//* Clipboard strategy:   Under styled TextEdit, TECut and TECopy will write both the text and associated   style information directly to the desk scrap as types 'TEXT' and 'styl'.   Instead of using TEToScrap and TEFromScrap, a new routine TEStylPaste, will    transfer the text and style from the desk scrap to the document.*/#if qInline#define qAppleEvents 1#endif#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 <OSEvents.h>#include <Packages.h>#include <Traps.h>#include <Printing.h>#include <DiskInit.h>#if qAppleEvents#include <Errors.h>#include <GestaltEqu.h>#include <AppleEvents.h>#if qInline#include <TextServices.h>#include <Script.h>#include <TSMTE.h>#endif // qInline#endif // qAppleEvents#include "InlineInputSample.h"// Constants// top left corner of the disk initialization dialogconst short kDITop = 80;const short kDILeft = 112;// the number of pixels we leave blank at the edge of the windowconst short kTextMargin = 2;// the maximum number of open documents at any one time. SetupMenus respects this// number, but the Apple event handlers don't.const short kMaxOpenDocuments = 4;	// arbitrary number used to specify the width of the TERec's destination// rectangle so that word wrap and horizontal scrolling can be demonstratedconst short kMaxDocWidth = 576;	// the minimum dimension of a window for GrowWindowconst short kMinDocDim = 64;// control contrlVis values to prevent or enable redrawing controls by// Control Manager routines such as SetCtlValueconst unsigned char kControlInvisible = 0;const unsigned char kControlVisible = 0xff;// for calculating scroll bar positions and sizesconst short kScrollbarWidth = 16;const short kScrollbarAdjust = 15; // should be kScrollbarWidth - 1, but C is too stupidconst short kScrollTweek = 2;	// ASCII code for delete characterconst unsigned char kDelChar = 8;	// pixels to scroll when the button part of the horizontal scrollbar is pressedconst short kButtonScroll = 4;// maximum text length we allow in a TERec; lower than 32767 to prevent errorsconst short kMaxTELength = 32000;// the SysEnvRec version we understandconst short kSysEnvironsVersion = 1;// events mask for no eventsconst short kNoEventsMask = 0;// the minimum heap size and minimal available heap space we require for running.// These are rough guesses; with styled TextEdit and printing you never really know.const Size kMinHeap = 50 * 1024;const Size kMinSpace = 40 * 1024;// values for setting up wide open rectangles and regionsconst short kExtremeNeg = -32768;const short kExtremePos = 32767 -1; // required to address an old region bug// extra security when pre-flighting edit commandsconst short kTESlop = 1024;// Types// A DocumentRecord contains the WindowRecord for one of our document windows,// as well as the TEHandle for the text we are editing. Other document fields// can be added to this record as needed. This is similar to how the// Window Manager and Dialog Manager add fields after the GrafPort.typedef struct {	WindowRecord	docWindow;	TEHandle		docTE;	ControlHandle	docVScroll;	ControlHandle	docHScroll;	TEClickLoopUPP	docClick;	Boolean			modified;#if qInline	TSMTERecHandle	docTSMTERecHandle;	TSMDocumentID	docTSMDoc;#endif} DocumentRecord, *DocumentPeek;// Global Variables// environment information that's set up during initializationBoolean gHasWaitNextEvent;		// WaitNextEvent trap is available#if qAppleEventsBoolean gHasAppleEvents;		// Apple events are available, so we expect to get events from the Finder#if qInlineBoolean gHasTextServices;		// Text Services Manager is available and should be usedBoolean gHasTSMTE;				// Text Services for Text Edit are available and should be used								// gHasTSMTE can only be set if gHasTextServices.#endif // qInline#endif // qAppleEvents// whether we are currently in the background. This accounts for major switches only.Boolean gInBackground;// the number of documents currently openshort gNumDocuments;// print record shared among all documents ??? probably should be attached to documents insteadTHPrint gPrinterRecord;// universal procedure pointersControlActionUPP gHActionUPP;ControlActionUPP gVActionUPP;#if powercTEClickLoopUPP gClickLoopUPP;#endif#if qAppleEventsAEEventHandlerUPP gHandleOAppUPP;AEEventHandlerUPP gHandleDocUPP;AEEventHandlerUPP gHandleQuitUPP;#if qInlineTSMTEPreUpdateUPP gTSMTEPreUpdateUPP;TSMTEPostUpdateUPP gTSMTEPostUpdateUPP;#endif // qInline#endif // qAppleEvents#if powerc// PowerPC libraries don't automatically define QuickDraw globalsQDGlobals qd;#endif#if qAppleEvents// to indicate that Quit command or Apple event was successfulBoolean gQuitting;#if qInline// variable to keep outside fontForce informationlong gSavedFontForce;#endif // qInline#endif // qAppleEvents// Routine Declarationsvoid AlertUser(short error);void EventLoop(void);void DoEvent(EventRecord *event);void AdjustCursor(Point mouse, RgnHandle region);void GetGlobalMouse(Point *mouse);void DoGrowWindow(WindowPtr window, EventRecord *event);void DoZoomWindow(WindowPtr window, short part);void ResizeWindow(WindowPtr window);void GetLocalUpdateRgn(WindowPtr window, RgnHandle localRgn);void DoUpdate(WindowPtr window);void DoActivate(WindowPtr window, Boolean becomingActive);void DoContentClick(WindowPtr window, EventRecord *event);void DoKeyDown(EventRecord *event);unsigned long GetSleep(void);void CommonAction(ControlHandle control, short *amount);pascal void VActionProc(ControlHandle control, short part);pascal void HActionProc(ControlHandle control, short part);void DoIdle(void);void DrawWindow(WindowPtr window);void AdjustMenus(void);void DoMenuCommand(long menuResult);void DoNew(void);Boolean DoCloseWindow(WindowPtr window);#if qAppleEventsstatic void PrepareToQuit(void);#else // qAppleEventsstatic void Terminate(void);#endif // qAppleEventsvoid Initialize(void);void BigBadError(short error);static void FailNilUPP(UniversalProcPtr theUPP);void GetTERect(WindowPtr window, Rect *teRect);void AdjustViewRect(TEHandle docTE);void AdjustTE(WindowPtr window);void AdjustHV(Boolean isVert, ControlHandle control, TEHandle docTE, Boolean canRedraw);void AdjustScrollValues(WindowPtr window, Boolean canRedraw);void AdjustScrollSizes(WindowPtr window);void AdjustScrollbars(WindowPtr window, Boolean needsResize);#if powercpascal Boolean ClickLoopProc(TEPtr pTE);#elseextern pascal void AsmClickLoopProc(void);#endifpascal void ClickLoopAddOn(void);pascal TEClickLoopUPP GetOldClickLoop(void);Boolean IsDocumentWindow(WindowPtr window);Boolean IsDAWindow(WindowPtr window);Boolean TrapAvailable(short theTrap);static void PrintText(TEHandle theText);#if qAppleEventsstatic void CheckAppleEvents(void);static OSErr InstallRequiredAppleEvents(void);static OSErr GotRequiredParameters(const AppleEvent *theAppleEvent);pascal OSErr HandleOAppEvent(const AppleEvent *theEvent, const AppleEvent *reply, long refCon);pascal OSErr HandleDocEvent(const AppleEvent *theEvent, const AppleEvent *reply, long refCon);pascal OSErr HandleQuitEvent(const AppleEvent *theEvent, const AppleEvent *reply, long refCon);#if qInlinestatic void CheckForTextServices(void);static pascal void MyTSMTEPreUpdateProc(TEHandle textH, long refCon);static pascal void MyTSMTEPostUpdateProc(TEHandle textH, long fixLen, long inputAreaStart,			long inputAreaEnd, long pinStart, long pinEnd, long refCon);static void ExitApplication(void);#endif // qInline#endif // qAppleEvents// Set up the whole world, including global variables, Toolbox managers.// If a problem occurs here, we alert the user and exit from the application.void Initialize(void){	EventRecord event;	short count;	SysEnvRec systemEnvironment;	long total, contig;	Handle menuBar;	gInBackground = false;#if qAppleEvents	gQuitting = false;#endif // qAppleEvents	InitGraf((Ptr) &qd.thePort);	InitFonts();	InitWindows();	InitMenus();	TEInit();	InitDialogs(nil);	InitCursor();	// the following loop is necessary to allow the default button of our	// alert to be outlined. We use EventAvail instead of GetNextEvent so we	// don't lose events.		for (count = 1; count <= 3; count++)		EventAvail(everyEvent, &event);		// collect environment information	// ignore the error returned from SysEnvirons; even if an error occurred,	// the SysEnvirons glue will fill in the SysEnvRec.	 	SysEnvirons(kSysEnvironsVersion, &systemEnvironment);		// make sure that the machine has at least 128K ROMs. If it doesn't, exit.		if (systemEnvironment.machineType < 0)		BigBadError(eOldROM);		// also, require at least system 6.0. The app should be able to run on system	// software 4.1 (where styled TextEdit was introduced) and later, but it's not really	// worth the trouble of testing on all those old systems anymore.		if (systemEnvironment.systemVersion < 0x600)		BigBadError(eOldSystemSoftware);		// 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 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) ApplicationZone() < kMinHeap)		BigBadError(eSmallSize);		// 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 < kMinSpace)		if (UnloadScrap() != noErr)			BigBadError(eNoMemory);		else		{			PurgeSpace(&total, &contig);			if (total < kMinSpace)				BigBadError(eNoMemory);		};	// 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.	// check for newer system services and set up our environment to make use of what's available.		gHasWaitNextEvent = TrapAvailable(_WaitNextEvent);#if qAppleEvents	CheckAppleEvents();	if (gHasAppleEvents)		(void) InstallRequiredAppleEvents();#if qInline	CheckForTextServices();		// this application uses TextEdit as the only text engine, and we don't support	// inline input without TSMTE. Therefore we call InitTSMAwareApplication only if	// TSMTE is available. A word processor that uses TextEdit only for dialogs	// and uses Text Services directly with the word processing engine would make this	// call depend on gHasTextServices.		if (!(gHasTSMTE && InitTSMAwareApplication() == noErr))	{		// if this happens, just move on without text services		gHasTextServices = false;		gHasTSMTE = false;	};		// get global fontForce flag, make sure it's off whenever we run		gSavedFontForce = GetScriptManagerVariable(smFontForce);	(void) SetScriptManagerVariable(smFontForce, 0);#endif // qInline#endif // qAppleEvents	// set up the menu bar and the menus that depend on the system environment		menuBar = GetNewMBar(rMenuBar);		if ( menuBar == nil )		BigBadError(eNoMemory);	SetMenuBar(menuBar);	DisposeHandle(menuBar);	AppendResMenu(GetMenuHandle(mApple), 'DRVR');	// build the Apple menu	AppendResMenu(GetMenuHandle(mFont), 'FONT');	// build the Font menu	DrawMenuBar();	// we have no document open yet		gNumDocuments = 0;	// set up printer stuff - this will allow the default page setup parameters to be used,	// so if the user decides to print without using the Page Setup command everything will	// be OK		gPrinterRecord = (THPrint) NewHandle(sizeof(TPrint));	if (gPrinterRecord != nil)	{		// if we got a print handle, initialize it to default values		PrOpen();		PrintDefault(gPrinterRecord);		PrClose();	};		// initialize the universal procedure pointers that we need#if qAppleEvents	// (Apple event handler UPPs are set up in InstallRequiredAppleEvents)#endif // qAppleEvents		gHActionUPP = NewControlActionProc(HActionProc);	FailNilUPP((UniversalProcPtr) gHActionUPP);	gVActionUPP = NewControlActionProc(VActionProc);	FailNilUPP((UniversalProcPtr) gVActionUPP);#if powerc	gClickLoopUPP = NewTEClickLoopProc(ClickLoopProc);	FailNilUPP((UniversalProcPtr) gClickLoopUPP);#endif#if qInline	if (gHasTSMTE)	{		gTSMTEPreUpdateUPP = NewTSMTEPreUpdateProc(MyTSMTEPreUpdateProc);		FailNilUPP((UniversalProcPtr) gTSMTEPreUpdateUPP);		gTSMTEPostUpdateUPP = NewTSMTEPostUpdateProc(MyTSMTEPostUpdateProc);		FailNilUPP((UniversalProcPtr) gTSMTEPostUpdateUPP);	};#endif // qInline}// report a fatal error to the user and exit from the applicationvoid BigBadError(short error){	AlertUser(error);#if qInline	ExitApplication();#else // qInline	ExitToShell();#endif // qInline}// check whether a valid UPP was allocatedstatic void FailNilUPP(UniversalProcPtr theUPP){	if (theUPP == nil)		BigBadError(eNoMemory);}#if qAppleEvents// check to see if a given bit in a long word is set.static Boolean BTst(long value, short bit){	long mask = 1L << bit;		return (value & mask) == mask;}#endif // qAppleEvents// check to see if a given trap is implemented. We follow IM VI-3-8.Boolean TrapAvailable(short theTrap){	TrapType theTrapType;	short numToolboxTraps;		if ((theTrap & 0x0800) > 0)		theTrapType = ToolTrap;	else		theTrapType = OSTrap;	if (theTrapType == ToolTrap)	{		theTrap = theTrap & 0x07ff;		if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xaa6e, ToolTrap))			numToolboxTraps = 0x0200;		else			numToolboxTraps = 0x0400;		if (theTrap >= numToolboxTraps)			theTrap = _Unimplemented;	};	return (NGetTrapAddress(theTrap, theTrapType) != NGetTrapAddress(_Unimplemented, ToolTrap));}#if qAppleEventsstatic void CheckAppleEvents(void){	long gestaltResponse;		gHasAppleEvents = false;		if (TrapAvailable(_Gestalt))	{		if (Gestalt(gestaltAppleEventsAttr, &gestaltResponse) == noErr)			gHasAppleEvents = BTst(gestaltResponse, gestaltAppleEventsPresent);	};}#if qInline// check whether the Text Services Manager and the extension for using Text Services with// TextEdit (TSMTE) are available, and sets gHasTextServices and gHasTSMTE accordingly.static void CheckForTextServices(void){	long gestaltResponse;		gHasTextServices = false;		// unless proven otherwise	gHasTSMTE = false;				// unless proven otherwise		if (TrapAvailable(_Gestalt))	{		if ((Gestalt(gestaltTSMgrVersion, &gestaltResponse) == noErr) && (gestaltResponse >= 1))		{			gHasTextServices = true;			if (Gestalt(gestaltTSMTEAttr, &gestaltResponse) == noErr)				gHasTSMTE = BTst(gestaltResponse, gestaltTSMTEPresent);		};	};}#endif // qInline#endif // qAppleEventsvoid main(void){	/*	If you have stack requirements that differ from the default,		then you could use SetApplLimit to increase StackSpace at 		this point, before calling MaxApplZone. */	MaxApplZone();					/* expand the heap */	Initialize();					/* initialize the program */#if qAppleEvents	if (!gHasAppleEvents)		DoNew();#else // qAppleEvents	DoNew();#endif // qAppleEvents	EventLoop();					/* call the main event loop */#if qAppleEvents#if qInline	ExitApplication();#else // qInline	ExitToShell();#endif // qInline#endif // qAppleEvents}#if qInline// IntlTSMEvent works around a bug in Kotoeri 1.0 (the Japanese input method),// which has been fixed in 1.1.1.static Boolean IntlTSMEvent(EventRecord *event){	short oldFont;	ScriptCode keyboardScript;		// make sure we have a port and it's not the Window Manager port	if (qd.thePort != nil && FrontWindow() != nil)	{		oldFont = qd.thePort->txFont;		keyboardScript = GetScriptManagerVariable(smKeyScript);		if (FontToScript(oldFont) != keyboardScript)			TextFont(GetScriptVariable(keyboardScript, smScriptAppFond));	};	return TSMEvent(event);}#endif // qInline/* Get events forever, and handle them by calling DoEvent.   Also call AdjustCursor each time through the loop. */void EventLoop(void){	RgnHandle	cursorRgn;	Boolean		gotEvent;	EventRecord	event;	Point		mouse;	cursorRgn = NewRgn();			/* weÕll pass WNE an empty region the 1st time thru */#if qAppleEvents	while (!gQuitting)#else // qAppleEvents	while (true) // loop forever, quit via ExitToShell#endif // qAppleEvents	{#if qInline		// set global fontForce flag so other apps don't get confused		(void) SetScriptManagerVariable(smFontForce, gSavedFontForce);		#endif // qInline		/* use WNE if it is available */		if ( gHasWaitNextEvent ) {			GetGlobalMouse(&mouse);			AdjustCursor(mouse, cursorRgn);			gotEvent = WaitNextEvent(everyEvent, &event, GetSleep(), cursorRgn);		}		else {			SystemTask();			gotEvent = GetNextEvent(everyEvent, &event);		};#if qInline		// clear fontForce again so it doesn't upset our operations		gSavedFontForce = GetScriptManagerVariable(smFontForce);		(void) SetScriptManagerVariable(smFontForce, 0);				// if we use inline input, we have to call IntlTSMEvent even with null events -		// some input methods rely on receiving them.		if (gHasTextServices && (gotEvent || event.what == nullEvent))			if (IntlTSMEvent(&event))				gotEvent = false;		#endif // qInline		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 */		/*	If you are using modeless dialogs that have editText items,			you will want to call IsDialogEvent to give the caret a chance			to blink, even if WNE/GNE returned FALSE. However, check FrontWindow			for a non-NIL value before calling IsDialogEvent. */	};}/* Do the right thing for an event. Determine what kind of event it is, and call the appropriate routines. */void DoEvent(EventRecord *event){	short		part, err;	WindowPtr	window;	char		key;	Point		aPoint;#if qInline	long		menuResult;#endif // qInline	switch ( event->what ) {		case nullEvent:			/* we idle for null/mouse moved events ands for events which arenÕt				ours (see EventLoop) */			DoIdle();			break;		case mouseDown:			part = FindWindow(event->where, &window);			switch ( part ) {				case inMenuBar:             /* process a mouse menu command (if any) */					AdjustMenus();#if qInline					menuResult = MenuSelect(event->where);					if (!(gHasTextServices && TSMMenuSelect(menuResult)))						DoMenuCommand(menuResult);					HiliteMenu(0); // needed even if TSM or Script Manager handle the menu#else // qInline					DoMenuCommand(MenuSelect(event->where));#endif // qInline					break;				case inSysWindow:           /* let the system handle the mouseDown */					SystemClick(event, window);					break;				case inContent:					if ( window != FrontWindow() ) {						SelectWindow(window);						AdjustMenus();					} else						DoContentClick(window, event);					break;				case inDrag:                /* pass screenBits.bounds to get all gDevices */					DragWindow(window, event->where, &qd.screenBits.bounds);					break;				case inGoAway:					if ( TrackGoAway(window, event->where) )						DoCloseWindow(window); /* we donÕt care if the user cancelled */					break;				case inGrow:					DoGrowWindow(window, event);					break;				case inZoomIn:				case inZoomOut:				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 ) {	/* Command key down */				if ( event->what == keyDown ) {					AdjustMenus();			/* enable/disable/check menu items properly */					DoMenuCommand(MenuKey(key));				}			} else				DoKeyDown(event);			break;		case activateEvt:			DoActivate((WindowPtr) event->message, (event->modifiers & activeFlag) != 0);			break;		case updateEvt:			DoUpdate((WindowPtr) event->message);			break;		/*	1.01 - It is not a bad idea to at least call DIBadMount in response			to a diskEvt, so that the user can format a floppy. */		case diskEvt:			if ( HiWord(event->message) != noErr ) {				SetPt(&aPoint, kDILeft, kDITop);				err = DIBadMount(aPoint, event->message);			}			break;		case osEvt:		/*	1.02 - must BitAND with 0x0FF to get only low byte */			switch ((event->message >> 24) & 0x0FF) {		/* high byte of message */				case mouseMovedMessage:					DoIdle();					/* mouse-moved is also an idle event */					break;				case suspendResumeMessage:		/* suspend/resume is also an activate/deactivate */					gInBackground = (event->message & resumeFlag) == 0;					DoActivate(FrontWindow(), !gInBackground);					break;			}			break;#if qAppleEvents		case kHighLevelEvent:			if (AEProcessAppleEvent(event) != noErr)				; // any ideas for error handling?			break;#endif // qAppleEvents	}} /*DoEvent*//*	Change the cursor's shape, depending on its position. This also calculates the region	where the current cursor resides (for WaitNextEvent). When the mouse moves outside of	this region, an event is generated. If there is more to the event than just	Òthe mouse movedÓ, we get called before the event is processed to make sure	the cursor is the right one. In any (ahem) event, this is called again before we	fall back into WNE. */void AdjustCursor(Point mouse, RgnHandle region){	WindowPtr	window;	RgnHandle	arrowRgn;	RgnHandle	iBeamRgn;	Rect		iBeamRect;	window = FrontWindow();	/* we only adjust the cursor when we are in front */	if ( (! gInBackground) && (! IsDAWindow(window)) ) {		/* calculate regions for different cursor shapes */		arrowRgn = NewRgn();		iBeamRgn = NewRgn();		/* start arrowRgn wide open */		SetRectRgn(arrowRgn, kExtremeNeg, kExtremeNeg, kExtremePos, kExtremePos);		/* calculate iBeamRgn */		if ( IsDocumentWindow(window) ) {			iBeamRect = (*((DocumentPeek) window)->docTE)->viewRect;			SetPort(window);	/* make a global version of the viewRect */			// ??? following two lines depend on Rect structure layout			LocalToGlobal((Point *) &(iBeamRect).top);			LocalToGlobal((Point *) &(iBeamRect).bottom);			RectRgn(iBeamRgn, &iBeamRect);			/* we temporarily change the portÕs origin to ÒglobalfyÓ the visRgn */			SetOrigin(-window->portBits.bounds.left, -window->portBits.bounds.top);			SectRgn(iBeamRgn, window->visRgn, iBeamRgn);			SetOrigin(0, 0);		}		/* subtract other regions from arrowRgn */		DiffRgn(arrowRgn, iBeamRgn, arrowRgn);#if qInline		// before we commit to anything, let's check whether some text service has a		// different idea				if (!(gHasTextServices && SetTSMCursor(mouse)))		{			// change the cursor and the region parameter			if (PtInRgn(mouse, iBeamRgn))			{				SetCursor(*GetCursor(iBeamCursor));				CopyRgn(iBeamRgn, region);			}			else			{				SetCursor(&qd.arrow);				CopyRgn(arrowRgn, region);			};		};		// and no matter how nice the region, with text services it cannot be bigger than		// a point. Yes, this defeats the purpose of all the calculations...				if (gHasTextServices)			SetRectRgn(region, mouse.h, mouse.v, mouse.h, mouse.v);#else // qInline		// change the cursor and the region parameter		if (PtInRgn(mouse, iBeamRgn))		{			SetCursor(*GetCursor(iBeamCursor));			CopyRgn(iBeamRgn, region);		}		else		{			SetCursor(&qd.arrow);			CopyRgn(arrowRgn, region);		};#endif // qInline		DisposeRgn(arrowRgn);		DisposeRgn(iBeamRgn);	}} /*AdjustCursor*//*	Get the global coordinates of the mouse. When you call OSEventAvail	it will return either a pending event or a null event. In either case,	the where field of the event record will contain the current position	of the mouse in global coordinates and the modifiers field will reflect	the current state of the modifiers. Another way to get the global	coordinates is to call GetMouse and LocalToGlobal, but that requires	being sure that thePort is set to a valid port. */void GetGlobalMouse(Point *mouse){	EventRecord	event;		OSEventAvail(kNoEventsMask, &event);	/* we aren't interested in any events */	*mouse = event.where;				/* just the mouse position */} /*GetGlobalMouse*//*	Called when a mouseDown occurs in the grow box of an active window. In	order to eliminate any 'flicker', we want to invalidate only what is	necessary. Since ResizeWindow invalidates the whole portRect, we save	the old TE viewRect, intersect it with the new TE viewRect, and	remove the result from the update region. However, we must make sure	that any old update region that might have been around gets put back. */void DoGrowWindow(WindowPtr window, EventRecord *event){	long		growResult;	Rect		tempRect;	RgnHandle	tempRgn;	DocumentPeek doc;		tempRect = qd.screenBits.bounds;					/* set up limiting values */	tempRect.left = kMinDocDim;	tempRect.top = kMinDocDim;	growResult = GrowWindow(window, event->where, &tempRect);	/* see if it really changed size */	if ( growResult != 0 ) {		doc = (DocumentPeek) window;		tempRect = (*doc->docTE)->viewRect;				/* save old text box */		tempRgn = NewRgn();		GetLocalUpdateRgn(window, tempRgn);				/* get localized update region */		SizeWindow(window, LoWord(growResult), HiWord(growResult), true);		ResizeWindow(window);		/* calculate & validate the region that hasnÕt changed so it wonÕt get redrawn */		SectRect(&tempRect, &(*doc->docTE)->viewRect, &tempRect);		ValidRect(&tempRect);							/* take it out of update */		InvalRgn(tempRgn);								/* put back any prior update */		DisposeRgn(tempRgn);	}} /* DoGrowWindow *//* 	Called when a mouseClick occurs in the zoom box of an active window.	Everything has to get re-drawn here, so we don't mind that	ResizeWindow invalidates the whole portRect. */void DoZoomWindow(WindowPtr window, short part){	EraseRect(&window->portRect);	ZoomWindow(window, part, window == FrontWindow());	ResizeWindow(window);} /*  DoZoomWindow *//* Called when the window has been resized to fix up the controls and content. */void ResizeWindow(WindowPtr window){	AdjustScrollbars(window, true);	AdjustTE(window);	InvalRect(&window->portRect);} /* ResizeWindow *//* Returns the update region in local coordinates */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 *//*	This is called when an update event is received for a window.	It calls DrawWindow to draw the contents of an application window.	As an efficiency measure that does not have to be followed, it	calls the drawing routine only if the visRgn is non-empty. This	will handle situations where calculations for drawing or drawing	itself is very time-consuming. */void DoUpdate(WindowPtr window){	if ( IsDocumentWindow(window) ) {		BeginUpdate(window);				/* this sets up the visRgn */		if ( ! EmptyRgn(window->visRgn) )	/* draw if updating needs to be done */			DrawWindow(window);		EndUpdate(window);	}} /*DoUpdate*//*	This is called when a window is activated or deactivated.	It calls TextEdit to deal with the selection. */void DoActivate(WindowPtr window, Boolean becomingActive){	RgnHandle	tempRgn, clipRgn;	Rect		growRect;	DocumentPeek doc;		if ( IsDocumentWindow(window) ) {		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. */			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;			InvalRect(&growRect);#if qInline			if (doc->docTSMDoc != nil)				(void) ActivateTSMDocument(doc->docTSMDoc);#endif // qInline		}		else		{		#if qInline			if (doc->docTSMDoc != nil)				(void) DeactivateTSMDocument(doc->docTSMDoc);#endif // qInline			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);		}	}} /*DoActivate*//*	This is called when a mouseDown occurs in the content of a window. */void DoContentClick(WindowPtr window, EventRecord *event){	Point		mouse;	ControlHandle control;	short		part, value;	Boolean		shiftDown;	DocumentPeek doc;	Rect		teRect;	if ( IsDocumentWindow(window) ) {		SetPort(window);		mouse = event->where;							/* get the click position */		GlobalToLocal(&mouse);		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 = GetControlValue(control);					part = TrackControl(control, mouse, nil);					if ( part != 0 ) {						value -= GetControlValue(control);						/* value now has CHANGE in value; if value changed, scroll */						if ( value != 0 )							if ( control == doc->docVScroll )								TEScroll(0, value, 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, gVActionUPP);					else						value = TrackControl(control, mouse, gHActionUPP);					break;			}		}	}} /*DoContentClick*//* 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. */void DoKeyDown(EventRecord *event){	enum { minArrowKey = 28, maxArrowKey = 31 };	WindowPtr	window;	char		key;	TEHandle	te;	window = FrontWindow();	if ( IsDocumentWindow(window) ) {		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 )		{			TEKey(key, te);			if ((key < minArrowKey) || (key > maxArrowKey)) // not a cursor key				((DocumentPeek) window)->modified = true;			AdjustScrollbars(window, false);		}		else			AlertUser(eExceedChar);	}} /*DoKeyDown*//*	Calculate a sleep value for WaitNextEvent. This takes into account the things	that DoIdle does with idle time. */unsigned long GetSleep(void){	long		sleep;	WindowPtr	window;	TEHandle	te;	sleep = LONG_MAX;						/* default value for sleep */	if ( !gInBackground ) {		window = FrontWindow();			/* and the front window is ours... */		if ( IsDocumentWindow(window) ) {			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*//*	Common algorithm for pinning the value of a control. It returns the actual amount	the value of the control changed. Note the pinning is done for the sake of returning	the amount the control value changed. */void CommonAction(ControlHandle control, short *amount){	short		value, max;		value = GetControlValue(control);	/* get current value */	max = GetControlMaximum(control);		/* and maximum value */	*amount = value - *amount;	if ( *amount < 0 )		*amount = 0;	else if ( *amount > max )		*amount = max;	SetControlValue(control, *amount);	*amount = value - *amount;		/* calculate the real change */} /* CommonAction *//* Determines how much to change the value of the vertical scrollbar by and how	much to scroll the TE record. */pascal void VActionProc(ControlHandle control, short part){	short		amount;	WindowPtr	window;	TEPtr		te;		if ( part != 0 ) {				/* if it was actually in the control */		window = (*control)->contrlOwner;		te = *((DocumentPeek) window)->docTE;		switch ( part ) {			case inUpButton:			case inDownButton:				amount = 24;				break;			case inPageUp:			/* one page */			case inPageDown:				amount = te->viewRect.bottom - te->viewRect.top;				break;		}		if ( (part == inDownButton) || (part == inPageDown) )			amount = -amount;		/* reverse direction for a downer */		CommonAction(control, &amount);		if ( amount != 0 )			TEScroll(0, amount, ((DocumentPeek) window)->docTE);	}} /* VActionProc *//* Determines how much to change the value of the horizontal scrollbar by and howmuch to scroll the TE record. */pascal void HActionProc(ControlHandle control, short part){	short		amount;	WindowPtr	window;	TEPtr		te;		if ( part != 0 ) {		window = (*control)->contrlOwner;		te = *((DocumentPeek) window)->docTE;		switch ( part ) {			case inUpButton:			case inDownButton:		/* a few pixels */				amount = kButtonScroll;				break;			case inPageUp:			/* a page */			case inPageDown:				amount = te->viewRect.right - te->viewRect.left;				break;		}		if ( (part == inDownButton) || (part == inPageDown) )			amount = -amount;		/* reverse direction */		CommonAction(control, &amount);		if ( amount != 0 )			TEScroll(amount, 0, ((DocumentPeek) window)->docTE);	}} /* VActionProc *//* This is called whenever we get a null event et al. It takes care of necessary periodic actions. For this program, it calls TEIdle. */void DoIdle(void){	WindowPtr	window;	window = FrontWindow();	if ( IsDocumentWindow(window) )		TEIdle(((DocumentPeek) window)->docTE);} /*DoIdle*//* Draw the contents of an application window. */void DrawWindow(WindowPtr window){	SetPort(window);	EraseRect(&window->portRect);	DrawControls(window);	DrawGrowIcon(window);	TEUpdate(&window->portRect, ((DocumentPeek) window)->docTE);} /*DrawWindow*/// menu handling utilitiesstatic void DisableMenu(MenuHandle menu){	DisableItem(menu, 0);}static void EnableMenu(MenuHandle menu){	EnableItem(menu, 0);}static void RemoveMenuCheckMarks(MenuHandle menu){	short item;	for (item = 1; item <= CountMItems(menu); item++)	{		CheckItem(menu, item, false);	};}static void RemoveMenuStyles(MenuHandle menu){	short item;	for (item = 1; item <= CountMItems(menu); item++)	{		SetItemStyle(menu, item, normal);	};}static void EnableItemIf(MenuHandle menu, short item, Boolean condition){	if (condition)		EnableItem(menu, item);	else		DisableItem(menu, item);}// enable and disable menus based on the current state.// In general, we set up the menu items only before calling MenuSelect or MenuKey,// since these are the only times that a menu item can be selected. However, we// also have to set up the menus whenever the front window changes because// we enable and disable some menu titles depending on the front window.void AdjustMenus(void){	WindowPtr	window;	Boolean		frontIsDAWindow;	Boolean		frontIsDocWindow;	Boolean		docHasSelection;	Boolean		clipboardHasText;	Boolean		haveContinuousFont;	MenuHandle	menu;	long		offset;	TEHandle	te;	TextStyle	theTextStyle;	short		theFont;	short		mode;	short		item;		// gather some state information that we'll need to decide which menu items to enable	window = FrontWindow();	frontIsDAWindow = (window != nil) && IsDAWindow(window);	frontIsDocWindow = (window != nil) && IsDocumentWindow(window);	if (frontIsDocWindow)	{		te = ((DocumentPeek) window)->docTE;		docHasSelection = (*te)->selStart < (*te)->selEnd;	}	else		docHasSelection = false;	clipboardHasText = GetScrap(nil, 'TEXT', &offset)  > 0;		// note that TEGetScrapLength works for the private TextEdit scrap only, which		// is not used by styled TextEdit	haveContinuousFont = false; // will really be set when setting up font menu		// Apple menu is always enabled		// File menu	menu = GetMenuHandle(mFile);	EnableItemIf(menu, iNew, gNumDocuments < kMaxOpenDocuments);	EnableItemIf(menu, iClose, window != nil);	EnableItemIf(menu, iPageSetup, frontIsDocWindow);	EnableItemIf(menu, iPrint, frontIsDocWindow);	EnableItem(menu, iQuit);	menu = GetMenuHandle(mEdit);	EnableItemIf(menu, iUndo, frontIsDAWindow); // can't handle Undo for documents yet	EnableItemIf(menu, iCut, frontIsDAWindow || docHasSelection);	EnableItemIf(menu, iCopy, frontIsDAWindow || docHasSelection);	EnableItemIf(menu, iPaste, frontIsDAWindow || (frontIsDocWindow && clipboardHasText));	EnableItemIf(menu, iClear, frontIsDAWindow || docHasSelection);	EnableItemIf(menu, iSelectAll, frontIsDocWindow);		menu = GetMenuHandle(mFont);	RemoveMenuCheckMarks(menu);	if (frontIsDocWindow)	{		EnableMenu(menu);		mode = doFont;		if (TEContinuousStyle(&mode, &theTextStyle, ((DocumentPeek) window)->docTE))		{			Str255 theName, itemName;			short itemCount;						GetFontName(theTextStyle.tsFont, theName);			itemCount = CountMItems(menu);			for (item = 1; item <= itemCount; item++)			{				GetMenuItemText(menu, item, itemName);				if (EqualString(theName, itemName, true, true))				{					CheckItem(menu, item, true);					break;				};			};						haveContinuousFont = true;			theFont = theTextStyle.tsFont;		};	}	else	{		DisableMenu(menu);	};		menu = GetMenuHandle (mFontSize);	RemoveMenuCheckMarks(menu);	RemoveMenuStyles(menu);	if (frontIsDocWindow)	{		EnableMenu(menu);		mode = doSize;		if (TEContinuousStyle(&mode, &theTextStyle, ((DocumentPeek) window)->docTE))		{					switch (theTextStyle.tsSize)			{				case 9: item = iNine; break;				case 10: item = iTen; break;				case 12: item = iTwelve; break;				case 14: item = iFourteen; break;				case 18: item = iEighteen; break;				case 24: item = iTwentyFour; break;			};			CheckItem(menu, item, true);		};		if (haveContinuousFont)		{			if (RealFont(theFont, 9))				SetItemStyle(menu, iNine, outline);			if (RealFont(theFont, 10))				SetItemStyle(menu, iTen, outline);			if (RealFont(theFont, 12))				SetItemStyle(menu, iTwelve, outline);			if (RealFont(theFont, 14))				SetItemStyle(menu, iFourteen, outline);			if (RealFont(theFont, 18))				SetItemStyle(menu, iEighteen, outline);			if (RealFont(theFont, 24))				SetItemStyle(menu, iTwentyFour, outline);		};	}	else	{		DisableMenu(menu);	};		menu = GetMenuHandle (mStyle);	RemoveMenuCheckMarks(menu);	if (frontIsDocWindow)	{		EnableMenu(menu);		mode = doFace;		if (TEContinuousStyle(&mode, &theTextStyle, ((DocumentPeek) window)->docTE))		{			CheckItem(menu, iPlain, theTextStyle.tsFace == normal);			CheckItem(menu, iBold, (theTextStyle.tsFace & bold) == bold);			CheckItem(menu, iItalic, (theTextStyle.tsFace & italic) == italic);			CheckItem(menu, iUnderline, (theTextStyle.tsFace & underline) == underline);			CheckItem(menu, iOutline, (theTextStyle.tsFace & outline) == outline);			CheckItem(menu, iShadow, (theTextStyle.tsFace & shadow) == shadow);		};			}	else	{		DisableMenu(menu);	};		DrawMenuBar();}/*	This is called when an item is chosen from the menu bar (after calling	MenuSelect or MenuKey). It does the right thing for each command. */void DoMenuCommand(long menuResult){	short		menuID, menuItem;	short		itemHit, daRefNum;	Str255		daName;	OSErr		saveErr;	TEHandle	te;	WindowPtr	window;	long		scrapLength;	long		offset;	Handle		aHandle;	long		oldSize, newSize;	long		total, contig;	TextStyle	theTextStyle;	Str255		theFontName;	short		theFontID;	short		theFontSize;	DocumentPeek theDocument;#if qInline	TSMDocumentID tsmDoc;#endif // qInline	window = FrontWindow();	menuID = HiWord(menuResult);	menuItem = LoWord(menuResult);#if qInline	if (menuID == 0)		// no real menu command, so we don't want to confirm inline input text		return;	#endif // qInline	if (IsDocumentWindow(window))	{		theDocument = (DocumentPeek) window;		te = theDocument->docTE;#if qInline		// for any real menu command, we first confirm inline input text if there is any.		// This is somewhat controversial - it can be argued that for some commands, e.g.		// opening a new document, it's not necessary to confirm the text. A user might do		// some editing in the new document, then come back and continue with the inline		// input. Then again, this might conflict with the Undo model you use. Test with		// real users, then use your best judgment.		tsmDoc = theDocument->docTSMDoc;		if (tsmDoc != nil)			(void) FixTSMDocument(tsmDoc);#endif // qInline	};		switch ( menuID ) {		case mApple:			switch ( menuItem ) {				case iAbout:		/* bring up alert for About */					itemHit = Alert(rAboutAlert, nil);					break;				default:			/* all non-About items in this menu are DAs et al */					/* type Str255 is an array in MPW 3 */					GetMenuItemText(GetMenuHandle(mApple), menuItem, daName);					daRefNum = OpenDeskAcc(daName);					AdjustMenus();					break;			}			break;		case mFile:			switch ( menuItem ) {				case iNew:					DoNew();					break;				case iClose:					(void) DoCloseWindow(FrontWindow());			/* ignore the result */					break;				case iPageSetup:					PrOpen();					if (PrError() == noErr)						(void) PrStlDialog(gPrinterRecord);					PrClose();					break;				case iPrint:					PrintText(te);					break;				case iQuit:#if qAppleEvents					PrepareToQuit();#else // qAppleEvents					Terminate();#endif // qAppleEvents					break;			}			break;		case mEdit:					/* call SystemEdit for DA editing & MultiFinder */			if ( !SystemEdit(menuItem-1) ) {				switch ( menuItem ) {					case iCut:						if ( ZeroScrap() == noErr )						{							PurgeSpace(&total, &contig);							if ((*te)->selEnd - (*te)->selStart + kTESlop > contig)								AlertUser(eNoSpaceCut);							else							{								TECut(te);								theDocument->modified = true;							};						};						break;					case iCopy:						if ( ZeroScrap() == noErr )							TECopy(te);						break;					case iPaste:						scrapLength = GetScrap(nil, 'TEXT', &offset);						if ( scrapLength + ((*te)->teLength -								((*te)->selEnd - (*te)->selStart)) > kMaxTELength )							AlertUser(eExceedPaste);						else						{							aHandle = (Handle) TEGetText(te);							oldSize = GetHandleSize(aHandle);							newSize = oldSize + scrapLength + kTESlop;							SetHandleSize(aHandle, newSize);							saveErr = MemError();							SetHandleSize(aHandle, oldSize);							if (saveErr != noErr)								AlertUser(eNoSpacePaste);							else							{								TEStylePaste(te);								theDocument->modified = true;							};						};						break;					case iClear:						TEDelete(te);						theDocument->modified = true;						break;					case iSelectAll:						TESetSelect(0, (*te)->teLength, te);						break;				};			if (menuItem != iCopy)				AdjustScrollbars(window, false);			}			break;		case mFont:			GetMenuItemText(GetMenuHandle(mFont), menuItem, theFontName);			GetFNum(theFontName, &theFontID);			theTextStyle.tsFont = theFontID;			TESetStyle(doFont, &theTextStyle, true, te);			if ((*te)->selEnd - (*te)->selStart > 0)				theDocument->modified = true;			AdjustScrollbars(window, false);			break;		case mFontSize:			switch (menuItem)			{				case iNine:					theFontSize = 9;					break;				case iTen:					theFontSize = 10;					break;				case iTwelve:					theFontSize = 12;					break;				case iFourteen:					theFontSize = 14;					break;				case iEighteen:					theFontSize = 18;					break;				case iTwentyFour:					theFontSize = 24;					break;			};			theTextStyle.tsSize = theFontSize;			TESetStyle(doSize, &theTextStyle, true, te);			if ((*te)->selEnd - (*te)->selStart > 0)				theDocument->modified = true;			AdjustScrollbars(window, false);			break;		case mStyle:			switch (menuItem)			{				case iPlain:					*((short *) &theTextStyle.tsFace) = 0; // see technote TE 16					theTextStyle.tsFace = normal;					break;				case iBold:					theTextStyle.tsFace = bold;					break;				case iItalic:					theTextStyle.tsFace = italic;					break;				case iUnderline:					theTextStyle.tsFace = underline;					break;				case iOutline:					theTextStyle.tsFace = outline;					break;				case iShadow:					theTextStyle.tsFace = shadow;					break;			};			if (menuItem == iPlain)				TESetStyle(doFace, &theTextStyle, true, te); // doToggle doesn't work for plain			else				TESetStyle(doFace + doToggle, &theTextStyle, true, te);			if ((*te)->selEnd - (*te)->selStart > 0)				theDocument->modified = true;			AdjustScrollbars(window, false);			break;	};	HiliteMenu(0);					/* unhighlight what MenuSelect (or MenuKey) hilited */}/* Create a new document and window. */void DoNew(void){	Boolean		good;	Ptr			storage;	WindowPtr	window;	Rect		destRect, viewRect;	DocumentPeek doc;#if qInline	OSType		supportedInterfaces[1];#endif // qInline	storage = NewPtr(sizeof(DocumentRecord));	if ( storage != nil ) {		doc = (DocumentPeek) storage;		doc->modified = false;#if qInline		doc->docTSMTERecHandle = nil;		doc->docTSMDoc = nil;#endif // qInline		window = GetNewWindow(rDocWindow, storage, (WindowPtr) -1);		if ( window != nil ) {			gNumDocuments += 1;			/* this will be decremented when we call DoCloseWindow */			good = false;			SetPort(window);						// on a Roman system, the default text size is 0; we need a real size to make the Size menu work			TextSize(GetDefFontSize());			GetTERect(window, &viewRect);			destRect = viewRect;			destRect.right = destRect.left + kMaxDocWidth;			doc->docTE = TEStyleNew(&destRect, &viewRect);			good = doc->docTE != nil;	/* if TENew succeeded, we have a good document */			if ( good ) {				/* 1.02 - good document? Ñ proceed */				TEAutoView(true, doc->docTE);				doc->docClick = (*doc->docTE)->clickLoop;#if powerc				TESetClickLoop(gClickLoopUPP, doc->docTE);#else				(*doc->docTE)->clickLoop = (TEClickLoopUPP) AsmClickLoopProc;#endif			}						if ( good ) {				/* good document? Ñ get scrollbars */				doc->docVScroll = GetNewControl(rVScroll, window);				good = (doc->docVScroll != nil);			}			if ( good) {				doc->docHScroll = GetNewControl(rHScroll, window);				good = (doc->docHScroll != nil);			}#if qInline			if (good && gHasTSMTE)			{				supportedInterfaces[0] = kTSMTEInterfaceType;				if (NewTSMDocument(1, supportedInterfaces, &doc->docTSMDoc,							(long) &doc->docTSMTERecHandle) == noErr)				{					TSMTERecPtr tsmteRecPtr = *(doc->docTSMTERecHandle);										tsmteRecPtr->textH = doc->docTE;					tsmteRecPtr->preUpdateProc = gTSMTEPreUpdateUPP;					tsmteRecPtr->postUpdateProc = gTSMTEPostUpdateUPP;					tsmteRecPtr->updateFlag = kTSMTEAutoScroll;					tsmteRecPtr->refCon = (long) window;				}				else					good = false;			};#endif // qInline			if ( good ) {				/* good? Ñ adjust & draw the controls, draw the window */				/* false to AdjustScrollValues means musnÕt redraw; technically, of course,				the window is hidden so it wouldnÕt matter whether we called ShowControl or not. */				AdjustScrollValues(window, false);				ShowWindow(window);				AdjustMenus();			} else {				DoCloseWindow(window);	/* otherwise regret we ever created it... */				AlertUser(eNoWindow);			/* and tell user */			}		} else			DisposePtr(storage);			/* get rid of the storage if it is never used */	}} /*DoNew*//* Close a window. This handles desk accessory and application windows. *//*	1.01 - At this point, if there was a document associated with a	window, you could do any document saving processing if it is 'dirty'.	DoCloseWindow would return true if the window actually closed, i.e.,	the user didnÕt cancel from a save dialog. This result is handy when	the user quits an application, but then cancels the save of a document	associated with a window. */Boolean DoCloseWindow(WindowPtr window){	DocumentPeek theDocument;	if ( IsDAWindow(window) )		CloseDeskAcc(((WindowPeek) window)->windowKind);	else if ( IsDocumentWindow(window) ) {		// ??? check modified flag whether document needs saving		theDocument = (DocumentPeek) window;#if qInline		if (theDocument->docTSMDoc != nil)		{			(void) FixTSMDocument(theDocument->docTSMDoc);			// DeleteTSMDocument might cause crash if we don't deactivate first, so...			(void) DeactivateTSMDocument(theDocument->docTSMDoc);			(void) DeleteTSMDocument(theDocument->docTSMDoc);		};#endif // qInline		if (theDocument->docTE != nil)			TEDispose(theDocument->docTE);		/*	1.01 - We used to call DisposeWindow, but that was technically			incorrect, even though we allocated storage for the window on			the heap. We should instead call CloseWindow to have the structures			taken care of and then dispose of the storage ourselves. */		CloseWindow(window);		DisposePtr((Ptr) window);		gNumDocuments -= 1;	}	AdjustMenus();	return true;} /*DoCloseWindow*/// PrintText prints the text in the edit record. It opens a printer port, calculates// the number of lines per page (which may be different for each page depending on the// text styles) and then calls TEUpdate for the page, scrolls a page and calls TEUpdate,// etc.static void PrintText(TEHandle theText){	const short kMargin = 20; // page margins in pixels	const Rect zeroRect = { 0, 0, 0, 0 };	short totalLines;	GrafPtr oldPort;	Rect oldViewRect;	Rect oldDestRect;	Rect viewRect;	Rect updateRect;	Rect clipRect;	short totalHeight;	short currentLine;	short scrollAmount;	TPrStatus thePrinterStatus;	Boolean printManagerIsOpen = false;	Boolean userHasCancelled = false;	short viewHeight;	TPPrPort thePrinterPort;		if (gPrinterRecord != nil)	{		PrOpen();		if (PrJobDialog(gPrinterRecord))		{			GetPort(&oldPort);			oldViewRect = (*theText)->viewRect;			oldDestRect = (*theText)->destRect;			thePrinterPort = PrOpenDoc(gPrinterRecord, nil, nil);			printManagerIsOpen = (PrError() == noErr);		};	};		if (printManagerIsOpen)	{		SetPort((GrafPtr) thePrinterPort);				// re-wrap the text to fill the entire page minus margins		viewRect = (*gPrinterRecord)->prInfo.rPage;		InsetRect(&viewRect, kMargin, kMargin);		(*theText)->inPort = (GrafPtr) thePrinterPort;		(*theText)->destRect = viewRect;		(*theText)->viewRect = viewRect;		TECalText(theText);		totalLines = (*theText)->nLines;		totalHeight = TEGetHeight(totalLines, 0, theText);		(*theText)->destRect.bottom = (*theText)->destRect.top + totalHeight;				currentLine = 1;				while ((!userHasCancelled) && (currentLine <= totalLines))		{			PrOpenPage(thePrinterPort, nil);			scrollAmount = 0;			clipRect = (*gPrinterRecord)->prInfo.rPage;			ClipRect(&clipRect);						viewHeight = (*theText)->viewRect.bottom - (*theText)->viewRect.top + 1;						while (((scrollAmount + TEGetHeight(currentLine, currentLine, theText)) <= viewHeight)						&& (currentLine <= totalLines))			{				scrollAmount += TEGetHeight(currentLine, currentLine, theText);				currentLine++;			};						(*theText)->viewRect.bottom = scrollAmount + kMargin;			TEDeactivate(theText); // avoid printing selections			updateRect = (*theText)->viewRect;			TEUpdate(&updateRect, theText);			ClipRect(&zeroRect); // prevent TEScroll from redrawing the text			TEScroll(0, -scrollAmount, theText); // scroll so we can print the next page			(*theText)->viewRect.bottom = viewRect.bottom; // reset to full page;						if (PrError() == iPrAbort)				userHasCancelled = true;			PrClosePage(thePrinterPort);		};				PrCloseDoc(thePrinterPort);		if ((*gPrinterRecord)->prJob.bJDocLoop == bSpoolLoop && PrError() == noErr)			PrPicFile(gPrinterRecord, nil, nil, nil, &thePrinterStatus);		PrClose();				SetPort(oldPort);		(*theText)->inPort = oldPort;		(*theText)->viewRect = oldViewRect;		(*theText)->destRect = oldDestRect;		TECalText(theText);		updateRect = (*theText)->viewRect;		TEUpdate(&updateRect, theText);	};}#if qAppleEvents// handle the Quit menu command or Apple event by closing all windows, and// setting gQuitting if successful.static void PrepareToQuit(void){	WindowPtr aWindow;		gQuitting = true;	aWindow = FrontWindow();	while (gQuitting && (aWindow != nil))	{		gQuitting = DoCloseWindow(aWindow);		aWindow = FrontWindow();	};}#if qInlinestatic void ExitApplication(void){	if (gHasTextServices)		(void) CloseTSMAwareApplication();	// set global fontForce flag so other apps don't get confused	(void) SetScriptManagerVariable(smFontForce, gSavedFontForce);	ExitToShell();}#endif // qInline#else // qAppleEvents// handle the Quit menu command by closing all windows, and quitting if successful.static void Terminate(void){	WindowPtr aWindow;	Boolean closed;		closed = true;	aWindow = FrontWindow();	while (closed && (aWindow != nil))	{		closed = DoCloseWindow(aWindow);		aWindow = FrontWindow();	};	if (closed)		ExitToShell(); // exit if no cancellation}#endif // qAppleEvents/* Return a rectangle that is inset from the portRect by the size of	the scrollbars and a little extra margin. */void GetTERect(WindowPtr window, Rect *teRect){	*teRect = window->portRect;	InsetRect(teRect, kTextMargin, kTextMargin);	/* adjust for margin */	teRect->bottom = teRect->bottom - 15;		/* and for the scrollbars */	teRect->right = teRect->right - 15;} /*GetTERect*//* Update the TERec's view rect so that it is the greatest multiple of	the lineHeight that still fits in the old viewRect. */void AdjustViewRect(TEHandle docTE){	TEPtr		te;		te = *docTE;	te->viewRect.bottom = (((te->viewRect.bottom - te->viewRect.top) / te->lineHeight)							* te->lineHeight) + te->viewRect.top;} /*AdjustViewRect*//* Scroll the TERec around to match up to the potentially updated scrollbar	values. This is really useful when the window has been resized such that the	scrollbars became inactive but the TERec was already scrolled. */void AdjustTE(WindowPtr window){	TEPtr		te;		te = *((DocumentPeek)window)->docTE;	TEScroll((te->viewRect.left - te->destRect.left) -			GetControlValue(((DocumentPeek)window)->docHScroll),			(te->viewRect.top - te->destRect.top) -				GetControlValue(((DocumentPeek)window)->docVScroll),			((DocumentPeek)window)->docTE);} /*AdjustTE*//* Calculate the new control maximum value and current value, whether it is the horizontal or	vertical scrollbar. The vertical max is calculated by comparing the number of lines to the	vertical size of the viewRect. The horizontal max is calculated by comparing the maximum document	width to the width of the viewRect. The current values are set by comparing the offset between	the view and destination rects. If necessary and we canRedraw, have the control be re-drawn by	calling ShowControl. */// for styled text, we cannot rely on the line height being constant, so we// have to use pixels instead of nLinesvoid AdjustHV(Boolean isVert, ControlHandle control, TEHandle docTE, Boolean canRedraw){	short		value, max;	short		oldValue, oldMax;		oldValue = GetControlValue(control);	oldMax = GetControlMaximum(control);	if (isVert)		max = TEGetHeight((*docTE)->nLines, 0, docTE) -					((*docTE)->viewRect.bottom - (*docTE)->viewRect.top);	else		max = kMaxDocWidth - ((*docTE)->viewRect.right - (*docTE)->viewRect.left);	if (max < 0)		max = 0;	SetControlMaximum(control, max);	if (isVert)		value = (*docTE)->viewRect.top - (*docTE)->destRect.top;	else		value = (*docTE)->viewRect.left - (*docTE)->destRect.left;		if ( value < 0 )		value = 0;	else if ( value >  max ) value = max;		SetControlValue(control, value);	/* now redraw the control if it needs to be and can be */	if ( canRedraw || (max != oldMax) || (value != oldValue) )		ShowControl(control);} /*AdjustHV*//* Simply call the common adjust routine for the vertical and horizontal scrollbars. */void AdjustScrollValues(WindowPtr window, Boolean canRedraw){	DocumentPeek doc;		doc = (DocumentPeek)window;	AdjustHV(true, doc->docVScroll, doc->docTE, canRedraw);	AdjustHV(false, doc->docHScroll, doc->docTE, canRedraw);} /*AdjustScrollValues*//*	Re-calculate the position and size of the viewRect and the scrollbars.	kScrollTweek compensates for off-by-one requirements of the scrollbars	to have borders coincide with the growbox. */void AdjustScrollSizes(WindowPtr window){	Rect		teRect;	DocumentPeek doc;		doc = (DocumentPeek) window;	GetTERect(window, &teRect);							/* start with TERect */	(*doc->docTE)->viewRect = teRect;	MoveControl(doc->docVScroll, window->portRect.right - kScrollbarAdjust, -1);	SizeControl(doc->docVScroll, kScrollbarWidth, (window->portRect.bottom - 				window->portRect.top) - (kScrollbarAdjust - kScrollTweek));	MoveControl(doc->docHScroll, -1, window->portRect.bottom - kScrollbarAdjust);	SizeControl(doc->docHScroll, (window->portRect.right - 				window->portRect.left) - (kScrollbarAdjust - kScrollTweek),				kScrollbarWidth);} /*AdjustScrollSizes*//* Turn off the controls by jamming a zero into their contrlVis fields (HideControl erases them	and we don't want that). If the controls are to be resized as well, call the procedure to do that,	then call the procedure to adjust the maximum and current values. Finally re-enable the controls	by jamming a $FF in their contrlVis fields. */void AdjustScrollbars(WindowPtr window, Boolean needsResize){	DocumentPeek doc;		doc = (DocumentPeek) window;	/* First, turn visibility of scrollbars off so we wonÕt get unwanted redrawing */	(*doc->docVScroll)->contrlVis = kControlInvisible;	/* turn them off */	(*doc->docHScroll)->contrlVis = kControlInvisible;	if ( needsResize )									/* move & size as needed */		AdjustScrollSizes(window);	AdjustScrollValues(window, needsResize);			/* fool with max and current value */	/* Now, restore visibility in case we never had to ShowControl during adjustment */	(*doc->docVScroll)->contrlVis = kControlVisible;	/* turn them on */	(*doc->docHScroll)->contrlVis = kControlVisible;} /* AdjustScrollbars */// When the user selects text by dragging, TextEdit repeatedly calls a click loop routine which// it gets from the TERecord's clikLoop field. TextEdit's default routine does some useful things,// such as scrolling the text being selected, but it doesn't know about our scroll bars.// Therefore, we replace the routine with one that calls both the old routine and an add-on routine// which handles the scroll bars. Unfortunately, the way this works is very different for 68K and// PowerPC. On 68K, we have to be aware that the original click loop routine has a register-based// interface, so our replacement is easier to write in assembly. For PowerPC, we can let routine// descriptors handle the argument conversions, and do everything in the C routine ClickLoopProc.#if powercpascal Boolean ClickLoopProc(TEPtr pTE){	CallTEClickLoopProc(GetOldClickLoop(), pTE);	ClickLoopAddOn();	return true;}#endif// The ClickLoopAddOn routine handles the scroll bars during drag-scrolling.pascal void ClickLoopAddOn(void){	WindowPtr	window;	RgnHandle	region;		window = FrontWindow();	region = NewRgn();	GetClip(region);					/* save clip */	ClipRect(&window->portRect);	AdjustScrollValues(window, true);	/* pass true for canRedraw */	SetClip(region);					/* restore clip */	DisposeRgn(region);}// GetOldClickLoop returns the address of the default click loop routine that we put into the// TERec when creating it.pascal TEClickLoopUPP GetOldClickLoop(void){	return ((DocumentPeek)FrontWindow())->docClick;}// Check whether a window is a document window created by the application.// These windows have the windowKind userKind, so we can distinguish them from// desk accessories, dialogs, and other windows.Boolean IsDocumentWindow(WindowPtr window){	return (window != nil) && (((WindowPeek) window)->windowKind == userKind);}// Check whether a window belongs to a desk accessory.// These windows have negative windowKinds.Boolean IsDAWindow(WindowPtr window){	return (window != nil) && (((WindowPeek) window)->windowKind < 0);}/*	Display an alert that tells the user an error occurred, then exit the program.	This routine is used as an ultimate bail-out for serious errors that prohibit	the continuation of the application. Errors that do not require the termination	of the application should be handled in a different manner. Error checking and	reporting has a place even in the simplest application. The error number is used	to index an 'STR#' resource so that a relevant message can be displayed. */void AlertUser(short error){	short		itemHit;	Str255		message;	SetCursor(&qd.arrow);	/* type Str255 is an array in MPW 3 */	GetIndString(message, rErrorStrings, error);	ParamText(message, (ConstStr255Param) "", (ConstStr255Param) "", (ConstStr255Param) "");	itemHit = Alert(rUserAlert, nil);} /* AlertUser */#if qAppleEvents// Apple Event Supportstatic OSErr GotRequiredParameters(const AppleEvent *theAppleEvent){	OSErr myErr;	DescType returnedType;	Size actualSize;		myErr = AEGetAttributePtr(theAppleEvent, keyMissedKeywordAttr, typeWildCard, &returnedType,				nil, 0, &actualSize);	if (myErr == errAEDescNotFound)		return noErr;	else if (myErr == noErr)		return errAEParamMissed;	else		return myErr;}pascal OSErr HandleOAppEvent(const AppleEvent *theEvent, const AppleEvent *reply, long refCon){	#pragma unused(reply, refCon)		OSErr theError;		theError = GotRequiredParameters(theEvent);	if (theError == noErr)		DoNew();	return theError;}pascal OSErr HandleDocEvent(const AppleEvent *theEvent, const AppleEvent *reply, long refCon){	#pragma unused(theEvent, reply, refCon)		OSErr theError;	AEDescList docList;	long itemsInList;	long index;	AEKeyword keyword;	DescType returnedType;	FSSpec theFileSpec;	Size actualSize;			theError = AEGetParamDesc(theEvent, keyDirectObject, typeAEList,  &docList);	if (theError == noErr)	{		theError = GotRequiredParameters(theEvent);		if (theError == noErr)		{			theError = AECountItems(&docList, &itemsInList);			if (theError == noErr)			{				for (index = 1; index <= itemsInList; index++)				{					theError = AEGetNthPtr(&docList, index, typeFSS, &keyword, &returnedType,								(Ptr) &theFileSpec, sizeof(theFileSpec), &actualSize);					if (theError == noErr)					{						if (refCon == kAEOpenDocuments)							// we don't open documents yet, but here's what it would look like:							// theError = OpenDocument(theFileSpec);							;						else							// we don't print disk documents either (we can't read them),							// but here's what it would look like:							// theError = PrintDocument(theFileSpec);							;					};				};			};		};		(void) AEDisposeDesc(&docList);	};	return theError;}pascal OSErr HandleQuitEvent(const AppleEvent *theEvent, const AppleEvent *reply, long refCon){	#pragma unused(reply, refCon)		OSErr theError;		theError = GotRequiredParameters(theEvent);	if (theError == noErr)	{		PrepareToQuit();		if (!gQuitting)			theError = userCanceledErr;	};	return theError;}static OSErr InstallRequiredAppleEvents(void){	OSErr result;		gHandleOAppUPP = NewAEEventHandlerProc(HandleOAppEvent);	FailNilUPP((UniversalProcPtr) gHandleOAppUPP);	gHandleDocUPP = NewAEEventHandlerProc(HandleDocEvent);	FailNilUPP((UniversalProcPtr) gHandleDocUPP);	gHandleQuitUPP = NewAEEventHandlerProc(HandleQuitEvent);	FailNilUPP((UniversalProcPtr) gHandleQuitUPP);	result = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,				gHandleOAppUPP, 0, false);	if (result == noErr)		result = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,					gHandleDocUPP, kAEOpenDocuments, false);	if (result == noErr)		result = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments,					gHandleDocUPP, kAEPrintDocuments, false);	if (result == noErr)		result = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,					gHandleQuitUPP, 0, false);	return result;}#if qInline// this TSMTEPreUpdateProc only works around a bug in TSMTE 1.0, which has// been fixed in 1.1. For other possible uses, see technote TE 27.static pascal void MyTSMTEPreUpdateProc(TEHandle textH, long refCon){	#pragma unused(refCon)	long response;	ScriptCode keyboardScript;	short mode;	TextStyle theStyle;		if ((Gestalt(gestaltTSMTEVersion, &response) == noErr) && (response == gestaltTSMTE1))	{		keyboardScript = GetScriptManagerVariable(smKeyScript);		mode = doFont;		if (!(TEContinuousStyle(&mode, &theStyle, textH) &&				FontToScript(theStyle.tsFont) == keyboardScript))		{			theStyle.tsFont = GetScriptVariable(keyboardScript, smScriptAppFond);			TESetStyle(doFont, &theStyle, false, textH);		};	};}// this TSMTEPostUpdateProc makes sure that our scrollbars and scroll information// are consistent with what TSMTE is doing. For other possible uses, see technote TE 27.static pascal void MyTSMTEPostUpdateProc(TEHandle textH, long fixLen, long inputAreaStart,			long inputAreaEnd, long pinStart, long pinEnd, long refCon){	#pragma unused(textH, fixLen, inputAreaStart, inputAreaEnd, pinStart, pinEnd)		AdjustScrollbars((WindowPtr) refCon, false);	AdjustTE((WindowPtr) refCon);}#endif // qInline#endif // qAppleEvents