June 94 - Programming for Flexibility: The Open Scripting Architecture
Programming for Flexibility: The Open Scripting
Architecture
PAUL G. SMITH
Users -- and developers -- waited a long time for the Macintosh Operating System to
support the ability to attach and run scripts as a way of customizing applications. The
Open Scripting Architecture (OSA) that's part of AppleScript finally provides the
necessary services. Now you can realize massive gains in flexibility by using
embedded scripts and can pass similar gains along to the user by making your
application OSA savvy.
Thanks to Apple's Open Scripting Architecture (OSA), an application can now be as
flexible as a set of Lego building blocks. Defining the program's high-level behavior
using scripts instead of traditional program code makes possible an unprecedented
amount of flexibility, a cause for celebration particularly among in-house developers
and developers of custom software. Want to make a change in the way your software
works? It's simple to modify the scripts that define the behavior of the objects
involved. Want to make a customized solution, using the program's components as
building blocks? Easy: just write some new scripts. Want to construct an OpenDoc part
from your program? You're already partway there.
Varying degrees of OSA support are open to your application. The OSA gives you the
ability to do the following:
• execute scripts previously created with the AppleScript Script Editor
• store compiled scripts and other script values in your program and data
files
• directly compile and execute scripts
• decompile existing scripts, for editing
• use embedded scripts to automate your program's handling of Apple events
• enable users to customize and extend your program's capabilities by
attaching scripts to objects in the application's domain
Further, the OSA makes it possible for all customizable applications to present a
common set of scripting languages and dialects for users to choose from.
This article orients you to the OSA by outlining an OSA-savvy programming structure
and then describing techniques you can use to import scripts from the Script Editor,
run a script in your application, attach scripts to objects, compile and decompile
scripts, route Apple events to scripts, and handle user-interface events. The
programming structure and these techniques are demonstratedin the source code for
the sample program SimpliFace on this issue's CD. The AppleScript Software
Development Toolkit, available from APDA, contains the essential tools for OSA
development.
STRUCTURING THE OSA-SAVVY PROGRAM
You need to do some preliminary setup work in your application before you can take
full advantage of the services offered by the OSA and make use of scripts. Depending on
how your application is already structured, this can mean anything from a slight
restructuring to a complete rewrite from the ground up. I'll describe the basic
requirements for an OSA-savvy program here and then show you the structure of
SimpliFace so that you can see how one looks.
THE BASIC REQUIREMENTS
The first requirement for an OSA-savvy program is that it comply with the Apple
event object model. As you probably know, this model sets out a standard way of
structuring a program so that it can be controlled from other programs and so that it's
scriptable using standard terminology familiar to the user. This model is solution
oriented (that's the crucial part) because it concentrates onwhat users do with the
application, not onhow they and the application do it. The articles "Apple Event Objects
and You" indevelop Issue 10 and "Better Apple Event Coding Through Objects
indevelop Issue 12 provide useful information about the Apple event object model and
how to support it in your application. The Apple Event Registry is the essential
reference for standard Apple event classes and commands.
The second requirement (which isn't completely separable from the first) is that the
application be fully factored -- that is, that it separate the interface from the
operations. In a factored program, the actions that result when users choose menu
items, click buttons, and so on, generate a sequence of Apple events. When a
user-initiated action is dispatched as an Apple event, or when an external program or
script sends an Apple event, the program resolves which object the Apple event relates
to. It then passes the Apple event to the appropriate handler for that object; this
program code is responsible for the object's behavior.
When your application complies with the Apple event object model and is fully
factored, and when it publishes its scripting terminology, it's possible to make it
attachable -- that is, to make it handle and store the data involved in the process of
embedding or attaching a script. (I say "embedding a script" when I mean building one
in at the program development stage, whereas I refer to "attaching a script" when I
mean it's added or modified by the user.) And once your program enables scripts to be
attached to objects such as windows, documents, and the application object itself, these
scripts can customize the program's handling of object-model Apple events.
Scripts attached to program objects can affect the behavior of the program and its
objects in two cases. In the first case, scripts attached to objects can modify the
behavior of those objects when Apple events are resolved and handled. In the second
case, scripts attached to user-interface objects like menus and buttons can define the
sequence of Apple events that result from user-initiated actions. Both of these
mechanisms may have a place in your application. Fortunately, your application's
structure doesn't have to change much to allow scripts to customize behavior.
An attachable program can give a compiled script first crack at handling an incoming
Apple event instead of passing the event first to the handler (the program code that
defines the object's behavior). If the script handles the Apple event, the program code
doesn't get called; if the script continues the Apple event (that is, passes the message
to the script's parent object) or if it doesn't handle it, the program code gets called as
usual, as illustrated in Figure 1. If necessary, the script can modify or add to the
original parameters for the Apple event before passing it on to the program code.
For more on handling Apple events, see the description of command handlers on page
241 of the AppleScript Language Guide . *
Figure 1. Routing an Apple event
Thus, attaching scripts to objects can make the operation of your program a great deal
more flexible. But you can go even further: instead of generating Apple events by
making long-winded calls to the Apple Event Manager in response to user-initiated
actions, you can attach scripts to user-interface objects. Selection of one of these
objects then results in a script being called; the result of executing the script is that
the appropriate Apple events are sent, as illustrated in Figure 2. In the first case, the
primary reason the program makes the Apple event calls is so that the action is
recordable; in the second case, the script makes the Apple event calls anyway, so that
no extra work is required to make the action recordable, and thus the recordability
comes for free. The overhead involved in this is minimal (and it may even reduce the
bulk of program code); the increased flexibility is massive. It's not even necessary to
make these embedded scripts user changeable -- that's entirely up to you.
A SAMPLE PROGRAM: SIMPLIFACE
The sample program SimpliFace on this issue's CD demonstrates the principles just
outlined. SimpliFace is a basic scriptable and attachable user-interface builder
written in MPW C++. SimpliFace constructs scripted windows that can contain text
labels (though not editable text) and buttons. It demonstrates many of the features of
the OSA APIs, uses a lightweight C++ framework for Apple event object model
compliance, suggests a novel approach to a fully factored application, and allows
scripts to be attached to all application objects. SimpliFace has little preprogrammed
behavior; virtually everything is defined through scripts supplied by the user.
Figure 2. Generating Apple events
SimpliFace is built around a rough-and-ready C++ framework, inspired by the one
used in the Apple Shared Library Manager's sample applications. I like to use
lightweight C++ classes that don't depend on one another too much (thus aiding their
reuse), so the program structure isn't as tightly integrated as that of, say, a MacApp
program. In the spirit of other Apple sample applications, most of the error handling
has been left for later.
Figure 3 illustrates the object containment hierarchy for SimpliFace at run time.
There is one application object, which can contain zero or more window objects. Each
window object can contain button objects and/or text label objects.
Figure 3. SimpliFace's object containment hierarchy
Figure 4 shows the SimpliFace class hierarchy. All application-domain scriptable
objects derive from a TScriptableObject class (see the source file