InlineInputSample/InlineInputSample.c

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