Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
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; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14