Extend AppleScript
Volume Number: 10
Issue Number: 1
Column Tag: Scripting
Extending AppleScript™ 
With Scripting Additions
Not everything is handled in vanilla AppleScript - here’s how you add to
it
By Donald Olson, Apple Computer, Inc.
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
I. A brief history
In the beginning, the high priests made AppleScript. It had conditionals and flow
controls and pretty printing. It had script objects with properties and methods. It
talked to applications and integrated their functionality. It conversed across the
network. It was an OODL. And the high priests proclaimed it good.
But the people of AppleScript soon saw something amiss in the design. “Where is
the ask/answer dialog and the lowly beep?” the people asked. “Where are the standard
file and PPC Browser dialogs?” they demanded.
The high priests declared, “These things you ask for are not part of a pure
language such as AppleScript. You must look to applications for the relief you seek!”
There was much wailing and gnashing of teeth in the land of AppleScript. “We
must have the simple beep and the friendly ask/answer dialogs so that we may talk
amongst ourselves without the use of special applications,” the people protested. “It
is an abomination to our hard drives that we must have so many tiny applications to do
our bidding!”
So the priests conferred. “The people of AppleScript want to add new
functionalities to our language” they said. “We will not allow the purity of
AppleScript, the language, to be sullied with such things as user interface elements and
noises. We, therefore, will provide a mechanism which allows the people to add syntax
and dialogs and data transformations to AppleScript without requiring that they be
made a permanent part of our language. And we will give them a basic set of these
extensions to appease their needs.”
And low, the high priests unveiled Scripting Additions to the people of
AppleScript.
The people rejoiced. “We have our beep, our ask/answer and it is good!
Scripting Additions are loaded on demand, are available to the entire system, and yet,
do not fill up our system heap!”
“But, oh high priest, ” one of the AppleScript people said, “this minimal set of
scripting additions does not have all that I need. How may I write such scripting
additions myself so that I can achieve the total integration AppleScript promises?”
“Oh AppleScript people, I will bring down from the mountain of VirtualTables
the coding instructions and the commandments of Scripting Addition programming. Use
this information wisely, and your Scripting Additions will prosper.”
“Heed me well, people of AppleScript. Scripting Additions are part of our new
OSA scripting language AppleScript. Scripting Additions provide a mechanism for
extending the language and functionality of AppleScript. They are similar to XCMD’s
for HyperCard and UCMD’s for Frontier.”
MacTech Editor: “Hellooow, Donald!”
Article Author: “Um, uh, yea Neil?”
MacTech Editor: “This mythology stuff is fun, but can we get on with the
article??”
Article Author: “Article?? Oh! Sorry! Ok, here we go”
II. The parts.
Scripting Additions are implemented as Apple Event handlers or coercions that
AppleScript loads on demand. In order to write Scripting Additions, you need a basic
understanding of the Apple Event Manager (AEM) .
The AEM is a collection of routines that provide a mechanism for sending and
receiving messages (called AppleEvents) between applications either on the local
machine or over an AppleTalk network. Each message contains a pair of identifiers
that serve to inform the receiving application of the action to be taken as a result of
this message. These identifiers are called the class and id of the message. Since the
class and id specify the action to be taken, it is common to call the class and id the verb
of the message.
Data may also be associated with the verb of the message. This data is placed in
structures called descriptors and added as parameters to the Apple event. The AEM
provides a set of routines for adding or extracting parameters from a message. Since
there may be more than one parameter associated with a message, each parameter has a
unique identifier called a keyword associated with it. Since a verb normally has a
focus for the action it is to take, there is a standard parameter called the direct
parameter (or direct object) which is used to specify the object the verb is to act on.
An example of an Apple Event message is the open document message that is sent
by the Finder when you double click on one or more documents associated with an
application. The class of the open document event is kCoreEventClass and the id is
kAEOpenDocuments. It has one parameter, the keyDirectObject (a.k.a. the direct
parameter), which contains an AppleEvent list containing aliases to the documents that
the application is to open.
When an application receives an Apple event (high level event) in its event
queue, it calls the AEM routine AEProcessAppleEvent. This causes the AEM to attempt
to dispatch the AppleEvent to an Apple event handler that is installed by the application
in the AEM application dispatch table. The AEM dispatches based on the class and id of
the event. In our example above, the application would need to have installed a handler
with the class kCoreEventClass and the id kAEOpenDocuments in order to successfully
deal with the open documents Apple event.
What, you ask, does this have to do with writing a Scripting Addition?
The AEM provides not only a dispatch table for AppleEvents for each application
that installs handlers, it also maintains a dispatch table for system handlers as well.
System handlers, unlike application specific handlers, are available to all applications
on the machine where they are installed. This is where AppleScript installs Scripting
Additions. If the AEM does not find a match in the application dispatch table, or if the
handler returns one of two special errors (errAEEventNotHandled or
errAENoSuchObject), the AEM attempts to find a match in this system dispatch table
and thus, OSAX are invoked.
Coercions work in a similar fashion. In coercions, class and id are not used. The
‘from’ and ‘to’ data types are used instead. The AEM maintains application and system
dispatch tables for coercions as well.
Now that we’ve had a very brief overview of Apple events, let’s dive into the
parts of a Scripting Addition.
The Scripting Additions File.
The file that contains the various resources that make up a Scripting Addition is
created with the type ‘osax’ and the creator ‘ascr’. This allows the Finder to associate
the Scripting Additions icon (stored in AppleScript) with each OSAX file.
The ‘ osax’ resource.
Each Scripting Addition file has at least one code resource of type ‘osax’ that
contains the event or coercion handler for the Scripting Addition. It's the name of the
‘osax’ resource that tells AppleScript the type of Scripting Addition and the class and id
of the event handler; or the from and to types for a coercion OSAX. The naming scheme
works like this: The first four characters of the ‘osax’ resources name tells
AppleScript the type of OSAX. It will be ‘AEVT’ for event handlers and ‘CSDS’ or
‘CSPT’ for coercion handlers. ‘AEVT’ stands for “Apple Event”, ‘CSDS’ stands for
“coerce from descriptor”, and ‘CSPT’ stands for “coerce from pointer”.
For example, an ‘osax’ resource named “AEVTaevtodoc” would install an Open
document handler. An ‘osax’ resource named “CSDSscptTEXT” would install a coercion
from the type ‘scpt’ (the data type for an AppleScript compiled script) to ‘TEXT’
which is the type for text.
Other resources
Both event and coercion OSAX can have other resources included in there files. An
event handler that puts up a dialog might have ‘DLOG’ and ‘DITL’ resources. A coercion
handler might have a resource that contains conversion information for the coercion to
use. The Scripting Addition mechanism in AppleScript makes the OSAX’s resource file
the current when prior to dispatching to the OSAX code. An OSAX can therefore access
any resource in the following resource chain: OSAX -> [target application] -> System.
If no target is specified in the script (i.e. no ‘tell application’ block is used), the
target application is the script editor in use.
Event handlers require one resource that coercion handlers do not require. The
‘aete’ or Apple event terminology extension resource. This is the resource that
provides the language syntax for your event.
The ‘aete’ resource.
It is the ‘aete’ resource that AppleScript uses to determine the syntax for your
OSAX command. In the aete you describe the Apple event and all its parameters along
with a corresponding grammatical equivalent for each. In fact, the way I like to design
my OSAX’s is by starting with the AppleScript syntax for the verb, write the aete to
match the syntax, and then actually writing the code.
III. Writing a Scripting Addition
Design your syntax first
Let’s design an OSAX that plays a QuickTime movie in a modal dialog. Let’s see,
how about the syntax: ‘play movie [at ]’? That sounds about right. This gives the user the ability to specify the movie to play and
optionally position the window on the screen. (We’re purposely not going to add
support for the controller in this implementation. That we’ll leave as an exercise for
the reader)
Here’s what the event for the above syntax looks like:
play movie - this is the verb so let’s assign the class and id for this.
class : ‘OLIE’ - this happens to be my nick name, you should use the signature of
your application that you’ve registered with DTS or some other 4 character code
that will differentiate your OSAX. Remember, lower case is reserved for the
System (read Apple Computer).
id: ‘QTIM’ - Try to use something descriptive here.
- The direct parameter of an Apple event is defined as not
having an associated language label. This is because the object the verb is acting
on implies that the object to act on will be described next. Since our verb ‘play
movie’ requires that we describe which movie we’re to play, we’ll use the direct
parameter for the path to the movie to play.
keyword: ‘----’ - The direct parameter.
type: typeAlias - For the path we’ll use typeAlias. For a discussion about aliases
versus other addressing forms, see the section ‘Tips, tricks and gotchas’ below.
[at ] - our first ‘named’ parameter. keyword: ‘LOCA’ - This can be a list of 2 or 4 integers or a record that contains
labels for the positions of the point or rectangle; For example: ‘play movie alias
“Cool Disk:Cooler Folder:Hot Movie” at {qttop: 15, qtleft:30}’. This is nice for
the user since they don’t need to remember the ordering for the point or
rectangle coordinates. Notice that we don’t simply use ‘top’ and ‘left’ for our
record labels, instead we use the prefix ‘qt’. This is because of a conflict with
terminology defined for the text suite included in AppleScript itself. ‘left’ and
‘right’ are already defined as formating options for a block of text. See Section V
below for a discussion of other possible syntax clashes.
type: typeAEList/typeMyRectangle -Since the data can be either be a list or a
record, we need to define this parameter with each of the possible data types. The
type typeMyRectangle is a custom type that we define as a class. See Section V
below for more information on using classes to define custom data types.
We could also use the wild card type for the ‘at’ parameter. We choose not to
however because it can add a level of ambiguity to the syntax of the OSAX since it
implies that any data type can be passed in for that parameter.
Build your aete second
Now take a look at the listing “SAPlayMovie.r” to see the above design work in an
‘aete’. Notice how we’ve filled in the other fields of the aete. In particular, pay close
attention to the class definition near the bottom. Here is where we declare the record
for our ‘at’ parameter. We define the rectangle record type to be a class of type ‘rect’
with properties for the top, left, bottom and right coordinates.
Also notice that we’ve declared the ‘at’ parameter twice: once with the type list,
and once with the type of the class definition for our rectangle record. This way
AppleScript knows both of the allowed types for the ‘at’ parameter.
The formal definition for an ‘aete’ resource is found in the file
“AEUserTermTypes.r”.
/* 1 */
/*
SAPlayMovie.r written by Donald O. Olson
A simple QuickTime Scripting Addition written
to illustrate writing Scripting Additions.
Copyright ®1993 Donald O. Olson
All rights reserved.
*/
#include ”Types.r
#include "SysTypes.r
#include "AEUserTermTypes.r
#define typeMyRectangle 'RECT'
resource 'vers' (1) {
0x1,
0x0,
final,
0x0,
verUS,
"1.0",
"1.0, Copyright ® 1993 Donald Olson
". All rights reserved.
};
resource 'vers' (2) {
0x1,
0x0,
final,
0x0,
verUS,
"1.0",
"(by Donald Olson)
};
/* This string gets displayed if the user double clicks on us. */
resource 'STR ' (-16397) {
"This document can not be opened or printed.
" It extends the functionality of AppleScript™
"and should be placed in the Scripting Additions
"folder found in the Extensions folder of your
" System Folder.
};
resource 'aete' (0, "play movie") {