|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
247
|
|
Chapter 18:
|
|
|
|
AppleScript, Etc.
|
|
The purpose of Newspeak was not only to provide a medium of expression
for the world-view and mental habits proper to the devotees of Ingsoc,
but to make all other modes of thought impossible.
|
|
- George Orwell, The Principles of Newspeak
|
|
One of the main purposes of scripting languages is to talk to other programs
or processes. On Unix systems, this is often called IPC(interprocess commu-
nication); Apple calls it IAC1(interapplication communication). IAC lets
two programs share data, which typically includes a command, list, string,
number, Boolean, or file alias.
|
|
There are several components to the IAC architecture in Mac OS. The one
we will deal with here is Apple Events. Apple Events are a popular, high-
level form of IAC; most Mac OS applications have Apple Event capabili-
ties built in, so that they can communicate directly with each other.
|
|
Apple's proprietary scripting language, AppleScript, also speaks Apple
Events. In fact, when an application is commonly referred to as "Apple-
Scriptable", that really means that it is scriptable with Apple Events.
Thus, AppleScript is just one language that can "speak" Apple Events.
|
|
Many Mac OS scripting languages are called OSA(Open Scripting Archi-
tecture) languages. These languages can be embedded into applications
with a special component protocol; normally, these languages all speak
Apple Events. AppleScript is the most popular of the OSA languages.
|
|
|
|
1A complete Inside Macintoshbook is devoted to IAC, most of it relating to Apple
Events. Obviously, we cannot cover all of Apple Events here in this book, but we
attempt to provide enough information to use Apple Events effectively with MacPerl.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AppleScript
|
|
AppleScript is a very powerful tool. With various extensions,2it can be
enabled to perform almost any function (e.g., handling regular expressions).
It is particularly good at providing access to system resources (e.g., users and
groups, audio CD information, or monitor resolution and depth3).
|
|
Calling AppleScript from MacPerl
|
|
The simplest way to do AppleScript-based IAC from MacPerl is through
the function MacPerl::DoAppleScript(). The function is very simple; it
takes a single argument (the complete text of an AppleScript) and returns a
textual representation of whatever the AppleScript returns.4
|
|
The following simple example opens the startup volume in the Finder.
|
|
$vol = MacPerl::MakePath((MacPerl::Volumes())[0]);
$script = <<EOS;
tell application "Finder"
open item "$vol"
end tell
EOS
|
|
print MacPerl::DoAppleScript($script)
or die("Could not run script\n");
|
|
Displays:
|
|
startup disk of Application "Finder"
|
|
Using AppleScript from MacPerl is quite slow , however.5Like Perl scripts,
AppleScripts must be (pre-)compiled before execution. To eliminate start-
up delays, AppleScripts are normally saved in a compiled format.
|
|
|
|
2Called OSAXs (or OSAXen), these are stored in the Scripting Additionsfolder, which
is either in the System Folderor the Extensionsfolder.
3MacPerl can use an installed OSAX too, via Apple Events, using the Finder as the
target application.
4The AppleScript language will not be explained; the authors have neither the space
nor the inclination.
5You can speed up AppleScript calls from MacPerl using Mac::OSA.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AppleScripts that are created by MacPerl scripts cannot take full advan-
tage of this optimization, however, because the AppleScript code is kept in
text format. So, time must be taken to compile the AppleScript before it can
be run, adding a significant amount of start-up delay.
|
|
Calling MacPerl from AppleScript
|
|
Communication can also go the other way, originating with an AppleScript
rather than MacPerl. In this case, the MacPerl script responds by means of
the MacPerl::Reply()function as described in Chapter 12, The MacPerl
Package. The script below could be executed from within Apple's Script
Editoror a compatible AppleScript editor.6
|
|
tell application "MacPerl"
return ¬
"Days until the year 2000: " & (Do Script "
use Time::Local;
$d1 = timelocal(0, 0, 0, 1, 0, 100);
$d2 = ($d1 - time()) / 60 / 60 / 24;
MacPerl::Reply(int($d2))
")
end tell
|
|
Displays (on April 1, 1998):
|
|
"Days until the year 2000: 640"
|
|
A MacPerl script invoked by the Do Scriptcommand can do pretty much
anything that a regular MacPerl script can do. If you need to use double
quotes in the MacPerl script, simply escape them (e.g., \"text\").
|
|
You can also embed AppleScript values into your Do Script event. We can't
do it as easily as we did with MacPerl's DoAppleScript function, which
evaluates the Perl variables before passing the string to the function, but
we can use concatenation in AppleScript to accomplish the same thing.
|
|
set mytext to "just another MacPerl hacker"
tell application "MacPerl"
return Do Script ¬
|
|
|
|
6To "break" and continue a long line of AppleScript code across more than one line,
end each unfininished partial line of AppleScript code with the continuation charac-
ter, ¬, (formed by the sequence option-return in Script Editor).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"MacPerl::Reply('" & mytext & "')"
end tell
|
|
Returns:
|
|
"just another MacPerl hacker"
|
|
Lists
|
|
Often, you might need a list of elements returned from MacPerl to Apple-
Script. The simplest way to do this is with delimited text that is joined by
MacPerl and split up by AppleScript.
|
|
tell application "MacPerl"
set myResult to Do Script ¬
"MacPerl::Reply(join ('|', ('a'..'g')))"
end tell
|
|
set AppleScript's text item delimiters to "|"
set myList to text items of myResult
set AppleScript's text item delimiters to ""
return myList
|
|
Returns an AppleScript list:
|
|
{"a", "b", "c", "d", "e", "f", "g"}
|
|
Or, going the other way, you might need an AppleScript list converted to a
MacPerl array. This can also be done with delimiters; in this example, we
use Text::ParseWords. We remove the brackets with a regex before pas-
sing the text to quotewords(), which uses ', 'as its delimiter.
|
|
use Text::ParseWords;
$script = <<EOS;
set myList to {"a", "b", "c", "d", "e", "f", "g"}
return myList
EOS
($result = MacPerl::DoAppleScript($script))
=~ s/^\{(.*)\}$/$1/;
@array = quotewords(', ', 0, $result);
print join(' ', @array);
|
|
Returns:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
a b c d e f g
|
|
Other OSA Languages
|
|
AppleScript may be the most popular of the OSA languages (largely
because it is included with every MacOS distribution) but it is not the only
OSA language. Other OSA-compliant languages are also available.
|
|
Frontier
|
|
Frontier is described by Userland as an automated Content Management
System, built around an object database, an integrated scripting language,
and an object-oriented website framework. The scripting language, User-
Talk, has a more "algebraic" syntax than AppleScript uses. That is, where
AppleScript's syntax tends to use English-like words such as tell, Frontier
uses functions, parentheses, and argument lists.
|
|
#!perl -w
use Mac::OSA;
use Mac::Components;
use Mac::AppleEvents;
my($vol, $co, $script, $result);
|
|
$vol= MacPerl::MakePath((MacPerl::Volumes())[0]);
$co= OpenDefaultComponent
(kOSAComponentType(), 'LAND');
$script = new AEDesc('TEXT', <<EOS);
return appleEvent
(Finder.id, 'aevt', 'odoc', '----', alias("$vol"))
EOS
$result = OSADoScript($co, $script, 0, 'TEXT', 0);
|
|
print AEPrint($result);
AEDisposeDesc($script);
AEDisposeDesc($result);
|
|
Displays:
|
|
"startupDisk"
|
|
The Frontier application needs to be running before UserTalk can be called
from MacPerl. The Mac::OSAmodule does most of the work here, and can be
used in a similar manner with any OSA language, like AppleScript.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Apple Events
|
|
In our examples, a MacPerl script calls an AppleScript (or UserTalk script)
or an AppleScript calls a MacPerl script. In either case, Apple Events are
used to effect the interaction. The Apple Events themselves are well dis-
guised, however, by the programming interface. Although you are free to
use this interface in ignorance of the underlying structure, we suggest that
you accompany us on a brief overview tour of Apple Event structure.
|
|
Apple Events have two major components: attributes and parameters. Attri-
butesare the portions of the event that define how it is to be used. Parame-
tersare contained in data structures similar to Perl's hashes, with a key-
wordas the key and some data as the value of that key. Parameter key-
words are always composed of exactly four (eight-bit) characters.
|
|
Attributes
|
|
An attribute is composed primarily of an event IDand an event class(suite).
Also given as attributes are such items as the target applicationfor the
event (specified by its creator ID).
|
|
An event ID is like the name of a function in Perl; an event must be defined
in the program in order to use it. We cannot define arbitrary events for
another application; we can only use the events that application has
provided. These events all belong to some event class, or suite, much as
functions in Perl all belong to a particular class or package.
|
|
tell application "Finder"
open item "$vol"
end tell
|
|
To open an item, as we did in our earlier example, we would use the event
ID odoc, which is in the class aevt. The target application is the Mac OS
Finder, which has MACSas its creator ID. For example:
|
|
%ae = (
target => 'MACS',
class=> 'aevt',
id=> 'odoc',
...
);
|
|
|
|
# creator ID
# event class
# event ID
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
Parameters often describe information about an application's built-in
classes7and propertiesof those classes. For instance, an application might
have a window class; the name of the window and its position and size on
the screen would be properties of that class.
|
|
A parameter might also be a simpler type of data, such as a string or file
alias. Each parameter is named by a unique keywordand has some data
assigned to it, like a Perl hash. Data is normally in the form of a recordor a
list, analogous to Perl scalars and arrays. Often, the most important data in
an event is passed through the direct objectparameter; the direct object
parameter, for instance, has the special keyword '----'.
|
|
Any self-contained part of an Apple Event is a descriptor. This includes
entire events with attributes and parameters, a standalone parameter, a
record, or a list.
|
|
An Apple Event Example
|
|
Once we have our attributes and parameters, we can build an AppleEvent
using the AEBuildAppleEvent()function which is part of the Mac::
AppleEventspackage. The event that is returned from that function can
then be sent via the AESend()function. The AEPrint()function can be
used to print out text representations of events and descriptors.
|
|
The example below uses AEBuildAppleEvent()to do exactly the same
thing our first example did through the AppleScript interface (opening the
startup volume in the Finder). It's a lot more complicated than the previous
version, however (although it runs much faster). Don't wory too much about
the syntax of the example; it's largely here to show you that TMTOWTDI.
|
|
We set up a hash (%ae) to store our attributes and parameters for use in the
AEBuildAppleEvent()function. Our build function takes all parameters
as one argument, with additional arguments to the function if the paramet-
ers call for them, much like sprintf()in Perl. We make the value of $ae
{'params'}an anonymous hash, so we can add as many values as neces-
sary. In this case, the TEXT(@)notation calls for an additional argument.
|
|
|
|
7These are not the same as the event class. They are similar in concept to Perl classes.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In this example, there is only one named parameter sent to this event: the
direct object parameter. The direct object parameter is passed an Apple
Event object specifier record, which is a type of descriptor record.
|
|
We're not going to get into the nitty gritty details of how this all works
here; that discussion is beyond the scope of this book. Suffice it to say that
the example below is identical in functionality to the first example we
gave in this chapter. Here goes!
|
|
#!perl -w
use Mac::AppleEvents;
my(%ae, $vol, $event, $reply);
|
|
$vol = MacPerl::MakePath((MacPerl::Volumes())[0]);
%ae = (
target => 'MACS',# creator ID
class=> 'aevt',# event class
id=> 'odoc',# event ID
params => [
"'----':obj " .# direct object keyword
"{want:type(cobj), " .# 4-char class ID
"from:null(), " .# object's container
"form:enum(name), " .# form of object data
"seld:TEXT(\@)}",# actual object data
$vol# the startup volume
]
);
|
|
# now build the event
$event= AEBuildAppleEvent(
$ae{'class'},# event class
$ae{'id'},# event ID
'sign',# appl. signature
$ae{'target'},# creator ID
0, 0,# end of this part
@{$ae{'params'}} # parameter list
) or die($^E);
$reply = AESend($event, kAEWaitReply) or die($^E);
print AEPrint($event), "\n";
print AEPrint($reply), "\n";
AEDisposeDesc($event);
AEDisposeDesc($reply);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
When run, this displays:
|
|
aevt\odoc{'----':obj {want:type(cobj),
from:'null'(), form:name, seld:"HD:"},
&inte:cans, &timo:3600}
aevt\ansr{'----':obj {want:type(prop),
from:'null'(), form:prop, seld:type(sdsk)}}
|
|
Pretty complex, isn't it?
|
|
If you're interested in learning more about Apple Event structure, and in
building Apple Events in this fashion, we recommend:
|
|
-
macperlcat.pod, on the CD-ROM
|
|
-
Inside Macintosh, especially Interapplication Communication
|
|
-
the various useful utilities such as AETE converter, on the CD-ROM
|
Copyright © 1997-1998 by Prime Time Freeware. All Rights Reserved.