Sources/MSScript.c

// MSScript.c
//
// Original version by Jon Lansdell and Nigel Humphreys.
// 4.0 and 3.1 updates by Greg Sutton.
// Human Interface changes and GX Printing by Don Swatman
// ©Apple Computer Inc 1996, all rights reserved.
 
 
#include <Memory.h>
#include <Resources.h>
#include <TextEdit.h>
#include <Controls.h>
#include <Dialogs.h>
#include <Errors.h>
#include <Files.h>
#include <Folders.h>
#include <Components.h>
#include <AppleEvents.h>
#include <AppleScript.h>
#include <AERegistry.h>
#include <ASRegistry.h>
#include <ASDebugging.h>
#include <Processes.h>
 
#include "MSScript.h"
 
#include "MSUtils.h"
#include "MSAEUtils.h"
#include "MSAETextUtils.h"
#include "MSAEMenuUtils.h"
#include "MSWindow.h"
#include "MSAESetData.h"
#include "MSAEGetData.h"
#include "MSResultWind.h"
#include "MSAEDelete.h"
#include "MSASSubroutines.h"
#include "MSMain.h"
#include "MSAEWindowUtils.h"
#include "MSAERecording.h"
 
/*
    Each menu (or menu item) can have a script associated with it. This
    script has an ID equal to 32 * theMenuID + theItem number. A script
    with an item number of 0 applies to the whole menu unless it is 
    overridden by a particular script for an item in the menu.
    
    Scripts are stored in two resource types
        'scpt' is a compiled script as returned by OSAStore()
        'SCPT' is a raw text script as defined in the resource file
        
    At open application time scripts are built from the 'SCPT' resources 
    if there are not any existing complied scripts of that resource ID.
    These are stored in the preferences file for the application.
    
    One problem with the scripted menu commands is that if the application
    name is changed the target is no longer found. This may be addressed later.
*/
 
 
    // Constants
#define kMaxScriptsInApp        100
#define kCompiledScriptType     kOSAScriptResourceType
#define kTextScriptType         'SCPT'
 
const short kLaunchScriptID         = 128;
const short kApplicationScriptID    = 200;
const short kResultsScriptID        = 300;
const short kPleaseWaitStart        = 1006;
const short kPleaseWaitEnd          = 1007;
 
const short kEscapeKey              = 27;
const short kEndOfTextKey           = 3;
 
const short MenuScriptEditor        = 400;
 
const Str31 kPrefFileName           = "\pMenuScripter Prefs";
 
    // Prototypes
pascal OSErr        MySendProc(const AppleEvent*    theAppleEvent,
                                    AppleEvent*     reply,
                                    AESendMode      sendMode,
                                    AESendPriority  sendPriority,
                                    long            timeOutInTicks,
                                    AEIdleUPP       idleProc,
                                    AEFilterUPP     filterProc,
                                    long            refCon);
pascal OSErr        StdActiveProc( long refCon );
 
static OSAID        GetScriptIDForResID( short lookForID );
OSErr               OpenPreferencesResourceFile(short *theRefNum);
OSErr               ClosePreferencesResourceFile( short theRefNum );
void                AddMenuScript( short theResID, OSAID theScriptID, Boolean fChanged );
OSErr               LoadApplicationScripts( void );
OSErr               StoreApplicationScripts( void );
OSErr               BuildMenuScripts( void );
OSErr               StoreMenuScripts( void );
Boolean             ScriptHasProperties( OSAID theScriptID );
static void         SetUpUniversalProcedures( void );
static DialogPtr    PoseWaitDialog( short theDialogID );
void                DisplayErrorInScript( TEHandle theHTE );
 
pascal void         DrawGrowBoxItem( DialogPtr theDialog, short theItem );
pascal void         DrawStyledTextEditRec( DialogPtr theDialog, short theItem );
pascal void         DrawScrollBar( DialogPtr theDialog, short theItem );
pascal void         ScrollVActionProc( ControlHandle control, short part );
pascal Boolean      MyFilterProc( DialogPtr theDialog, EventRecord *myEvent, short *itemHit );
 
void                MoveCustTextEditItem( DialogPtr theDialog, short theItem,
                                                    short deltaW, short deltaH );
void                MoveScrollBarItem( DialogPtr theDialog, short theItem,
                                                    short deltaW, short deltaH );
void                MoveOneDialogControlItem( DialogPtr theDialog, short theItem,
                                                    short deltaW, short deltaH );
void                MoveOneDialogItem( DialogPtr theDialog, short theItem,
                                                    short deltaW, short deltaH );
 
 
void                SetDrawProcForUserItem( DialogPtr theDialog,
                                    short theItem, ControlActionUPP theProc );
void                FlashButton( DialogPtr theDialog, short theItem );
void                TrackTEPosn( ControlHandle theControl, TEHandle theHTE );
void                SetScrollLimit( ControlHandle newScroll, TEHandle theHTE );
Boolean             DoDialogMenu ( long menuResult, TEHandle theTE );
 
OSErr               SetMenuScript( short theResID, OSAID theScriptID );
void                SetMenuForDialog( void );
void                ReSetMenuForDialog( void );
 
pascal OSErr        EventPrehandler( AppleEvent *theEvent, AppleEvent *theReply, long theRefcon );
Boolean             CanScriptEvent( AEEventClass theClass, AEEventID theID, AppleEvent* theEvent );
void                AddMissingParameter( AEKeyword theAEKeyword, DescType theType,
                                            Ptr theData, Size theDataSize, AppleEvent* theEvent );
OSErr               DoScriptEvent( AppleEvent           *theEvent, 
                                    AppleEvent          *theReply, 
                                    OSAID               theScriptID );
static OSErr        GetTokenDescScript( AEDesc* theTokenDesc, OSAID* theOSAID );
 
    // Globals
extern short        gRefNum;
 
ComponentInstance   gScriptingComponent;
short               gActiveMenuItem;
OSASendUPP          oldSendProc;
long                refCon;
MenuScriptRecPtr    gMenuScripts;
short               gNumScripts;
short               gPrefsRefFile;
OSAActiveUPP        gOSAActiveUPP;
Boolean             gEditingScript; // Is the script editing dialog up?
 
ControlActionUPP    gScrollScriptVActionUPP;
ControlActionUPP    gDrawStyledTextUPP;
ControlActionUPP    gDrawScrollBarUPP;
ControlActionUPP    gDrawGrowBoxUPP;
 
 
//  These are used in documentation but not defined in the interfaces
 
#ifndef kComponentNotFound
    #define kComponentNotFound  -1
#endif
 
 
pascal OSErr MySendProc(const AppleEvent*   theAppleEvent,
                            AppleEvent*     reply,
                            AESendMode      sendMode,
                            AESendPriority  sendPriority,
                            long            timeOutInTicks,
                            AEIdleUPP       idleProc,
                            AEFilterUPP     filterProc,
                            long            refCon)
{ 
#ifdef __MWERKS__
    #pragma unused(refCon)
#endif
 
    DescType     typeCode;
    Size         descSize;
    OSErr        myErr;
    OSErr        ignoreErr;
    OSType       whatEvent;
    AppleEvent   myCopyAEvt;
    
    myErr = AEDuplicateDesc( theAppleEvent, &myCopyAEvt );
 
    myErr = AEGetAttributePtr(&myCopyAEvt, keyEventIDAttr, typeType, &typeCode, 
                                    (Ptr)&whatEvent, sizeof(whatEvent), &descSize );
                                                        
    ignoreErr = AEDisposeDesc( &myCopyAEvt );
    
    if (myErr != noErr) whatEvent = 0x3F3F3F3F;
    
    //  Record the final Set Data that occurs due to a script
    //  'gdte' = id sent to get 'aete' resource from app, dealt with
    //  by system handler.
 
    if ( ( sendMode & kAEDontRecord ) && 
            ( ( whatEvent != kAEGetData ) && ( whatEvent != kGetAETE ) ) )
        sendMode = sendMode - kAEDontRecord;
        
    /* will be this when a5 bug fixed   // =Jon=
    return((*oldSendProc)(theAppleEvent,
                                 reply,
                                 sendMode,
                                 sendPriority,
                                 timeOutInTicks,
                                 idleProc,
                                 filterProc,
                                 refCon));
    */
    
    return( AESend( theAppleEvent, reply, sendMode, sendPriority,
                                timeOutInTicks, idleProc, filterProc ) );                           
}   // MySendProc
 
 
// Our activeProc is called at various times during script execution and compilation
// we use this to detect when the application is being sent to the background. This can happen
// when a script is targetted to another application, causing this application to be
// brought to the front. Since HiliteMenu(0) is called when the menu command has completed - and
// this will be called when the script has completed, we must ensure that the application is
// in the foreground when HiliteMenu is called. Otherwise the menu bar gets drawn over the frontmost
// application, which is not what we want.
 
pascal OSErr StdActiveProc( long refCon )
{
#ifdef __MWERKS__
    #pragma unused(refCon)
#endif
 
    // Call WaitnextEvent() to get suspend event
    EventRecord myEvent;
 
    if ( ! gEditingScript )
    {
        if ( WaitNextEvent( everyEvent, &myEvent, 0, NULL ) )
        {
            switch ( myEvent.what )
            {
                case osEvt:
                    HiliteMenu( 0 );// Don't call HiliteMenu in the background 
                                    // so clear it before we switch to the background
                    
                    if ( ( myEvent.message >> 24 ) & suspendResumeMessage ) // suspend or resume
                    {
                        gInBackground = ( ( myEvent.message & resumeFlag ) == 0);
                        DoActivate( FrontWindow( ), ! gInBackground );
                    }
                    break;
            }
        }
    }
 
        // Now call the standard ActiveProc
    return ( CallOSAActiveProc( gOSAActiveUPP,0 ) );
}
 
    
static OSAID    GetScriptIDForResID( short lookForID )
{
    short   index;
    
    for ( index = 0; index < gNumScripts; index++ )
    {
        if ( gMenuScripts[index].theResID == lookForID )
            return gMenuScripts[index].theScriptID;
    }
        
    return kOSANullScript;
}
    
MenuScriptRecPtr    GetMenuScriptRecPtr( short theResID )
{
    short   index;
    
    for ( index = 0; index < gNumScripts; index++ )
    {
        if ( gMenuScripts[index].theResID == theResID )
            return &gMenuScripts[index];
    }
        
    return NULL;
}
    
 
//  Name : OpenPreferencesResourceFile
//  Purpose : Opens the prefs file, in the system prefs folder
//              creating it if it is not found. Sets the current
//              resource file to the prefs file so that resources
//              will be read from there in preference to the app
//              if they have been overridden, and new resources
//              will be created in it.
    
OSErr   OpenPreferencesResourceFile(short *theRefNum)
{
    short  foundVRefNum;
    long   foundDirID;
    OSErr  myErr;
    
    myErr = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
                                                    &foundVRefNum, &foundDirID);
                                         
    if (myErr==noErr)
    {
 
        *theRefNum = HOpenResFile(foundVRefNum, foundDirID, kPrefFileName, fsWrPerm);
        myErr = ResError();
        
        if (myErr==fnfErr)
        {
            HCreateResFile(foundVRefNum, foundDirID, kPrefFileName);
            myErr = ResError();
 
            *theRefNum = HOpenResFile(foundVRefNum, foundDirID, kPrefFileName, fsWrPerm);
            myErr = ResError();
        }
    }
 
    return(myErr);
}
    
OSErr   ClosePreferencesResourceFile( short theRefNum )
{
    CloseResFile( theRefNum );
    return( ResError( ) );
}
 
 
//  Name : AddMenuScript
//  Purpose : Adds a compiled script OSAID into the
//            global gMenuScripts array for later access
 
void    AddMenuScript( short theResID, OSAID theScriptID, Boolean fChanged )
{
    if ( gNumScripts < kMaxScriptsInApp )
    {
        gMenuScripts[gNumScripts].theResID = theResID;
        gMenuScripts[gNumScripts].theScriptID = theScriptID;
        gMenuScripts[gNumScripts].fChanged = fChanged;
        gNumScripts++;
        
        (void)CheckForMenuItemName( theResID, theScriptID );
    }
}
 
 
//  Name : BuildMenuScripts
//  Purpose : Reads compiled scripts from the prefs file,
//            Any uncompiled scripts ('SCPT' resources with
//             no 'scpt' of same id are compiled. Any which fail
//             to compile are ignored. Changed scripts are stored
//             in the preferences file on quit.
 
OSErr   BuildMenuScripts( void )
{
    short       aScriptCount,
                index,
                anID;
    ResType     aResType;
    Str255      aResName;
    Handle      aScript;
    AEDesc      aScriptDesc = { typeNull, NULL };
    OSAID       aScriptID = kOSANullScript;
    OSErr       anErr;
    
    gNumScripts = 0;
    
    gMenuScripts = (MenuScriptRecord *)NewPtr( sizeof( MenuScriptRecord ) * kMaxScriptsInApp );
    
        // Firstly load all the 'scpt' scripts stored in the preferences file,
        //  these scripts may be customised by the user.
    aScriptCount = Count1Resources( kCompiledScriptType );
    for ( index = 1 ; index <= aScriptCount ; index++ )
    {
        aScript = Get1IndResource( kCompiledScriptType, index );
        GetResInfo( aScript, &anID, &aResType, aResName );
        anErr = ResError( );
        if ( noErr != anErr ) goto done;
        
        HLock( aScript );
        anErr = AECreateDesc( typeOSAGenericStorage, (Ptr)*aScript,
                                    GetHandleSize( aScript ), &aScriptDesc );
        HUnlock( aScript );
        ReleaseResource( aScript );
        if ( noErr != anErr ) goto done;    // This shouldn't happen
        
        aScriptID = kOSANullScript;
        anErr = OSALoad( gScriptingComponent, &aScriptDesc, kOSAModeNull, &aScriptID );
        
        if ( noErr == anErr )
            AddMenuScript( anID, aScriptID, false );
        
        (void)AEDisposeDesc( &aScriptDesc );
    }
        
        // Next load and compile any of the text based scripts, 'SCPT', that 
        //  have no equivalent 'scpt' in the preferences file.
    aScriptCount = CountResources( kTextScriptType );
    for ( index = 1; index <= aScriptCount; index++ )
    {
        aScript = GetIndResource( kTextScriptType, index );
        GetResInfo( aScript, &anID, &aResType, aResName );
        anErr = ResError( );
        if ( noErr != anErr ) goto done;
        
        if ( GetScriptIDForResID( anID ) == kOSANullScript )
        {
                    // Stored as a C string so knock off NULL terninator
            HLock( aScript );
            anErr = AECreateDesc( typeChar, (Ptr)*aScript,
                                    GetHandleSize( aScript ) - 1, &aScriptDesc );
            HUnlock( aScript );
            ReleaseResource( aScript );
            if ( noErr != anErr ) goto done;    // This shouldn't happen
            
            aScriptID = kOSANullScript;
            anErr = OSACompile( gScriptingComponent, &aScriptDesc,
                                    kOSAModeCompileIntoContext, &aScriptID );
                                                
            if ( noErr == anErr )
                AddMenuScript( anID, aScriptID, true ); // We need to save this script
                                                        //  so set fChanged to true.
            (void)AEDisposeDesc( &aScriptDesc );
        }
    }
    
done:
    return anErr;
}
 
// This checks to see if a script contains
//  properties. These could change in the script and therefore
//  the script needs to be stored for the next
//  time the application is launched.
 
Boolean ScriptHasProperties( OSAID theScriptID )
{
    AEDescList  resultingPropertyNames = { typeNull, NULL };
    long        aCount = 0;
    OSErr       anErr;
    Boolean     aResult = true;
 
        // OSAGetPropertyNames() returns typeChar descriptors of
        //  all the properties in a script.
    anErr = OSAGetPropertyNames( gScriptingComponent, kOSAModeNull, 
                        theScriptID, &resultingPropertyNames );
 
    if ( noErr != anErr )
    {
        aResult = false;
        goto done;
    }
    
    anErr = AECountItems( &resultingPropertyNames, &aCount );
    
    if ( noErr != anErr || aCount == 0 )
        aResult = false;
    
done:
    (void)AEDisposeDesc( &resultingPropertyNames );
 
    return aResult;
}
 
OSErr   StoreMenuScripts( void )
{
    short       index;
    OSErr       anErr;
    
    for ( index = 0; index < gNumScripts; index++ )
    {
            // If there are properties or it
            //  has changed then store the script
        if ( gMenuScripts[index].fChanged
                || ScriptHasProperties( gMenuScripts[index].theScriptID ) )
        {
            anErr = StoreScriptToResFileRef( gPrefsRefFile, gMenuScripts[index].theResID,
                                                        gMenuScripts[index].theScriptID, "\p" );
        }
    }
        
    return anErr;
}
 
 
OSErr   LoadApplicationScripts( void )
{
    OSAID   aScriptID = kOSANullScript,
            aResultID = kOSANullScript;
    DPtr    aDoc;
    OSErr   anErr;
 
    OpenPreferencesResourceFile( &gPrefsRefFile );
    
    gMenuScripts = NULL;
    anErr = BuildMenuScripts( );
    if ( noErr != anErr ) goto done;
    
        // Get the current application scipt from the prefs file.
        //  If there isn't one then the launch script can set one.
    anErr = LoadScriptFromResFileRef( gPrefsRefFile,
                            kApplicationScriptID, &gAppRec.theScriptID );
        
        // Load the results window script too.      
    aDoc = GetResultsDoc( );
    if ( aDoc )
        anErr = LoadScriptFromResFileRef( gPrefsRefFile,
                            kResultsScriptID, &aDoc->theScriptID );
    
        // The application script property is set by executing the 
        //  application script - current script checks to see
        //  if there is already a script - from prefs file.
    anErr = LoadScriptFromResFileRef( gRefNum, kLaunchScriptID, &aScriptID );
    if ( noErr != anErr ) goto done;
    
    anErr = OSAExecute( gScriptingComponent, aScriptID, kOSANullScript,
                                            kOSAModeAlwaysInteract, &aResultID );
    
done:
    (void)OSADispose( gScriptingComponent, aScriptID );
    (void)OSADispose( gScriptingComponent, aResultID );
        
    return anErr;
}
 
 
OSErr   StoreApplicationScripts( void )
{
    DPtr    aDoc;
    OSErr   anErr;
    
    anErr = StoreMenuScripts( );
    if ( noErr != anErr ) goto done;
 
        // Store any changes to the applications script
    anErr = StoreScriptToResFileRef( gPrefsRefFile, kApplicationScriptID,
                                gAppRec.theScriptID, "\pApplicationScript" );
 
        // Store any changes to the results document script
    aDoc = GetResultsDoc( );
    if ( aDoc )
        anErr = StoreScriptToResFileRef( gPrefsRefFile, kResultsScriptID,
                                aDoc->theScriptID, "\pResultsScript" );
 
    ClosePreferencesResourceFile( gPrefsRefFile );
 
done:
    return anErr;
}
 
 
static void SetUpUniversalProcedures( void )
{
    gScrollScriptVActionUPP = NewControlActionProc( ScrollVActionProc );
    gDrawStyledTextUPP = NewControlActionProc( DrawStyledTextEditRec );
    gDrawScrollBarUPP = NewControlActionProc( DrawScrollBar );
    gDrawGrowBoxUPP = NewControlActionProc( DrawGrowBoxItem );
}
 
 
static DialogPtr    PoseWaitDialog( short theDialogID )
{
    DialogPtr progressDialog;
 
        // pose a dialog indicating that we are connecting
        // to the AppleScript component.
    progressDialog = GetNewDialog( theDialogID, NULL, (WindowPtr)-1 );
    SetPort( progressDialog );
    DrawDialog( progressDialog );
    return progressDialog;
}
 
//  Name:   InitEditorScripting
//  Purpose:Connects to the AppleScript component.
 
OSErr   InitEditorScripting( void )
{
    GrafPtr                 oldPort;
    DialogPtr               progressDialog;
    OSAError                err = 0;
    ComponentDescription    descr;
    Component               aComponent;
    ComponentInstance       aScriptingComponent;
    
    GetPort( &oldPort );
    progressDialog = PoseWaitDialog( kPleaseWaitStart );
    
    SetUpUniversalProcedures( );
    gEditingScript = false;
    
    descr.componentType         = kOSAComponentType;
    descr.componentSubType      = (OSType) 0;
    descr.componentManufacturer = (OSType) 0;
    descr.componentFlags        = kOSASupportsCompiling + 
                                    kOSASupportsGetSource +
                                        kOSASupportsAECoercion + 
                                            kOSASupportsAESending +
                                                kOSASupportsRecording +
                                                    kOSASupportsConvenience +
                                                        kOSASupportsEventHandling;
    descr.componentFlagsMask    = descr.componentFlags;
    
    aComponent = FindNextComponent( NULL, &descr );
        
    if ( ! aComponent )
    {
        err = kComponentNotFound;
        goto done;
    }
    else
    {
        aScriptingComponent = OpenComponent( aComponent );
                                                                                                
        if (! aScriptingComponent)
        {
            err = kComponentNotFound;
            goto done;
        }
    }
        
    gScriptingComponent = aScriptingComponent;
 
            
    err = OSAGetSendProc( gScriptingComponent, &oldSendProc, &refCon );
    if ( noErr != err ) goto done;
                                                
    err = OSASetSendProc( gScriptingComponent, NewOSASendProc( MySendProc ), 0 );
    if ( noErr != err ) goto done;
 
    err = AEInstallSpecialHandler( keyPreDispatch, (UniversalProcPtr)NewAEEventHandlerProc( EventPrehandler ), false );
    if ( noErr != err ) goto done;
    
    err = LoadApplicationScripts( );
 
done:
    DisposeDialog( progressDialog );
    SetPort( oldPort );
 
    return err;
}
 
 
OSErr   SetOSAActiveProcedure(void)
{
    OSErr   err;
    long    theRefCon;
    
        // store the old active proc in order to call this from ours
    err = OSAGetActiveProc( gScriptingComponent, &gOSAActiveUPP, &theRefCon );
    
    return( OSASetActiveProc( gScriptingComponent, NewOSAActiveProc( StdActiveProc ), 0 ) );
}
 
 
//  Name    : CloseEditorScripting
//  Purpose : Shutdown of editor scripting capabilities
 
OSErr   CloseEditorScripting( void )
{
    GrafPtr     oldPort;
    DialogPtr   progressDialog;
    short       index;
    OSErr       err;
    
    GetPort( &oldPort );
    progressDialog = PoseWaitDialog( kPleaseWaitEnd );
 
    StoreApplicationScripts( );
    
    for ( index = 0; index < gNumScripts; index++ )
        err = OSADispose( gScriptingComponent, gMenuScripts[index].theScriptID );
    
    err = CloseComponent( gScriptingComponent );
 
    DisposeDialog( progressDialog );
    SetPort( oldPort );
    
    return err;
}
 
    
 
//  Name:    DisplayErrorInScript
//  Purpose: Put up an alert showing the error text for the last error.
//           Also selects the responsible text in theHTE if one is supplied
 
void    DisplayErrorInScript( TEHandle theHTE )
{
    Str255   errorMessageText;
    AEDesc   errRangeDesc;
    AEDesc   errRecordDesc;
    AEDesc   errTextDesc;
    AEDesc   errNumberDesc;
    DescType typeCode;
    Size     actSize;
    OSErr    myErr;
    OSErr    ignoreErr;
    OSErr    errorNumber;
    short    errorStart;
    short    errorEnd;
    
    SetCursor(&qd.arrow);
    
    myErr = OSAScriptError(gScriptingComponent, kOSAErrorNumber,
                                        typeShortInteger, &errNumberDesc);
    
    myErr = GetIntegerFromDescriptor(&errNumberDesc, &errorNumber);
            
    ignoreErr = AEDisposeDesc(&errNumberDesc);
    
    myErr = OSAScriptError(gScriptingComponent, kOSAErrorMessage,
                                                typeChar, &errTextDesc);
                                                 
    myErr = GetPStringFromDescriptor( &errTextDesc, errorMessageText );
    
    ignoreErr = AEDisposeDesc(&errTextDesc);
    
    if (theHTE)
    {
        myErr = OSAScriptError(gScriptingComponent, kOSAErrorRange,
                                        typeOSAErrorRange, &errRangeDesc);
                                                     
        myErr = AECoerceDesc(&errRangeDesc, typeAERecord, &errRecordDesc);
        
        ignoreErr = AEDisposeDesc(&errRangeDesc);
        
        myErr = AEGetKeyPtr(&errRecordDesc, keyOSASourceStart, typeShortInteger,
                            &typeCode, (Ptr)&errorStart, sizeof(errorStart), &actSize);
                                                
        myErr = AEGetKeyPtr(&errRecordDesc, keyOSASourceEnd, typeShortInteger,
                                &typeCode, (Ptr)&errorEnd, sizeof(errorEnd), &actSize);
                                                
        ignoreErr = AEDisposeDesc(&errRecordDesc);
        
        TESetSelect(errorStart, errorEnd, theHTE);
    }
    
    ShowError(errorMessageText, errorNumber);
}
 
 
//  Name    : CompileDocument
//  Purpose : Compiles the text of the supplied document - does NOT keep
//            a compiled version - merely checks that it will compile for now
 
OSErr   CompileDocument( DPtr theDoc )
{
    WindowPropToken aToken;
    AEDesc          aTextDesc = { typeNull, NULL },
                    aCompiledDesc = { typeNull, NULL },
                    aNullDesc = { typeNull, NULL };
    OSAID           aScriptID = kOSANullScript;
    OSErr           anErr;
    
    aToken.tokenWindowToken.tokenWindow = theDoc->theWindow;
    aToken.tokenProperty = pText;       // All of the text in document
    anErr = GetDocumentTokenProperty( &aToken, typeWildCard, &aTextDesc );
    if ( noErr != anErr ) goto done;
        
    anErr = OSACompile( gScriptingComponent, &aTextDesc,
                                            kOSAModeCompileIntoContext, &aScriptID );
    if ( noErr != anErr ) goto done;
        
    anErr = OSAGetSource( gScriptingComponent, aScriptID,
                                        typeStyledText, &aCompiledDesc );
    if ( noErr != anErr ) goto done;
    
    anErr = DisplayDescResult( theDoc->theWindow, &aCompiledDesc, NULL, noErr );
 
done:
    (void)DisplayDescResult( GetResultsWindPtr( ), &aNullDesc, theDoc, anErr );
 
    (void)OSADispose( gScriptingComponent, aScriptID );
    (void)AEDisposeDesc( &aTextDesc );
    (void)AEDisposeDesc( &aCompiledDesc );
 
    return anErr;
}
    
 
//  Name    : ExecuteDocument
//  Purpose : Compiles the text of the supplied document and then executes it
//            - does NOT keep a compiled version after execution
 
OSErr   ExecuteDocument( DPtr theDoc )
{
    WindowPropToken aToken;
    AEDesc          aTextDesc = { typeNull, NULL },
                    aCompiledDesc = { typeNull, NULL };
    OSAID           aScriptID = kOSANullScript,
                    aResultID = kOSANullScript;
    OSErr           anErr;
    
    aToken.tokenWindowToken.tokenWindow = theDoc->theWindow;
    aToken.tokenProperty = pText;       // All of the text in document
    anErr = GetDocumentTokenProperty( &aToken, typeWildCard, &aTextDesc );
    if ( noErr != anErr ) goto done;
    
    anErr = OSACompile( gScriptingComponent, &aTextDesc, kOSAModeCompileIntoContext, &aScriptID );
    if ( noErr != anErr ) goto done;
                
            // Set the compiled text first, before executing
    anErr = OSAGetSource( gScriptingComponent, aScriptID,
                                    typeStyledText, &aCompiledDesc );
    if ( noErr != anErr ) goto done;
                                    
    anErr = DisplayDescResult( theDoc->theWindow, &aCompiledDesc, NULL, noErr );
    if ( noErr != anErr ) goto done;
    
    anErr = OSAExecute( gScriptingComponent, aScriptID,
                            kOSANullScript, kOSAModeNull, &aResultID );
 
done:
        // If aResultID is kOSANullScript then clear the window
    (void)DisplayOSAIDResult( GetResultsWindPtr( ), aResultID, theDoc, anErr );
 
    (void)OSADispose( gScriptingComponent, aScriptID );
    (void)OSADispose( gScriptingComponent, aResultID );
    (void)AEDisposeDesc( &aTextDesc );
    (void)AEDisposeDesc( &aCompiledDesc );
 
    return anErr;
}
 
 
//  Name   : ScriptForMenuExists
//  Purpose: Tests for the existance of a valid script for a given menu item             
    
OSErr   ScriptForMenuExists(short theMenu, short theItem, Boolean *exists)
{
    short  resID;
    OSAID  myScriptID;
    OSErr  myErr;
    
    myErr = noErr;
    
    resID = (theMenu<<5) + theItem;
    
    myScriptID = GetScriptIDForResID(resID);
    
    if (myScriptID==kOSANullScript)
    {
        if (theItem!=0)
            myErr = ScriptForMenuExists(theMenu, 0, exists);
        else
            *exists = false;
    }
    else
      *exists = true;
            
    return myErr;       
} // ScriptForMenuExists
    
    
//  Name   : ExecuteScriptForMenu
//  Purpose: Executes a script associated with the menu item.
//           Should be called only after ScriptForMenuExists
//              returns exists.
 
OSErr   ExecuteScriptForMenu( short theMenu, short theItem )
{
    short           resID;
    OSAID           aScriptID,
                    aResultID = kOSANullScript;
    OSErr           err;
 
    resID = ( theMenu << 5 ) + theItem;
    
    aScriptID = GetScriptIDForResID( resID );
            
    if ( aScriptID == kOSANullScript )
    {
        resID = ( theMenu << 5 ); // look for a script for the whole menu - item 0
        aScriptID = GetScriptIDForResID( resID );
    }
    
    if ( aScriptID != kOSANullScript )
    {
        gActiveMenuItem = theItem;
        
        err = OSAExecute( gScriptingComponent, aScriptID,
                                    kOSANullScript, 0, &aResultID );
 
        (void)DisplayOSAIDResult( GetResultsWindPtr( ), aResultID, NULL, err );
 
        gActiveMenuItem = 0;
    }
 
done:
    (void)OSADispose( gScriptingComponent, aResultID );
    
    return err;
}
 
 
//  Name :   GetScriptActiveItem
//  Purpose : Used by SVAppleEvents for the active item property of a menu.
//            Allows a script associated with more than one menu item to
//              find which menu item it was called by.
 
short   GetScriptActiveItem( void )
{
    return gActiveMenuItem;
}
 
 
 
//  Routines used in the StyledTextEdit Dialog Item
 
struct StyleTextInfo
{
    ControlHandle theScrollBar;
    TEHandle      theTEHandle;
};
typedef struct StyleTextInfo StyleTextInfo;
typedef StyleTextInfo *pStyleTextInfo;
 
 
pascal void DrawGrowBoxItem( DialogPtr theDialog, short theItem )
{
    short   theType;
    Handle  theHandle;
    Rect    itemRect;
            
    GetDItem(theDialog, theItem, &theType, &theHandle, &itemRect);
    PlotIconID (&itemRect, atBottomRight, ttNone, 400);
}
 
pascal void DrawStyledTextEditRec( DialogPtr theDialog, short theItem )
{
    short   theType;
    Handle  theHandle;
    Rect    itemRect;
            
    GetDItem(theDialog, theItem, &theType, &theHandle, &itemRect);
    FrameRect(&itemRect);
    InsetRect(&itemRect, 5, 5);
    
    TEUpdate(&itemRect, ((pStyleTextInfo) GetWRefCon(theDialog))->theTEHandle);
}
    
pascal void DrawScrollBar( DialogPtr theDialog, short theItem )
{       
#ifdef __MWERKS__
    #pragma unused(theItem)
#endif
 
    Draw1Control(((pStyleTextInfo) GetWRefCon(theDialog))->theScrollBar);
}
 
 
//  Name:    SetDrawProcForUserItem
//  Purpose: Installs a procedure that will be called when theItem needs
//           to be drawn.
 
void SetDrawProcForUserItem( DialogPtr theDialog, short theItem, ControlActionUPP theProc )
{
    short  theType;
    Handle theHandle;
    Rect   itemRect;
    
    GetDItem(theDialog, theItem, &theType, &theHandle, &itemRect);
    SetDItem(theDialog, theItem, theType, (Handle)theProc, &itemRect);
}
    
 
//  Name:    FlashButton
//  Purpose: Provide visual effect of button having been
//           selected.
 
void    FlashButton( DialogPtr theDialog, short theItem )
{
    short    theType;
    Handle   theHandle;
    Rect     itemRect;
    
    GetDItem(theDialog, theItem, &theType, &theHandle, &itemRect);
    
    HiliteControl((ControlHandle)theHandle, inButton);
    HiliteControl((ControlHandle)theHandle, 0);
}
    
 
//  Name:    ScrollVActionProc
//  Purpose: Called as scrollbar is tracked in buttons or page up/down.
//           Keeps text in sync with scrollBar
 
pascal void ScrollVActionProc( ControlHandle control, short part )
{
    short  amount;
    short  initialValue;
    
    if ( part )
    {
        switch ( part )
        {
            case inUpButton:
            case inDownButton:
                amount = 24;
                break;
            case inPageUp:
            case inPageDown:
                amount = (**control).contrlRect.bottom - (**control).contrlRect.top;
                break;
        }
        
        if ( part == inUpButton || part == inPageUp )
            amount = -amount; // reverse direction
    
        initialValue = GetCtlValue( control );
    
        SetCtlValue( control, initialValue+amount ); // Pinned within Min/Max automatically
        
        amount = GetCtlValue( control ) - initialValue;             
        
        if (amount)
            TEScroll(0, -amount,
                ((pStyleTextInfo)GetWRefCon((**control).contrlOwner))->theTEHandle);
    }
} // ScrollVActionProc
    
 
//  Name:    TrackTEPosn
//  Purpose: Ensures that the scrollbar value matches the position
//           of the text edit record.
 
void    TrackTEPosn( ControlHandle theControl, TEHandle theHTE )
{
    short shouldBe;
    
    shouldBe = (**theHTE).viewRect.top - (**theHTE).destRect.top;
                        
    if ( GetCtlValue( theControl ) != shouldBe )
        SetCtlValue( theControl, shouldBe );
}
 
 
//  Name:    SetScrollLimit
//  Purpose: Ensures that the maximum value of the scrollbar is sufficient
//           to allow showing of all text, and no more.
 
void    SetScrollLimit( ControlHandle newScroll, TEHandle theHTE )
{
    Point     teLimit;
    short     currentMax;
    short     currentValue;
    TextStyle theStyle;
    short     lineHeight;
    short     fontAscent;
    
    currentMax = GetCtlMax(newScroll);
    
    //  Scroll max should be position of bottom of bottom line when unscrolled
    //  This is equal to
    //    The position of the bottom line relative to current position +
    //      The amount currently scrolled -
    //      The height of the visible part
            
    teLimit    = TEGetPoint((**theHTE).teLength, theHTE); // Position of bottom relative to current posn
    
    TEGetStyle((**theHTE).teLength, &theStyle, &lineHeight, &fontAscent,theHTE);        
    teLimit.v += lineHeight-fontAscent; // Add in for descent - TEGetPoint is posn of baseline not bottom of chars
    
    teLimit.v += ((**theHTE).viewRect.top -(**theHTE).destRect.top); // Amount already scrolled
    
    teLimit.v -= ((**theHTE).viewRect.bottom - (**theHTE).viewRect.top); // Height of page
    
    if (teLimit.v<0) // Cannot be negative
        teLimit.v = 0;
        
    if ( currentMax > teLimit.v )
    {
        currentValue = GetCtlValue( newScroll );
 
        if ( currentValue > teLimit.v )
        {
            TEScroll( 0, currentValue - teLimit.v, theHTE );
            SetCtlValue( newScroll, teLimit.v );
        }
 
        SetCtlMax( newScroll, teLimit.v );
    }
        
    if ( currentMax < teLimit.v )
        SetCtlMax( newScroll, teLimit.v );
}
 
#define kCompile   3
#define kTextItem  4
#define kScrollBar 5
#define kGrowBox   6
 
Boolean DoDialogMenu( long menuResult, TEHandle theTE )
{
    short   theItem;
    short   theMenuID;
    Boolean hasHandled = false;
 
    theItem  = LoWord( menuResult );
    theMenuID = HiWord( menuResult );
 
    switch ( theMenuID )
    {
        case editID:
            switch (theItem)
            {
                case cutCommand:
                    TECut ( theTE );
                    hasHandled = true;
                    break;
                                             
                case copyCommand:
                    TECopy ( theTE );
                    hasHandled = true;
                    break;
                                             
                case pasteCommand:
                {
                    TEStylePaste ( theTE );
                    hasHandled = true;
                    break;
                }                            
                case clearCommand:
                    TEDelete ( theTE );     
                    hasHandled = true;
                    break;
                                             
                case selectAllCommand:
                    TESetSelect(0, kMaxTELength, theTE);
                    hasHandled = true;
                    break;
            }
                
            break;
    }
    return hasHandled;
}
 
void MoveOneDialogItem ( DialogPtr theDialog,
                         short     theItem,
                         short     deltaW,
                         short     deltaH )
{
    short  theType;
    Handle theHandle;
    Rect   itemRect;
    
    GetDItem(theDialog, theItem, &theType, &theHandle, &itemRect);
    OffsetRect ( &itemRect, deltaW, deltaH);
    SetDItem(theDialog, theItem, theType, theHandle, &itemRect);
}
 
void MoveOneDialogControlItem( DialogPtr theDialog,
                                 short     theItem,
                                 short     deltaW,
                                 short     deltaH )
{
    short  theType;
    Handle theHandle;
    Rect   itemRect;
    
    GetDItem(theDialog, theItem, &theType, &theHandle, &itemRect);
    OffsetRect ( &itemRect, deltaW, deltaH);
    SetDItem(theDialog, theItem, theType, theHandle, &itemRect);
 
    MoveControl ( (ControlHandle)theHandle,
                    (**(ControlHandle)theHandle).contrlRect.left + deltaW,
                    (**(ControlHandle)theHandle).contrlRect.top  + deltaH);
}
 
void MoveScrollBarItem( DialogPtr theDialog,
                        short     theItem,
                        short     deltaW,
                        short     deltaH )
{
    short  theType;
    Handle theHandle;
    Rect   itemRect;
    Rect   controlRect;
    ControlHandle hTheControl;
    
    GetDItem(theDialog, theItem, &theType, &theHandle, &itemRect);
    
    itemRect.left  = itemRect.left + deltaW;
    itemRect.right = itemRect.right + deltaW;
    itemRect.bottom = itemRect.bottom + deltaH;
 
    SetDItem(theDialog, theItem, theType, theHandle, &itemRect);
 
    hTheControl = ((pStyleTextInfo) GetWRefCon(theDialog))->theScrollBar;
    controlRect = (**hTheControl).contrlRect;
    HideControl ( hTheControl );
    SizeControl ( hTheControl,
                    controlRect.right - controlRect.left,
                    deltaH + controlRect.bottom - controlRect.top );
    MoveControl ( hTheControl,
                    controlRect.left + deltaW,
                    controlRect.top );
    ShowControl ( hTheControl );
}
 
void MoveCustTextEditItem( DialogPtr theDialog,
                            short     theItem,
                            short     deltaW,
                            short     deltaH )
{
    short    theType;
    Handle   theHandle;
    Rect     itemRect;
    TEHandle theTEHandle;
    
    GetDItem(theDialog, theItem, &theType, &theHandle, &itemRect);
    
    itemRect.right = itemRect.right + deltaW;
    itemRect.bottom = itemRect.bottom + deltaH;
    SetDItem(theDialog, theItem, theType, theHandle, &itemRect);
 
    theTEHandle = ((pStyleTextInfo) GetWRefCon(theDialog))->theTEHandle;
    (**theTEHandle).destRect.right  = (**theTEHandle).destRect.right  + deltaW;
    (**theTEHandle).destRect.bottom = (**theTEHandle).destRect.bottom + deltaH;
    (**theTEHandle).viewRect.right  = (**theTEHandle).viewRect.right  + deltaW;
    (**theTEHandle).viewRect.bottom = (**theTEHandle).viewRect.bottom + deltaH;
}
 
 
pascal Boolean  MyFilterProc( DialogPtr theDialog, EventRecord *myEvent, short *itemHit )
{
    short           myPart;
    WindowPtr       whichWindow;
    TEHandle        newTE;
    Boolean         extend;
    GrafPtr         oldPort;
    Point           mousePt;
    char            myKey;
    Boolean         returnVal;
    long            menuResult;
    Rect            teItemRect;
    Rect            scrollItemRect;
    Rect            growItemRect;
    ControlHandle   newScroll;
    ControlHandle   theControl;
    short           part;
    short           value;
    short           cntlCode;
    Rect            growBounds;
    long            newRectSize;
    short           deltaW;
    short           deltaH;
    SFTypeList      myTypes = {'TEXT'};
#ifdef THINK_C
    ModalFilterUPP  standardProc;
#endif
    
    GetPort(&oldPort);
    
    returnVal = false;
    
    newTE     = ((pStyleTextInfo)GetWRefCon(theDialog))->theTEHandle;
    newScroll = ((pStyleTextInfo)GetWRefCon(theDialog))->theScrollBar;
    
    TEIdle(newTE);
    
    // Maintain the cursor
    
    SetPort(theDialog);
    GetMouse(&mousePt);
    
    GetRectOfDialogItem(theDialog, kTextItem,  &teItemRect);
    GetRectOfDialogItem(theDialog, kScrollBar, &scrollItemRect);
    GetRectOfDialogItem(theDialog, kGrowBox, &growItemRect);
    
    if (PtInRect(mousePt, &teItemRect))
    SetCursor(&editCursor);
    else
    SetCursor(&qd.arrow);
    
    switch (myEvent->what)
    {
        case mouseDown:
            myPart = FindWindow(myEvent->where, &whichWindow);
            
            if ( ( whichWindow != theDialog ) && ( myPart != inMenuBar ) )
                break;
 
            switch (myPart)
            {
                case inContent:
                case inGrow:
                    SetPort(whichWindow);
                    mousePt = myEvent->where;
                    GlobalToLocal(&mousePt);
            
                    extend = ((myEvent->modifiers & shiftKey) != 0);
                    if ( PtInRect( mousePt, &(*(newTE))->viewRect ) )
                    {
                        TEClick(mousePt, extend, newTE);
                        TrackTEPosn(newScroll, newTE);
                    }
                    else
                    {
                        if (PtInRect(mousePt, &scrollItemRect))
                        {
                            cntlCode = FindControl(mousePt, theDialog, &theControl);
                            if (theControl)
                                if (cntlCode == inThumb)
                                {
                                    value = GetCtlValue(theControl);
                                    part  = TrackControl(theControl, mousePt, nil);
                                    if (part)
                                    {
                                        value -= GetCtlValue(theControl);
                                        if (value)
                                            TEScroll(0, value, newTE);
                                    }
                                }
                                else if ( cntlCode != 0 )
                                    part = TrackControl(theControl, mousePt, gScrollScriptVActionUPP);
                        }   // See if click is in grow box
                        else if ( PtInRect(mousePt, &growItemRect ))
                        {
        // Let the user drag out the new window size
                            SetRect(&growBounds, 300, 115,
                                                qd.screenBits.bounds.right,
                                                qd.screenBits.bounds.bottom);
                            newRectSize = GrowWindow ( whichWindow, myEvent->where,
                                                        &growBounds);
                            if (newRectSize)
                            {
        // Calculate the change in size of the window
                                SetPort(theDialog);
                                deltaW = LoWord(newRectSize) + whichWindow->portRect.left
                                            - whichWindow->portRect.right;
                                deltaH = HiWord(newRectSize) + whichWindow->portRect.top
                                            - whichWindow->portRect.bottom;
                                
        // Move all the items arround as appropriate
                                MoveOneDialogControlItem( theDialog, ok,         deltaW, deltaH );
                                MoveOneDialogControlItem( theDialog, cancel,     deltaW, deltaH );
                                MoveOneDialogControlItem( theDialog, kCompile,   deltaW, deltaH );
                                MoveOneDialogItem( theDialog, kGrowBox, deltaW, deltaH );
 
        // Move the scroll bar and Text edit around. Change the scroll bar's maximum
        //   and current value as necessary
                                MoveScrollBarItem( theDialog, kScrollBar, deltaW, deltaH );
                                MoveCustTextEditItem( theDialog, kTextItem, deltaW, deltaH );
                                SetScrollLimit( newScroll, newTE);
 
        // Resize the window, don't bother about the update because we're going it
        //     invalidate it all later
                                SizeWindow ( whichWindow,
                                             LoWord(newRectSize), HiWord(newRectSize),
                                             false );
 
        // Invalidate the window for update : Note that you have to add an erase rect
        //     to make sure that the window is cleared. This seems to be because the
        //     Dialog manager doesn't do one.
                
                                EraseRect( &whichWindow->portRect );
                                InvalRect( &whichWindow->portRect );
                            }
                            returnVal = true;
                        }
                    }
                    break;
            
                case inDrag:
                    DragWindow(whichWindow, myEvent->where, &qd.screenBits.bounds);
                    returnVal = true; // Stops modal dialog beeping
                    break;
            
                case inMenuBar:
                    menuResult = MenuSelect(myEvent->where);
                    DoDialogMenu( menuResult, newTE );
                    HiliteMenu( 0 );
                    returnVal = true;   // Always return handled to stop beeping
                    break;                                                                                      
            }
            break;
 
        case keyDown:
        case autoKey: // Need hooks here for command keys if menus enabled
            myKey = myEvent->message & charCodeMask;
            
            if ((myEvent->modifiers & cmdKey) == cmdKey)
            {
                menuResult = MenuKey(myKey);
                returnVal = DoDialogMenu( menuResult, newTE );
                HiliteMenu(0);
            }
            else if ( myKey != kEscapeKey ) // Escape Key
                if ( myKey == kEndOfTextKey )
                {
                    *itemHit = kCompile;
                    FlashButton(theDialog, kCompile);
                    returnVal = true;
                }
                else
                {
                    TEKey(myKey, newTE);
                    SetScrollLimit(newScroll, newTE);
                    TrackTEPosn(newScroll, newTE);
                    returnVal = true;
                }
            break;
                                
        case activateEvt:
            if (((myEvent->modifiers & activeFlag) != 0))
            {
                if ((WindowPtr)myEvent->message == theDialog)
                    TEActivate(newTE);
                else
                    DoActivate((WindowPtr)myEvent->message, true); // Should not get here!
            }
            else if ((WindowPtr)myEvent->message == theDialog)
                TEDeactivate(newTE);
            else
                DoActivate((WindowPtr)myEvent->message, false);
            break;
                                        
        case updateEvt:
            if (Ours((WindowPtr)myEvent->message))
                DoUpdate( (WindowPtr)myEvent->message );
            break;
    }
 
    if ( ! returnVal )
    {
        #ifdef THINK_C  // Can't use StdFilterProc() get and
                        //  call the standard filter ourselves
            if ( noErr == GetStdFilterProc( &standardProc ) ) 
                returnVal= ((ModalFilterUPP)standardProc)( theDialog, myEvent, itemHit );
        #else
            returnVal = StdFilterProc( theDialog, myEvent, itemHit );
        #endif
    }
    
    MaintainEditItems( newTE, 1, myTypes );
    SetPort( oldPort );
    
    return returnVal;
}
 
 
// Given a resource ID for a menu, along with a OSAID for a script
//  to be associated with it. This routine sets the menu or menu item
//  script property. This in turn sets that the script has changed
//  so that it will get saved when the application is quit.
 
OSErr   SetMenuScript( short theResID, OSAID theScriptID )
{
    MenuPropToken       aMenuPropToken;
    MenuItemPropToken   aMenuItemPropToken;
    AEDesc              aDesc = { typeNull, NULL };
    OSErr               anErr;
              
    anErr = GetScriptDesc( theScriptID, typeWildCard, &aDesc );
 
    if ( theResID % 32 )    // Then the script is for an item
    {
        MenuItemTokenFromResID( theResID, &aMenuItemPropToken.token );
        aMenuItemPropToken.tokenProperty = pScript;
 
        anErr = SetMenuItemTokenProperty( &aMenuItemPropToken, &aDesc );
    }
    else                    // The script is for the whole menu
    {
        MenuTokenFromResID( theResID, &aMenuPropToken.token );
        aMenuPropToken.tokenProperty = pScript;
 
        anErr = SetMenuTokenProperty( &aMenuPropToken, &aDesc );
    }
    
done:
    (void)AEDisposeDesc( &aDesc );
 
    return anErr;
}
 
void SetMenuForDialog ( void )
{
    Boolean redrawMenuBar = false;
    
// Unhilite the menu bar
    HiliteMenu( 0 );
 
// Gray out apple menu (This will allow us to get at the edit menu)
    redrawMenuBar |= SetMenuItemState ( false, myMenus[appleM], kMenuTitle);
 
// Now Gray out everything apart from the edit menu
    redrawMenuBar |= SetMenuItemState ( false,  myMenus[fileM], kMenuTitle);
    if ( CountDocuments( ) > 0 )
    {
        redrawMenuBar |= SetMenuItemState ( false, myMenus[fontM], kMenuTitle);
        redrawMenuBar |= SetMenuItemState ( false, myMenus[sizeM], kMenuTitle);
        redrawMenuBar |= SetMenuItemState ( false, myMenus[styleM], kMenuTitle);
        redrawMenuBar |= SetMenuItemState ( false, myMenus[scriptM], kMenuTitle);
        redrawMenuBar |= SetMenuItemState ( false, myMenus[subroutineM], kMenuTitle);
    }
 
// Enable the edit menu
    redrawMenuBar |= SetMenuItemState ( true, myMenus[editM], kMenuTitle);
    SetMenuItemState ( true, myMenus[editM], cutCommand);
    SetMenuItemState ( true, myMenus[editM], copyCommand);
    SetMenuItemState ( true, myMenus[editM], pasteCommand);
    SetMenuItemState ( true, myMenus[editM], clearCommand);
    SetMenuItemState ( true, myMenus[editM], selectAllCommand);
 
    if (redrawMenuBar)
        DrawMenuBar();
}
 
void ReSetMenuForDialog ( void )
{
    Boolean redrawMenuBar = false;
    
        // Reset the menus
    redrawMenuBar |= SetMenuItemState ( true, myMenus[appleM], kMenuTitle);
    redrawMenuBar |= SetMenuItemState ( true, myMenus[fileM], kMenuTitle);
    if ( CountDocuments( ) > 0 )
    {
        redrawMenuBar |= SetMenuItemState ( true, myMenus[fontM], kMenuTitle);
        redrawMenuBar |= SetMenuItemState ( true, myMenus[sizeM], kMenuTitle);
        redrawMenuBar |= SetMenuItemState ( true, myMenus[styleM], kMenuTitle);
        redrawMenuBar |= SetMenuItemState ( true, myMenus[scriptM], kMenuTitle);
        redrawMenuBar |= SetMenuItemState ( true, myMenus[subroutineM], kMenuTitle);
    }
    MaintainMenus( &redrawMenuBar);
}
 
 
OSErr   EditMenuScript(short theMenu, short theItem)
{
    short         resID;
    OSAID         myScriptID;
    OSErr         myErr;
    OSErr         ignoreErr;
    AEDesc        scriptTextDesc;
    DialogPtr     scriptEditDlog;
    short         itemHit;
    TEHandle      newTE;
    GrafPtr       oldPort;
    Rect          itemRect;
    OSAID         myCompiledID;
    Boolean       wholeMenu;
    ControlHandle myScrollBar;
    StyleTextInfo theStyleTextInfo;
    ModalFilterUPP modalFilterDescriptor;
 
    SetMenuForDialog( );
 
    gEditingScript = true;
 
    GetPort(&oldPort);
    
    //  Look for a script associated with theItem first,
    //  and if not found look for one associated with 
    //  the whole menu
    
    wholeMenu = false;
    
    resID = ( theMenu << 5 ) + theItem;
    
    myScriptID = GetScriptIDForResID( resID );
    
    if ( myScriptID == kOSANullScript )
    {
        resID = (theMenu<<5);
        myScriptID = GetScriptIDForResID(resID);
        wholeMenu = true;
    }
            
    scriptEditDlog = GetNewDialog( MenuScriptEditor, NULL, (WindowPtr)-1 );                  
    SetPort( scriptEditDlog );
 
    //  Create a styled text edit record for the userItem
    //  and install it
 
    GetRectOfDialogItem( scriptEditDlog, kTextItem,  &itemRect );
    
    InsetRect(&itemRect, 5, 5);
    
    newTE = TEStylNew(&itemRect, &itemRect);
    
    TEAutoView(true, newTE);
    
    SetDrawProcForUserItem(scriptEditDlog, kTextItem, gDrawStyledTextUPP);
    
    //  Now create a scrollbar and install it
    
    GetRectOfDialogItem(scriptEditDlog, kScrollBar, &itemRect);
    
    myScrollBar = NewControl(scriptEditDlog, &itemRect, (unsigned char *)"",
                                true, 0, 0, kMaxTELength, // Will be fixed up later - by SetScrollLimit()
                                 scrollBarProc, 0);
                                                     
    SetDrawProcForUserItem(scriptEditDlog, kScrollBar, gDrawScrollBarUPP);
 
    //  Now create grow box draw proc
    SetDrawProcForUserItem(scriptEditDlog, kGrowBox, gDrawGrowBoxUPP);
    
    //  Use the dialog window refcon to allow access to the 
    //  TEHandle and scrollHandle avoiding the use
    //  of globals
    
    theStyleTextInfo.theTEHandle  = newTE;
    theStyleTextInfo.theScrollBar = myScrollBar;
    
    SetWRefCon(scriptEditDlog, (long) &theStyleTextInfo);
 
    //  Put the source of the script into the 
    //  text edit record
    
    myErr = OSAGetSource(gScriptingComponent, myScriptID,
                                    typeStyledText, &scriptTextDesc);
 
    PutStyledTextFromDescIntoTEHandle(&scriptTextDesc, newTE);
    
    ignoreErr = AEDisposeDesc(&scriptTextDesc);
    
    SetScrollLimit(myScrollBar, newTE);
    TrackTEPosn(myScrollBar, newTE);
 
    // Set item 1 - <OK> to have a default box around it
    SetDialogDefaultItem(scriptEditDlog,ok);
    SetDialogCancelItem(scriptEditDlog,cancel);
    
    //  Added during PowerPC port:
    
    modalFilterDescriptor = NewModalFilterProc( MyFilterProc );
    
    do
    {
        ModalDialog(modalFilterDescriptor, &itemHit);
        switch ( itemHit )
        {
            case ok:
            case kCompile:
                SetCursor(&waitCursor);
                HLock((Handle)(**newTE).hText);
                myErr = AECreateDesc(typeChar, (Ptr)*((**newTE).hText),
                                        (**newTE).teLength, &scriptTextDesc);
                HUnlock((Handle)(**newTE).hText);
                                                
                myCompiledID = kOSANullScript;
                myErr = OSACompile(gScriptingComponent, &scriptTextDesc,
                                            kOSAModeCompileIntoContext, &myCompiledID);
                                                                                     
                ignoreErr = AEDisposeDesc(&scriptTextDesc);
                                                
                scriptTextDesc.dataHandle = nil;
                                                
                if (myErr==noErr)
                {
                    myErr = OSAGetSource(gScriptingComponent, myCompiledID,
                                                    typeStyledText, &scriptTextDesc);
                                                             
                        // Zap current contents and replace with returned source
                         
                    TESetSelect(0, kMaxTELength, newTE);
                    TEDelete(newTE);
 
                        // We deleted all the text so it won't be scrolled anymore
                    SetCtlValue(myScrollBar, 0);
                    
                    PutStyledTextFromDescIntoTEHandle(&scriptTextDesc, newTE);
                    
                    DrawStyledTextEditRec(scriptEditDlog, kTextItem);
                                                         
                    ignoreErr = AEDisposeDesc(&scriptTextDesc);
                }
                else if ( errOSAScriptError == myErr )
                {
                    DisplayErrorInScript( newTE );
                    itemHit  = 0; // Stop dialog going away
                }
                    
                SetScrollLimit(myScrollBar, newTE);
                TrackTEPosn(myScrollBar, newTE);
            
                SetCursor(&qd.arrow);
                    
                if ( ok == itemHit )
                {       // Swap this script for the old one even if whole menu 
                    if ( noErr == myErr )
                        SetMenuScript( resID, myCompiledID );
                    else
                        itemHit = -1;
                }
                                                        
                (void)OSADispose(gScriptingComponent, myCompiledID);
                break;
                                                
            case cancel: 
            case kTextItem: 
            case kScrollBar:
                break;
        }
    } while ( itemHit != ok && itemHit != cancel );
            
    DisposeRoutineDescriptor( modalFilterDescriptor );
            
    DisposeDialog( scriptEditDlog );
    
    SetPort(oldPort);
 
    gEditingScript = false;
 
    ReSetMenuForDialog( );
    
    return noErr;
}
 
 
OSAError    LoadDocumentScript( DPtr theDoc, short theFileRef )
{
    OSAError    err;
 
    theDoc->theScriptID = kOSANullScript;
    
    err = LoadScriptFromResFileRef( theFileRef,
                    kDefaultDocumentScript, &theDoc->theScriptID );
                    
    return err;
}
 
 
OSAError    StoreDocumentScript( DPtr theDoc, short theFileRef )
{
    OSAError    err;
    
    if ( kOSANullScript == theDoc->theScriptID )
        return noErr;
    
    err = StoreScriptToResFileRef( theFileRef, kDefaultDocumentScript,
                                theDoc->theScriptID, "\pDocument Script" );
                    
    return err;
}
 
 
OSAError    GetScriptProperty( OSAID contextID, const AEDesc* propertyName, AEDesc* result )
{
    OSAID       aScriptValueID = kOSANullScript;
    OSAError    err;
    
    if ( kOSANullScript == contextID )
        return errAEEventNotHandled;
    
                        // Get the property as a script ID
    err = OSAGetProperty( gScriptingComponent, kOSAModeNull, contextID,
                                        propertyName, &aScriptValueID );
    if (noErr != err) goto done;
                        // Convert the script data located by the script ID to an AEDesc
    err = OSACoerceToDesc( gScriptingComponent, aScriptValueID,
                                    typeWildCard, kOSAModeNull, result );
 
done:
    (void)OSADispose( gScriptingComponent, aScriptValueID );
 
    return(err);
}
 
 
// Set a script property using a descriptor.
 
OSAError    SetScriptProperty( OSAID contextID, const AEDesc* propertyName, const AEDesc* value )
{
    OSAID       aScriptValueID = kOSANullScript;
    OSAError    err;
                        // Convert from a descriptor to a script ID
    err = OSACoerceFromDesc( gScriptingComponent, value,
                                       kOSAModeNull, &aScriptValueID );
    if (noErr != err) goto done;
                        // Set the property using the script ID
    err = OSASetProperty( gScriptingComponent, kOSAModeNull,
                                contextID, propertyName, aScriptValueID );
 
done:
    (void)OSADispose( gScriptingComponent, aScriptValueID );
 
    return(err);
}
 
 
OSErr   GetScriptDesc( OSAID theScriptID, DescType theWantType, AEDesc* theResult )
{
    OSErr   err;
 
    if ( theWantType == typeChar || theWantType == typeIntlText )
    {   // If caller wants text, we need to de-compile the script
        err = (OSErr)OSAGetSource( gScriptingComponent, theScriptID, 
                                                        theWantType, theResult );
    }
    else
    {
        if ( theWantType == typeWildCard )
            theWantType = typeOSAGenericStorage;
            
        err = (OSErr)OSAStore( gScriptingComponent, theScriptID, 
                                theWantType, kOSAModeDontStoreParent, theResult );
    }
    
    return err;
}
 
 
OSErr   SetScriptDesc( const AEDesc* theData, OSAID* theResult )
{
    OSAID       aScriptID = kOSANullScript;
    OSAError    err;
 
    switch ( theData->descriptorType )
    {
        case typeChar:
        case typeIntlText:
            err = OSACompile( gScriptingComponent, theData, 
                                    kOSAModeCompileIntoContext, &aScriptID );
            if ( errOSAScriptError == err )
                DisplayOSAScriptError( NULL );
            break;
        
        default:
            err = OSALoad( gScriptingComponent, theData, 
                                    kOSAModeNull, &aScriptID );
    }
    
    if ( noErr == err )
    {
        (void)OSADispose( gScriptingComponent, *theResult );
        *theResult = aScriptID;
    }
 
    return (OSErr)err;
}
 
void    AddMissingParameter( AEKeyword theAEKeyword, DescType theType,
                                    Ptr theData, Size theDataSize, AppleEvent* theEvent )
{
    AEDesc  aDesc = { typeNull, NULL };
    OSErr   anErr;
    
        // See if the parameter is there first
    anErr = AEGetParamDesc( theEvent, theAEKeyword, typeWildCard, &aDesc );
    if ( noErr == anErr ) goto done;
    
    switch ( theType )
    {
        case typeAEList:
        case typeAERecord:
            anErr = AECreateList( NULL, 0, ( typeAERecord == theType ), &aDesc );
            if ( noErr != anErr ) goto done;
        
            anErr = AEPutParamDesc( theEvent, theAEKeyword, &aDesc );
            break;
            
        default:
            anErr = AECreateDesc( theType, theData, theDataSize, &aDesc );
            if ( noErr != anErr ) goto done;
        
            anErr = AEPutParamDesc( theEvent, theAEKeyword, &aDesc );
    }
 
done:   
    (void)AEDisposeDesc( &aDesc );
}
 
 
Boolean CanScriptEvent( AEEventClass theClass, AEEventID theID, AppleEvent* theEvent )
{
    DescType    aType;
    FSSpec      aSpec;
    Boolean     result;
    
    switch ( theClass )
    {
        case kCoreEventClass:
            switch ( theID )
            {
                case kAEOpenApplication:
                    result = false;
                    break;
                    
                default:
                    result = true;
            }
            break;
            
        case kAECoreSuite:
            switch ( theID )
            {
                case kAESetData:
                case kAEGetData:
                case kAEDoObjectsExist:
                    result = false;
                    break;
                    
                case kAECreateElement:
                    aType = typeNull;   // Null to show nothing
                    AddMissingParameter( keyAEInsertHere, typeType,
                                            (Ptr)&aType, sizeof( aType ), theEvent );
                    AddMissingParameter( keyAEData, typeAERecord, NULL, 0, theEvent );
                    AddMissingParameter( keyAEPropData, typeAERecord, NULL, 0, theEvent );
                    result = true;
                    break;
                    
                case kAEClose:
                    aType = kAEAsk;     // Default for no parameter
                    AddMissingParameter( keyAESaveOptions, typeEnumeration,
                                            (Ptr)&aType, sizeof( aType ), theEvent );
                    result = true;
                    break;
                    
                case kAESave:
                    aSpec.vRefNum = 0;  // Send a blank FSSpec
                    aSpec.parID = 0;
                    aSpec.name[0] = 0;
                    AddMissingParameter( keyAEFile, typeFSS,
                                            (Ptr)&aSpec, sizeof( aSpec ), theEvent );
                    result = true;
                    break;
                    
                default:
                    result = true;
            }
            break;
 
        case kAEMiscStandards:  // e.g. kAERevert
            switch ( theID )
            {
                default:
                    result = true;
            }
            break;
 
        case kASAppleScriptSuite:
            switch ( theID )
            {
                case kASSubroutineEvent:
                    result = true;
                    break;
                    
                default:
                    result = false;
            }
            break;
            
        default:
            result = false;
    }
    
    return result;
}
 
 
// Debugging routine
 
OSAError    GetAHandler( AEDesc* theName, OSAID theOSAID );
OSAError    GetAHandler( AEDesc* theName, OSAID theOSAID )
{
    AEDesc      aDesc = { typeNull, NULL };
    OSAID       aResultID;
    OSAError    anErr;
 
    (void)DisplayDescResult( GetResultsWindPtr( ), theName, NULL, noErr );
    
    anErr = OSAGetHandler( gScriptingComponent, kOSAModeNull, theOSAID, theName, &aResultID );
    if ( noErr != anErr ) goto done;
    
    anErr = OSAGetSource( gScriptingComponent, aResultID, typeStyledText, &aDesc );
    if ( noErr != anErr ) goto done;
 
done:
    (void)DisplayDescResult( GetResultsWindPtr( ), &aDesc, NULL, anErr );
    (void)AEDisposeDesc( &aDesc );
 
    return anErr;
}
 
// Debugging routine
 
OSErr   LookAtHandlers( OSAID theOSAID );
OSErr   LookAtHandlers( OSAID theOSAID )
{
    AEDesc      aList = { typeNull, NULL },
                aDesc = { typeNull, NULL };
    long        aCount,
                anIndex;
    AEKeyword   anAEKeyword;
    OSAError    anErr;
 
    anErr = OSAGetHandlerNames( gScriptingComponent, kOSAModeNull, theOSAID, &aList );
    if ( noErr != anErr ) goto done;
    
    anErr = AECountItems( &aList, &aCount );
    if ( noErr != anErr ) goto done;
 
    for ( anIndex = 1; anIndex <= aCount; anIndex++ )
    {
        anErr = AEGetNthDesc( &aList, anIndex, typeWildCard, &anAEKeyword, &aDesc );
        if ( noErr != anErr ) continue;
        
        anErr = GetAHandler( &aDesc, theOSAID );
    }
 
done:
    (void)DisplayDescResult( GetResultsWindPtr( ) , &aDesc, NULL, anErr );
    (void)AEDisposeDesc( &aList );
    (void)AEDisposeDesc( &aDesc );
 
    return anErr;
}
 
 
static OSErr    GetTokenDescScript( AEDesc* theTokenDesc, OSAID* theOSAID )
{
    WindowToken     aWindowToken;
    Size            aSize;
    DPtr            aDoc;
    OSErr           err = noErr;
    
    *theOSAID = kOSANullScript;     // Null to begin with
 
    switch ( theTokenDesc->descriptorType )
    {
        case typeMyDocument:
            GetRawDataFromDescriptor( theTokenDesc, (Ptr)&aWindowToken,
                                                sizeof( aWindowToken ), &aSize );
            aDoc = DPtrFromWindowPtr( aWindowToken.tokenWindow );
            *theOSAID = aDoc->theScriptID;
            
//          LookAtHandlers( *theOSAID );
            break;
            
        case typeMyAppl:
            *theOSAID = gAppRec.theScriptID;
 
//          LookAtHandlers( *theOSAID );
            break;
            
        default:
            err = errAEEventNotHandled;
    }
 
    return err;
}
 
 
OSErr   DoScriptEvent( AppleEvent           *theEvent, 
                        AppleEvent          *theReply, 
                        OSAID               theScriptID )
{
    OSAError            err = errAEEventNotHandled;
    AEEventHandlerUPP   oldResumeProc;
    long                oldRefcon;
    
    if ( kOSANullScript == theScriptID )
        return err;
    
    OSAGetResumeDispatchProc( gScriptingComponent, &oldResumeProc, &oldRefcon );
    
    err = OSASetResumeDispatchProc( gScriptingComponent,
                    (AEEventHandlerUPP)kOSAUseStandardDispatch, kOSADontUsePhac );
    if ( noErr == err )
    {
        err = OSADoEvent( gScriptingComponent, theEvent, theScriptID, 
                                                kOSAModeAlwaysInteract, theReply );
        if ( errOSAScriptError == err )
            (void)DisplayOSAScriptError( NULL );
    }
 
    OSASetResumeDispatchProc( gScriptingComponent, oldResumeProc, oldRefcon ) ;
    
    return  (OSErr)err;
}
 
 
pascal OSErr    EventPrehandler( AppleEvent *theEvent, AppleEvent *theReply, long theRefcon )
{
#ifdef __MWERKS__
    #pragma unused( theRefcon )
#endif
 
    AEEventClass        aClass;
    AEEventID           anID;
    DescType            aType;
    Size                aSize;
    AEDesc              anObject = { typeNull, NULL },
                        aTokenDesc = { typeNull, NULL };
    OSAID               aScriptID = kOSANullScript;
    OSAError            err;
 
    // Extract the class and ID of the event from the AppleEvent
    err = AEGetAttributePtr( theEvent, keyEventClassAttr, typeType,
                                    &aType, (Ptr)&aClass, sizeof( aClass ), &aSize ); 
    if ( noErr != err ) goto done;
 
    err = AEGetAttributePtr( theEvent, keyEventIDAttr, typeType,
                                    &aType, (Ptr)&anID, sizeof( anID ), &aSize );
    if ( noErr != err ) goto done;
    
    if ( CanScriptEvent( aClass, anID, theEvent ) )
    {       // Above test is to make sure we handle all the scriptable events 
            //  and that we skip the ones we don't want to be scriptable.
        err = AEGetParamDesc( theEvent, keyDirectObject, typeWildCard, &anObject );
 
            // If there is no object specifier for the direct object
            //  then well have a go with the application script.
        if ( noErr != err || typeObjectSpecifier != anObject.descriptorType )
            aTokenDesc.descriptorType = typeMyAppl;
        else
        {   // Resolve to an internal token
            err = AEResolve( &anObject, kAEIDoMinimum, &aTokenDesc );
            if ( noErr != err ) goto done;
        }
    
        err = GetTokenDescScript( &aTokenDesc, &aScriptID );
        if ( noErr != err ) goto done;
        
            // If there's no script then we want the application
            //  to handle it in the normal way.
        if ( kOSANullScript == aScriptID )
        {
            err = errAEEventNotHandled;
            goto done;
        }
        
        err = DoScriptEvent( theEvent, theReply, aScriptID );
    }
    else
        err = errAEEventNotHandled;
 
done:
    (void)AEDisposeDesc( &anObject );
    (void)AEDisposeDesc( &aTokenDesc );
        
    return (OSErr)err;
}