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.
main.c
/* |
File: main.c |
Abstract: A skeleton of modern nib-based and Carbon Events-based Carbon application. |
AppleScriptRunner contains a folder within its application bundle, "AppleScripts" |
which contains a number of AppleScripts. AppleScriptRunner builds a popup menu |
populated with the AppleScript names. When "Execute" is clicked, the selected |
AppleScript is executed passing it one Text parameter, the contents of out HITextView. |
In this example all AppleScripts must take one, and only one, text parameter. An |
advisable method of adding functionallity to an application to send email is to |
create an AppleScript to send email, and then envoke the script from the application. |
In this example "Entourage" and "Mail" each send email, provided the email clients are |
correctly configured. The format of the HITextView to send mail should be: |
"Santa@NorthPole.com Dear Santa, My chimney ..." |
Because this sample requires only one input parameter, the AppleScrips themselves parse |
the "To" email address out as the first word, and assume the rest is the message body. |
The main entry to the "interesting" portion of the code is the routine: RunAppleScript(). |
Version: 1.0 |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under Apple's |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Copyright © 2005 Apple Computer, Inc., All Rights Reserved |
*/ |
#include <Carbon/Carbon.h> |
#include <sys/param.h> |
struct GlobalAppInfo // Application globals |
{ |
IBNibRef mainNibRef; |
}; |
typedef struct GlobalAppInfo GlobalAppInfo; |
static void SendCommandProcessEvent( UInt32 commandID ); |
static OSStatus GetControlBySigAndID( WindowRef window, OSType signature, SInt32 id, ControlRef *control ); |
static void InstallAppleEventHandlers( void ); |
static void DoPreferences( void ); |
static OSStatus DoSomething(); |
static OSStatus NavOpenDocument(); |
static pascal Boolean NavOpenFilterProc( AEDesc *theItem, void *info, NavCallBackUserData callBackUD, NavFilterModes filterMode ); |
static OSStatus HIViewFindBySigAndID( HIViewRef inStartView, OSType signature, SInt32 id, HIViewRef *outControl ); |
static TXNObject GetTXNObjectBySigAndID( WindowRef window, OSType signature, SInt32 id ); |
static void Wprintf( WindowRef window, char const *fmt, ... ); |
static OSStatus DisplayTextFileInWindow( WindowRef window, FSRef *fsRef ); |
static pascal OSStatus CommandProcessEventHandler( EventHandlerCallRef nextHandler, EventRef inEvent, void *userData ); // Added kHICommandRevert, kHICommandPageSetup, and kHICommandPrint |
static OSStatus OpenDocuments( AEDescList docList ); // Open Text files |
static OSStatus RunAppleScript( FSRef *scriptFSRef, char *textParameter ); |
static OSStatus GetAppleScriptsFolderFSRef( FSRef *fsRef); |
static OSStatus CreateMessageEvent( AppleEvent *theEvent, char *parameter ); |
static pascal OSStatus MainWindowEventHandler( EventHandlerCallRef nextHandler, EventRef inEvent, void *userData ); |
static OSStatus DoNewWindow( WindowRef *window ); // Install Event Handler for Text view |
GlobalAppInfo g; |
#pragma mark - |
#pragma mark ¥ Main ¥ |
int main( int argc, char *argv[] ) |
{ |
OSStatus status; |
long response; |
const EventTypeSpec commandProcessEvents[] = { { kEventClassCommand, kEventCommandProcess } }; |
BlockZero( &g, sizeof(g) ); |
status = Gestalt( gestaltSystemVersion, &response ); |
if ( ! ((status == noErr) && (response >= 0x00001030)) ) // We require Mac OS X 10.3 or greater to run |
{ |
DialogRef alertDialod; |
CreateStandardAlert( kAlertStopAlert, CFSTR("Mac OS X 10.3 (minimum) is required for this application"), NULL, NULL, &alertDialod ); |
RunStandardAlert( alertDialod, NULL, NULL ); |
ExitToShell(); |
} |
// Create a Nib reference passing the name of the nib file (without the .nib extension) CreateNibReference only searches into the application bundle. |
status = CreateNibReference( CFSTR("main"), &g.mainNibRef ); |
require_noerr( status, CantGetNibRef ); |
// Once the nib reference is created, set the menu bar. "MainMenu" is the name of the menu bar object. This name is set in InterfaceBuilder when the nib is created. |
status = SetMenuBarFromNib( g.mainNibRef, CFSTR("MenuBar") ); |
require_noerr( status, CantSetMenuBar ); |
EnableMenuCommand( NULL, kHICommandPreferences ); // Enable Preferences menu item |
InstallAppleEventHandlers(); |
InstallApplicationEventHandler( NewEventHandlerUPP(CommandProcessEventHandler), GetEventTypeCount(commandProcessEvents), commandProcessEvents, NULL, NULL ); |
RunApplicationEventLoop(); // Call the event loop |
CantSetMenuBar: |
CantGetNibRef: |
return( status ); |
} |
#pragma mark - |
#pragma mark ¥ AppleEvent Handlers ¥ |
static pascal OSErr HandleAppleEventOapp( const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) |
{ |
SendCommandProcessEvent( kHICommandNew ); // Instantiate an empty window at the beginning so the User sees something |
return( noErr ); |
} |
static pascal OSErr HandleAppleEventRapp( const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) |
{ |
WindowRef window = GetFrontWindowOfClass( kDocumentWindowClass, true ); |
if ( window == NULL ) |
SendCommandProcessEvent( kHICommandNew ); // We were already running but with no windows so we create an empty one. |
return( noErr ); |
} |
static pascal OSErr HandleAppleEventOdoc( const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) |
{ |
AEDescList docList; |
OSErr err = AEGetParamDesc( theAppleEvent, keyDirectObject, typeAEList, &docList ); |
require_noerr( err, CantGetDocList ); |
err = OpenDocuments( docList ); |
AEDisposeDesc( &docList ); |
CantGetDocList: |
return( err ); |
} |
static pascal OSErr HandleAppleEventPdoc( const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) |
{ |
return( errAEEventNotHandled ); |
} |
static void InstallAppleEventHandlers( void ) |
{ |
OSErr status; |
status = AEInstallEventHandler( kCoreEventClass, kAEOpenApplication, NewAEEventHandlerUPP(HandleAppleEventOapp), 0, false ); require_noerr( status, CantInstallAppleEventHandler ); |
status = AEInstallEventHandler( kCoreEventClass, kAEReopenApplication, NewAEEventHandlerUPP(HandleAppleEventRapp), 0, false ); require_noerr( status, CantInstallAppleEventHandler ); |
status = AEInstallEventHandler( kCoreEventClass, kAEOpenDocuments, NewAEEventHandlerUPP(HandleAppleEventOdoc), 0, false ); require_noerr( status, CantInstallAppleEventHandler ); |
status = AEInstallEventHandler( kCoreEventClass, kAEPrintDocuments, NewAEEventHandlerUPP(HandleAppleEventPdoc), 0, false ); require_noerr( status, CantInstallAppleEventHandler ); |
// Note: Since RunApplicationEventLoop installs a Quit AE Handler, there is no need to do it here. |
CantInstallAppleEventHandler: |
return; |
} |
#pragma mark - |
#pragma mark ¥ CarbonEvent Handlers ¥ |
static pascal OSStatus CommandProcessEventHandler( EventHandlerCallRef nextHandler, EventRef inEvent, void *userData ) |
{ |
HICommand command; |
UInt32 secs; |
DateTimeRec date; |
OSStatus status = eventNotHandledErr; |
TXNObject txnObject = GetTXNObjectBySigAndID( GetFrontWindowOfClass(kDocumentWindowClass, true), 'HITv', 0 ); |
GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &command ); |
switch ( command.commandID ) |
{ |
case kHICommandNew: |
status = DoNewWindow( NULL ); |
GetDateTime( &secs ); // Demonstrate how to use utility routine Wprintf |
SecondsToDate( secs, &date ); |
Wprintf( GetFrontWindowOfClass(kDocumentWindowClass, true), "Todays Date: %d/%d/%d\n", date.month, date.day, date.year ); |
break; |
case kHICommandOpen: |
status = NavOpenDocument(); |
break; |
case kHICommandPreferences: |
DoPreferences(); |
break; |
case kHICommandRevert: |
if ( txnObject != NULL ) status = TXNRevert( txnObject ); |
break; |
case kHICommandPageSetup: |
if ( txnObject != NULL ) status = TXNPageSetup( txnObject ); |
status = noErr; |
break; |
case kHICommandPrint: |
if ( txnObject != NULL ) status = TXNPrint( txnObject ); |
status = noErr; |
break; |
case 'DoIt': // Our own menu to hook in and Do Something... |
status = DoSomething(); |
break; |
} |
return( status ); |
} |
static pascal OSStatus MainWindowEventHandler( EventHandlerCallRef nextHandler, EventRef inEvent, void *inUserData ) |
{ |
#pragma unused ( inCallRef ) |
HICommand command; |
OSStatus status = eventNotHandledErr; |
UInt32 eventKind = GetEventKind( inEvent ); |
UInt32 eventClass = GetEventClass( inEvent ); |
WindowRef window = (WindowRef) inUserData; |
switch ( eventClass ) |
{ |
case kEventClassWindow: |
//if ( eventKind == kEventWindowClose ) // Dispose extra window storage here |
break; |
case kEventClassCommand: |
if ( eventKind == kEventCommandProcess ) |
{ |
GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &command ); |
if ( command.commandID == kHICommandOK ) // OK (Execute) Button was clicked in window |
{ |
FSRef parentFSRef; |
FSRef fsRef; |
TXNObject txnObject; |
CFStringRef cfString; |
HFSUniStr255 appleScriptName; |
ControlRef control; |
SInt32 controlValue; |
MenuRef menuRef; |
Handle txnDataHandle = NULL; |
// Get the text to pass as a parameter to the AppleScript |
txnObject = GetTXNObjectBySigAndID( window, 'HITv', 0 ); // Get the TXNObject |
status = TXNGetDataEncoded( txnObject, kTXNStartOffset, kTXNEndOffset, &txnDataHandle, kTXNTextData ); // Retrieve the text. kTXNTextData specifies Text, not Unicode or Rich Text |
// Find the name of the chosen menu item, which is the name of our AppleScript to run |
status = GetControlBySigAndID( window, 'PopM', 0, &control ); // Get the popup menu control |
controlValue = GetControl32BitValue( control ); // Get the control value, which is the selected menu item |
menuRef = GetControlPopupMenuHandle( control ); // Get the MenuRef from the control |
CopyMenuItemTextAsCFString( menuRef, controlValue, &cfString ); // Copy the selected item text into a CFString |
// Convert our CFString to an HFSUniStr255 and create an FSRef to our chosen AppleScript |
appleScriptName.length = CFStringGetBytes( cfString, CFRangeMake(0, MIN(CFStringGetLength(cfString), 255)), kCFStringEncodingUnicode, 0, false, (UInt8 *)(appleScriptName.unicode), 255, NULL ); |
status = GetAppleScriptsFolderFSRef( &parentFSRef ); // Get FSRef to AppleScripts folder |
status = FSMakeFSRefUnicode( &parentFSRef, appleScriptName.length, appleScriptName.unicode, kTextEncodingUnknown, &fsRef ); // FSMakeFSRefUnicode to make FSRef to the selected AppleScript |
// Run the AppleScript passing in our text as a parameter |
status = RunAppleScript( &fsRef, *txnDataHandle ); |
// Release what we have allocated |
if ( cfString != NULL ) CFRelease( cfString ); |
if ( txnDataHandle != NULL ) DisposeHandle( txnDataHandle ); |
} |
} |
break; |
} |
return( status ); |
} |
#pragma mark - |
#pragma mark ¥ Windows ¥ |
static void DoPreferences( void ) |
{ // Entry point for a preferences window |
DialogRef theAlert; |
CreateStandardAlert( kAlertStopAlert, CFSTR("No Preferences yet!"), NULL, NULL, &theAlert ); |
RunStandardAlert( theAlert, NULL, NULL ); |
} |
static OSStatus DoNewWindow( WindowRef *outWindow ) |
{ |
OSStatus status; |
static EventHandlerUPP windowEventHandlerUPP; |
const EventTypeSpec windowEvents[] = { { kEventClassCommand, kEventCommandProcess }, { kEventClassWindow, kEventWindowClose } }; |
WindowRef window = NULL; |
// Create a window. "MainWindow" is the name of the window object. This name is set in InterfaceBuilder when the nib is created. |
status = CreateWindowFromNib( g.mainNibRef, CFSTR("MainWindow"), &window ); |
require_noerr( status, CantCreateWindow ); |
if ( windowEventHandlerUPP == NULL ) windowEventHandlerUPP = NewEventHandlerUPP( MainWindowEventHandler ); |
status = InstallWindowEventHandler( window, windowEventHandlerUPP, GetEventTypeCount(windowEvents), windowEvents, window, NULL ); |
require_noerr( status, CantInstallWindowEventHandler ); |
{ |
FSIterator iterator; |
ItemCount actualObjects; |
FSRef fsRefs[32]; // Allow up to 32 FSRefs, in this case 32 AppleScripts |
FSRef fsRef; |
CFStringRef cfString; |
ControlRef control; |
int i; |
MenuRef menuRef; |
status = GetAppleScriptsFolderFSRef( &fsRef ); // Locate our AppleScripts folder within our application bundle |
status = FSOpenIterator( &fsRef, kFSIterateFlat, &iterator ); // Read the first 32 items of the "AppleScripts" folder using FSOpenIterator-FSGetCatalogInfoBulk-FSCloseIterator |
require_noerr( status, FileSystemError ); |
status = FSGetCatalogInfoBulk( iterator, 32, &actualObjects, NULL, 0, NULL, fsRefs, NULL, NULL ); |
status = FSCloseIterator( iterator ); |
if ( actualObjects > 0 ) // If we found some items |
{ |
status = GetControlBySigAndID( window, 'PopM', 0, &control ); // Get the popup menu control |
menuRef = GetControlPopupMenuHandle( control ); // Get the MenuRef from the control |
SetControl32BitMaximum( control, actualObjects ); // Need to SetControl32BitMaximum for popups to function as expected |
DeleteMenuItem( menuRef, 1 ); // Delete the first menuItem titled: "No AppleScripts Found" |
for ( i = 0 ; i < actualObjects ; i++ ) // Itterate over the files we found |
{ |
status = LSCopyDisplayNameForRef( &fsRefs[i], &cfString ); // Get the name of the file the Finder displays (may not include file extension) |
CFShow( cfString ); |
if ( (status == noErr) && ( ( CFStringHasPrefix( cfString, CFSTR(".") ) ) == false ) ) // Don't add ".DS_Store", or any other file starting with a "." |
AppendMenuItemTextWithCFString( menuRef, cfString, 0, 0, NULL ); // Append the CFString of the AppleScript to out popup menu |
} |
} |
} |
// The window was created hidden so show it if the window parameter is NULL, if it's not, it will be the responsibility of the caller to show it. |
if ( outWindow == NULL ) ShowWindow( window ); |
SetWindowModified( window, false ); |
FileSystemError: |
CantInstallWindowEventHandler: |
CantCreateWindow: |
if ( outWindow != NULL ) *outWindow = window; |
return( status ); |
} |
#pragma mark - |
#pragma mark ¥ Save/Open Document ¥ |
static OSStatus NavOpenDocument() |
{ |
OSStatus status; |
NavDialogCreationOptions options; |
NavReplyRecord navReply; |
static NavObjectFilterUPP navFilterUPP; |
NavDialogRef navDialog = NULL; |
status = NavGetDefaultDialogCreationOptions( &options ); |
require_noerr( status, CantGetDefaultOptions ); |
if ( navFilterUPP == NULL ) navFilterUPP = NewNavObjectFilterUPP( NavOpenFilterProc ); // Filter only documents we can open |
status = NavCreateChooseFileDialog( &options, NULL, NULL, NULL, navFilterUPP, NULL, &navDialog ); |
require_noerr( status, CantCreateDialog ); |
status = NavDialogRun( navDialog ); |
require_noerr( status, CantRunDialog ); |
status = NavDialogGetReply( navDialog, &navReply ); |
require( (status == noErr) || (status == userCanceledErr), CantGetReply ); |
if ( navReply.validRecord ) status = OpenDocuments( navReply.selection ); |
else status = userCanceledErr; |
NavDisposeReply( &navReply ); |
CantGetReply: |
CantRunDialog: |
if ( navDialog != NULL ) NavDialogDispose( navDialog ); |
CantCreateDialog: |
CantGetDefaultOptions: |
return( status ); |
} |
// Generic Navigation Services filter proc described in TechNote 2017, "Using Launch Services" |
// This filter querries LaunchServices to ask what types of documents this application can open. |
// Openable document types are defined within the applications info.plist file. |
static pascal Boolean NavOpenFilterProc( AEDesc *theItem, void *info, NavCallBackUserData callBackUD, NavFilterModes filterMode ) |
{ |
LSItemInfoRecord lsInfoRec; |
FSRef fsRef; |
OSStatus status; |
static Boolean applicationFSRefInitialized; |
static FSRef applicationFSRef; |
Boolean canViewItem = false; |
if ( theItem->descriptorType == typeFSRef ) |
{ |
status = AEGetDescData( theItem, &fsRef, sizeof(fsRef) ); |
require_noerr( status, CantGetFSRef ); |
status = LSCopyItemInfoForRef( &fsRef, kLSRequestAllInfo, &lsInfoRec ); // Ask LaunchServices for information about the item |
require( (status == noErr) || (status == kLSApplicationNotFoundErr), LaunchServicesError ); |
if ( applicationFSRefInitialized == false ) // First time this routine ir run, we locate our application bundle |
{ |
ProcessSerialNumber psn = { 0, kCurrentProcess }; |
GetProcessBundleLocation( &psn, &applicationFSRef ); // Save the reference in a static |
applicationFSRefInitialized = true; |
} |
if ( (lsInfoRec.flags & kLSItemInfoIsContainer) != 0 ) // If it's a folder, make it selectable |
canViewItem = true; |
else |
status = LSCanRefAcceptItem( &fsRef, &applicationFSRef, kLSRolesViewer, kLSAcceptDefault, &canViewItem ); // Can this application "view" this file? |
} |
LaunchServicesError: |
CantGetFSRef: |
return( canViewItem ); |
} |
static OSStatus OpenDocuments( AEDescList docList ) |
{ |
long index; |
FSRef fsRef; |
CFStringRef fileName; |
WindowRef window = NULL; |
long count = 0; |
OSStatus status = AECountItems( &docList, &count ); |
require_noerr( status, CantGetCount ); |
for( index = 1; index <= count; index++ ) |
{ |
if ( (status = AEGetNthPtr( &docList, index, typeFSRef, NULL, NULL, &fsRef, sizeof(FSRef), NULL) ) == noErr ) |
{ |
status = DoNewWindow( &window ); // Create a new (Hidden) window |
require_noerr( status, CantCreateWindow ); |
status = LSCopyDisplayNameForRef( &fsRef, &fileName ); // Get the name of the file the Finder displays (may not include file extension) |
require_noerr( status, CantGetName ); |
SetWindowTitleWithCFString( window, fileName ); // Set the Window title |
status = DisplayTextFileInWindow( window, &fsRef ); |
if ( status == noErr ) |
ShowWindow( window ); // The window was created hidden so show it |
else |
DisposeWindow( window ); |
} |
} |
CantGetName: |
CantCreateWindow: |
CantGetCount: |
return( status ); |
} |
#pragma mark - |
#pragma mark ¥ Utilities ¥ |
// Utility routine to synchronously send a basic "kEventClassCommand / kEventCommandProcess" CarbonEvent to the application target |
static void SendCommandProcessEvent( UInt32 commandID ) |
{ |
HICommand command; |
EventRef event; |
BlockZero( &command, sizeof(command) ); |
command.commandID = commandID; |
(void) CreateEvent( NULL, kEventClassCommand, kEventCommandProcess, GetCurrentEventTime(), kEventAttributeUserEvent, &event ); |
(void) SetEventParameter( event, kEventParamDirectObject, typeHICommand, sizeof(command), &command ); |
(void) SendEventToApplication( event ); |
(void) ReleaseEvent( event ); |
} |
static OSStatus GetControlBySigAndID( WindowRef window, OSType signature, SInt32 id, ControlRef *control ) |
{ |
ControlID controlID = { signature, id }; |
return( GetControlByID( window, &controlID, control ) ); |
} |
static OSStatus HIViewFindBySigAndID( HIViewRef inStartView, OSType signature, SInt32 id, HIViewRef *outControl ) |
{ |
HIViewID hiViewID = { signature, id }; |
return( HIViewFindByID( inStartView, hiViewID, outControl ) ); |
} |
static TXNObject GetTXNObjectBySigAndID( WindowRef window, OSType signature, SInt32 id ) |
{ |
HIViewRef hiTextView; |
TXNObject txnObject = NULL; |
if ( window != NULL ) |
if ( HIViewFindBySigAndID( HIViewGetRoot(window), signature, id, &hiTextView ) == noErr ) |
if ( hiTextView != NULL ) |
txnObject = HITextViewGetTXNObject( hiTextView ); |
return( txnObject ); |
} |
/***************************************************** |
* |
* ExecuteCompiledAppleScriptEvent( AEDesc *scriptData, AppleEvent *theEvent, AEDesc *resultData ) |
* |
* Purpose: Generic routine to execute our AppleScriptEvent, passing parameters to an |
* AppleScript running inside my application |
* |
* Notes: http://developer.apple.com/qa/qa2001/qa1111.html |
* |
* Inputs: scriptData - Reference to the AppleScript to be executed |
* theEvent - text parameter to our AppleScript as an AppleEvent |
* resultData - result from script |
* |
* Returns: OSStatus - error code (0 == no error) |
*/ |
OSStatus ExecuteCompiledAppleScriptEvent( AEDesc *scriptData, AppleEvent *theEvent, AEDesc *resultData ) |
{ |
OSStatus err; |
ComponentInstance theComponent = NULL; |
OSAID contextID = kOSANullScript; |
OSAID resultID = kOSANullScript; |
theComponent = OpenDefaultComponent( kOSAComponentType, typeAppleScript ); // Open the scripting component |
if ( theComponent == NULL ) { err = paramErr; goto Bail; } |
err = OSALoad( theComponent, scriptData, kOSAModeNull, &contextID ); // Compile the script into a new context |
require_noerr( err, Bail ); |
err = OSAExecuteEvent( theComponent, theEvent, contextID, kOSAModeNull, &resultID ); // Run the script |
if ( resultData != NULL ) // Collect the results - if any |
{ |
AECreateDesc( typeNull, NULL, 0, resultData ); |
if ( err == errOSAScriptError ) |
OSAScriptError( theComponent, kOSAErrorMessage, typeChar, resultData ); |
else if ( (err == noErr) && (resultID != kOSANullScript) ) |
OSADisplay(theComponent, resultID, typeChar, kOSAModeNull, resultData); |
} |
Bail: |
if ( contextID != kOSANullScript ) OSADispose( theComponent, contextID ); |
if ( resultID != kOSANullScript ) OSADispose( theComponent, resultID ); |
if ( theComponent != NULL ) CloseComponent( theComponent ); |
return( err ); |
} |
#pragma mark - |
#pragma mark ¥ Specialized Routines ¥ |
static OSStatus DoSomething() |
{ |
DebugStr( "\pDoSomething Was Called\n" ); |
return( noErr ); |
} |
// This utility routine takes printf style arguments and puts the resulting text in the HITextView ( 'HITv', 0 ) within the passed in window. |
static void Wprintf( WindowRef window, char const *fmt, ... ) |
{ |
char s[4096]; |
va_list args; |
TXNObject txnObject; |
if ( window == NULL ) return; |
va_start( args, fmt ); |
(void) vsprintf( s, fmt, args ); |
va_end( args ); |
txnObject = GetTXNObjectBySigAndID( window, 'HITv', 0 ); |
TXNSetData( txnObject, kTXNTextData, s, strlen(s), kTXNUseCurrentSelection, kTXNUseCurrentSelection ); |
return; |
} |
// Return the FSRef to the AppleScripts directory within our bundle |
static OSStatus GetAppleScriptsFolderFSRef( FSRef *fsRef) |
{ |
Boolean successful = false; |
CFURLRef urlRef1 = NULL; |
CFURLRef urlRef2 = NULL; |
urlRef1 = CFBundleCopyResourcesDirectoryURL( CFBundleGetMainBundle() ); // Get URL to our bundles "Resources" directory |
require( urlRef1 != NULL , Bail ); |
urlRef2 = CFURLCreateCopyAppendingPathComponent( NULL, urlRef1, CFSTR("AppleScripts"), true ); // Append "AppleScripts" onto the URL, location of our AppleScripts |
require( urlRef1 != NULL , Bail ); |
successful = CFURLGetFSRef( urlRef2, fsRef ); // Convert the CFURL into an FSRef to be returned |
if ( urlRef1 != NULL ) CFRelease( urlRef1 ); |
if ( urlRef2 != NULL ) CFRelease( urlRef2 ); |
Bail: |
return( successful == false ); |
} |
static OSStatus DisplayTextFileInWindow( WindowRef window, FSRef *fsRef ) |
{ |
OSStatus status; |
TXNObject txnObject; |
FSSpec fsSpec; |
CFURLRef cfURL = NULL; |
require( (fsRef != NULL) && (window != NULL), Bail ); |
txnObject = GetTXNObjectBySigAndID( window, 'HITv', 0 ); |
require( txnObject != NULL, CantGetTXNObject ); |
cfURL = CFURLCreateFromFSRef( NULL, fsRef ); // Create a CFURL from the passed in FSRef |
require( cfURL != NULL, CantCreateURL ); |
status = TXNSetDataFromCFURLRef( txnObject, cfURL, kTXNStartOffset, kTXNEndOffset ); // url will be stored by MLTE |
require_noerr( status, CantSetTXN ); |
status = FSGetCatalogInfo( fsRef, kFSCatInfoNone, NULL, NULL, &fsSpec, NULL ); // Need the FSSpec, since SetWindowProxy requires it |
require_noerr( status, CantGetFSSpec ); |
SetWindowProxyFSSpec( window, &fsSpec ); |
// The following lines deal with current shortcomings of the HITextView |
TXNRevert( txnObject ); // The change count is not set to 0 as it should, a call to TXNRevert workarounds the problem |
TXNSetSelection( txnObject, kTXNEndOffset, kTXNEndOffset ); |
TXNShowSelection( txnObject, true ); // The HIScrollView scroll bars don't reflect the current size of the HITextView, a couple of calls to Set/Show selection workaround the problem |
TXNSetSelection( txnObject, kTXNStartOffset, kTXNStartOffset ); |
TXNShowSelection( txnObject, true ); // The HIScrollView scroll bars don't reflect the current size of the HITextView, a couple of calls to Set/Show selection workaround the problem |
CantGetFSSpec: |
CantSetTXN: |
CantCreateURL: |
if ( cfURL != NULL ) CFRelease( cfURL ); |
CantGetTXNObject: |
Bail: |
return( status ); |
} |
/***************************************************** |
* |
* RunAppleScript( FSRef *scriptFSRef, char *textParameter ) |
* |
* Purpose: Runs an AppleScript with one text parameter as input. |
* CreateMessageEvent, and therefore RunAppleScript, assumes the AppleScript has a |
* subroutine entry titled "applescriptentry" and accepts one TEXT parameter. |
* |
* Inputs: scriptFSRef - FSRef to our AppleScript |
* textParameter - text parameter to our AppleScript |
* |
* Returns: OSStatus - error code (0 == no error) |
*/ |
static OSStatus RunAppleScript( FSRef *scriptFSRef, char *textParameter ) |
{ |
OSStatus err; |
AppleEvent aeParameter; |
AEDesc scriptData; |
short refNum; |
FSCatalogInfo catalogInfo; |
Handle h = NULL; |
refNum = FSOpenResFile( scriptFSRef, fsRdPerm ); // Older (Mac OS 8/9) scripts store their data in the 'scpt' (1) resource |
if ( refNum != -1 ) |
{ |
h = Get1IndResource( 'scpt', 1 ); |
if( h != NULL ) DetachResource( h ); // Detach the handle before closing the resource |
CloseResFile( refNum ); |
} |
if ( h == NULL ) |
{ |
err = FSGetCatalogInfo( scriptFSRef, kFSCatInfoDataSizes, &catalogInfo, NULL, NULL, NULL ); // Get the size of the script |
require_noerr( err, Bail ); |
err = FSOpenFork( scriptFSRef, 0, NULL, fsRdPerm, &refNum ); // Open the data fork read only |
require_noerr( err, Bail ); |
h = NewHandle( catalogInfo.dataLogicalSize ); |
err = FSReadFork( refNum, fsFromStart, 0, catalogInfo.dataLogicalSize, *h, NULL ); // Read the script into our handle |
(void) FSCloseFork( refNum ); // Close the file reference |
} |
err = CreateMessageEvent( &aeParameter, textParameter ); // Create the AppleEvent, and use the Apple event to call the script's subroutine |
require_noerr( err, Bail ); |
err = AECreateDesc( typeOSAGenericStorage, *h, GetHandleSize(h), &scriptData ); // Load the compiled script into an AEDesc of type typeOSAGenericStorage |
require_noerr( err, Bail ); |
err = ExecuteCompiledAppleScriptEvent( &scriptData, &aeParameter, NULL ); // "Generic" routine to execute our AppleScript |
Bail: |
if ( h != NULL ) DisposeHandle( h ); |
return( err ); |
} |
// http://developer.apple.com/qa/qa2001/qa1111.html |
// Creates an AppleEvent with one text parameter. We leave it up to the AppleScript |
// to further parse the text parameter into potentially more parameters. |
static OSStatus CreateMessageEvent( AppleEvent *theEvent, char *parameter ) |
{ |
OSStatus err; |
ProcessSerialNumber psn = {0, kCurrentProcess}; |
err = AEBuildAppleEvent( 'ascr', kASSubroutineEvent, typeProcessSerialNumber, (Ptr) &psn, sizeof(psn), kAutoGenerateReturnID, kAnyTransactionID, |
theEvent, |
NULL, |
"'----':[TEXT(@)]," // One TEXT pointer parameter |
"'snam':TEXT(@)", // The keyASSubroutineName ('snam') parameter must contain the name of the subroutine that is being called with every letter converted to lowercase. For example, if name of the subroutine in your script is "GetDocumentSize", then the string provided in the keyASSubroutineName parameter should be "getdocumentsize". |
parameter, "applescriptentry"); // The entry routine whithin the AppleScript |
return( err ); |
} |
Copyright © 2006 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2006-07-31