26.4. Timers, Hooks, Attachability, Observability
A number of applications will trigger your script in response to the arrival of a certain day or time. Your choice of such an application will depend upon your particular needs (and possibly what you're willing to spend). Possibilities to examine include Script Timer , iDo Script Scheduler , and various cron front ends. There is also QuicKeys X, though this is rather pricey for what it does. Another option might even be iCal (which is already present on your computer); the alarm for an event or to-do item can involve running a script.
A hook is a point in an application's operation where it is willing to turn to youor more exactly, to a script you've suppliedto ask what to do. Many sorts of application use a hook to let you customize their behavior at key moments. Such, for the most part, are the applications listed in "Automatic Location" in Chapter 2. Thus, one example is iCal: as I mentioned a moment ago, when an alarm fires, iCal can run a script in response. Similarly, Apple's Mail program and Microsoft Entourage let you create rules that are performed in response to the arrival of mail messages meeting certain criteria; one of the things a rule can do is to run a script.
Some applications have hooks as their life blood, so to speak. An example is Salling Clicker , which is all about responding to a handheld device (such as a Bluetooth phone) by running a script. Another example is Ovolab Phlink , which answers the phone and turns at every stage of the phone call to your scripts to see what to dowhen the phone rings, when it answers, when the caller presses keys on a touchtone phone, when the call ends, and so forth.
Phlink calls into your script by taking advantage of named parameters. For example, when a phone call arrives on your computer, if you have a script called ring in the proper location, it will be called. In order for this to work, your script must contain a handler with this definition:
on incoming_call given call:theCall,
This is a user handler; you can define it without any scripting addition because it contains no special terminology. With named parameters, the names are up to youexcept that you've purposely used the names that Phlink will use when it calls your script. Phlink does define these handlers in its dictionary, but only for informational purposes; it can't enforce them on your script, because your script isn't targeting Phlink at the point where the handler is defined. A nice thing about this choice of syntax is that you can omit, in your definition, parameters you're not interested in receiving; there is, you remember, no penalty when a handler is called with extra parameters (they are simply ignored; see "Syntax of Defining and Calling a Handler" in Chapter 9).
An application may be considered attachable if it treats ordinary user actions as hooks (see "Modes of Scriptability" in Chapter 3). For example, BBEdit will run scripts located in its Menu Scripts folder, in response to your choosing from any of BBEdit's built-in menus. Thus, any of BBEdit's menus can do what it normally does, but optionally it can include, or can be overridden by, functionality that you provide as a script. Such a script must contain a handler with the following structure:
on menuselect(menuName, itemName) -- code end menuselect
The fact that an application can call into a user handler that takes positional parameters in your script is surprising, and involves a cool mechanism under the hood; this same mechanism is also how scriptability is implemented in an applet (see Chapter 27).
An event is observable if another application can see it. So for example QuicKeys , mentioned earlier, can run a script in response to a certain application starting or quitting, or becoming or ceasing to be frontmost, or when a certain drive is mounted or unmounted. That events at a high system level should be observable is not surprising, because it is reasonable that they should be so; an application certainly has a need and a right to know when it becomes frontmost, so why shouldn't it be able to tell when some other application becomes frontmost? On the other hand, the notion that one application can observe what's happening internally within some other application is surprising, both technically and, as it were, morally: an application that "sees" what characters I'm typing into FrameMaker at this moment could justly be accused of spying. Nevertheless, in recent versions of Mac OS X the range of observable application-internal events has been greatly increased, to include not simply when an application is activated or hidden, but also when a menu item is chosen, when a window is created, moved, resized, or miniaturized, as well as what happens at an even more detailed level of the application's interface. This means that your script can react to such internal events occurring in a particular application.
The mechanism here is the Accessibility API, just as for GUI scripting (see Chapter 24), except that it's being used in reverse. When an item in an appropriately written application's user interface is affected, the application gives off a little squawk called a notification. A second application can register to receive such notifications, and so it is effectively observing what goes on in the first. Not every application can be observed in this way; but every Cocoa application can, at the least. To experience the full power of this observation mechanism, try PreFab's UI Actions .
An example used in the tutorial included with UI Actions is a modification of Script Editor (I can't think how else to express itattachability really does, in effect, modify an application's behavior) so that every time a new (untitled) script window is created, a comment is inserted giving the current date:
tell application "UI Actions" tell UI action "Script Editor-AXWindowCreated" set d to date string of (get timestamp) set w to affected UI element end tell end tell tell application "System Events" set n to title of w if n begins with "Untitled" then tell application "Script Editor" set text of document n to "-- " & d end tell end if end tell
As with folder actions (see earlier in this chapter), UI Actions lets you associate an observed event in an application with a script that should be executed when that event occurs. So, using UI Actions, the user will "attach" this script to Script Editor's "AXWindowCreated" notification, so that when a new window is created in Script Editor, the script will be called. When the script is called, it targets UI Actions to get a reference to the window that generated the notification; then it targets System Events to learn (through GUI scripting) the name of this window; finally, if this appears to be a new script window (because its name starts with "Untitled"), it enters the current date into the window.
UI Actions can be an elegant solution to the problem of making sure that your script is triggered at an appropriate moment. The UI Actions application itself is scriptable, so you could write a script that creates a script and attaches it to a particular action, or that enables or disables a particular attached script. In fact, such a script could itself be triggered through being attached to some action. The mind boggles.