[ Team LiB ] |
24.1 AppletsAn applet is a compiled script wrapped up in a simple standalone application shell (see Section 4.6). To make a script into an applet, save it from the Script Editor as an application instead of as a compiled script. You elect this choice in the Save As dialog. The result is a standalone application. If you open the application from the Finder (by double-clicking it, for example), the script runs. Alternatively, you can now save a script as an application bundle. From the outside, the result looks and works like an applet, exactly as described in this chapter. Since it's a bundle, though, you can do things with it that you can't do with an old-style applet, such as storing extra resources inside it; for an example, see Section 24.1.4, later in this chapter. Also, an application bundle can call scripting additions contained within itself; see Section 20.4. Keep in mind that this format is not backward-compatible with earlier system versions. A script still remains editable from inside the applet. The only trick is that now you must open the script for editing in a different way. You can't edit it by double-clicking it from the Finder, since that runs the applet. But the Script Editor can still open it. You can even have an applet open for editing in the Script Editor, save it without closing it, and then double-click it in the Finder to run it, as a way of testing while developing. (But you can't save an applet's script into the applet while the applet is actually running, for obvious reasons.) And there's another way to open an applet for editing, which we'll come to in a moment. 24.1.1 Applet OptionsWhen you select Application (or Application Bundle) in the Save As dialog, some further options come into play. These options affect the way the application will behave. One option is Show Startup Screen. If this is checked, the script's description is used to form a dialog which is displayed when the applet is started up, as a kind of introductory splash screen. In Script Editor, the description may be typed into the Description tab at the bottom of the window. It is styled text, and the styling is maintained in the startup screen dialog. The dialog offers the choice to run the applet's script or to quit without running it. Another option is Stay Open. The default behavior of an applet is to run its script and then quit automatically. But if Stay Open is checked, it doesn't quit automatically after running. A Stay Open applet has a Quit menu item, so the user can quit it manually. (Actually, even a non-Stay Open applet has a Quit menu item, but one is unlikely to notice this unless its run handler takes a long time. This can confuse the user; see the next section.) It also has a File menu item that lets the user suppress the startup screen (if the applet was originally saved with Show Startup Screen checked), and an Edit menu item that lets the user open the applet's script in the Script Editor.[1]
An applet that was saved with the Show Startup Screen option unchecked, or whose startup screen has been suppressed by the user, can be forced to show its startup screen dialog by holding down the Control key as the applet starts up. While the startup screen dialog is showing, the applet's menus are active. Thus, the user can always edit an applet's script, even if the applet is not set to Stay Open, by starting up the applet and holding down the Control key to bring up its startup screen dialog, then choosing the Edit menu item. To prevent the applet from being editable by the user, you can save it as Run Only. Keep in mind that this means even you can no longer edit the applet; if you have no other copy of the script, you lose all ability to edit the applet's script forever. 24.1.2 Applet HandlersCertain handlers in an applet script, if present, will be called automatically at certain moments in the lifetime of the applet. Because of their special status, none of these handlers takes parentheses in the first line of its definition; they are not called as handlers, but are responding to predefined commands (events):
So, for example, here's an annoying little Stay Open applet: on run display dialog "Howdy!" end run on quit display dialog "Farewell!" continue quit end quit on idle beep display dialog "Get to Work!" return 1 * minutes end idle An applet is scriptable with respect to its handlers; that is, you can tell an applet to run, reopen, idle, or quit, and it will execute the respective handler if it has one. If you tell an applet to run and it has no run handler code, it simply starts up if it isn't running already. If you tell an applet to run and it does have a run handler, and it wasn't running already, the run handler will be called twice—once because you targeted it, and again because you told it to run. To prevent this, first tell the applet to launch, and then tell it to run. (To prevent the run handler from being called at all, tell the applet to launch and don't tell it to run.) If you tell an applet to quit and it has no quit handler, it simply quits. If you tell an applet to idle and it has no idle handler, or to reopen and it has no reopen handler, nothing happens (but the calling script may receive an error). You are also free to add handlers of your own to an applet's script. If you do, then the applet becomes scriptable with respect to these handlers. You call them like ordinary handlers. For example, suppose we have an applet howdy whose script goes like this: on sayHowdy(toWhom) activate display dialog "Howdy, " & toWhom end sayHowdy Then we can say in another script: tell application "howdy" sayHowdy("Matt") end tell The value is returned as one would expect; here, the calling script receives the value {button returned:"OK"} if user presses the OK button. An applet has no dictionary. This means that when you call an applet's handler from another script, AppleScript has no way of knowing whether the applet contains such a handler, or, if it does, what its parameters should be. But it doesn't need to know. Without attempting to resolve it as terminology, AppleScript converts your call into the Call·subroutine command ('ascr/psbr') (see Appendix A). Essentially, it just recodes your call as a record and throws that record at the applet. It is up to the applet to decide how to respond; and it decides this correctly, all by itself. If you call an applet handler that doesn't exist, or if you call an applet handler with parameters that are not in accordance with those of the handler definition, the applet returns an error at runtime. The need to be able to code a handler call as an Apple event that can be blindly sent from one process to another in this way helps to explain the peculiar rules of AppleScript's handler-calling syntax (Section 8.3). The idle handler should not be treated as ensuring a precise measure of time. The time interval returned is merely a request not to be called until after the interval has elapsed. I am not entirely clear on what the time interval is measured from; experiments returning 0 seemed to suggest that it was measured from when the idle handler was last called, not from when it last returned, but this didn't seem to be true for other values. If your goal is to run a script at precise times or intervals, you might be happier using a utility to handle this for you. For example, see http://www.sophisticated.com/products/ido/ido_ss.html for iDo Script Scheduler, a commercial product that runs scripts at specified times. The question arises of how to interrupt a time-consuming applet. Suppose the run handler takes a long time and the user wishes to stop it and quit. Even a non-Stay Open applet has a Quit menu item, both in the menu bar and in the Dock, so the user might try choosing one; but this won't work. The user can cause an error by pressing -Period, which your script can catch and respond by quitting; but the user might not think of this. The user can force-quit, but then any cleanup operations in your quit handler won't be performed. The best you can do is probably something like this:[2]
global shouldQuit global didCleanup on run set shouldQuit to false set didCleanup to false try -- lengthy operation goes here repeat with x from 1 to 10 if shouldQuit then error say (x as string) delay 5 end repeat on error tell me to quit end try end run on quit if not didCleanup then -- cleanup operation goes here say "cleaning up" set didCleanup to true end if set shouldQuit to true continue quit end quit While the run handler of that example is executing, here's what the user can do, and what our code will do in response:
24.1.3 DropletsA droplet is simply an applet with an open handler:
The open handler's parameter is a command parameter, not a handler parameter, so it does not have to be expressed in parentheses in the first line of the definition. If a droplet is started up by double-clicking it from the Finder, then its run handler is executed and its open handler is not. But if it is started up by dropping items on it in the Finder, then it's the other way around: its open handler is executed and its run handler is not. Once a droplet is running (assuming it is a Stay Open droplet), the open handler can be executed by dropping items onto the droplet's icon in the Finder. The open handler is also scriptable, using the open command, whose parameter should be a list of aliases. In this simple example, the droplet reports how many folders were dropped on its icon:[3]
on open what set total to 0 tell application "Finder" repeat with f in what if kind of f is "folder" then set total to total + 1 end repeat end tell display dialog (total as string) & " folder(s)" end open 24.1.4 PersistencePersistence of top-level entities (see Section 7.6 and Section 9.2.2) works in an applet. The script is re-saved when the applet quits, maintaining the state of its top-level environment. So, for example, the following modification to the previous example would cause an applet to report the count of folders that had ever been dropped on it, not just the count of folders dropped on it at this moment: property total : 0 on open what tell application "Finder" repeat with f in what if kind of f is "folder" then set total to total + 1 end repeat end tell display dialog (total as string) & " folder(s)" end open On the other hand, this persistence ends as soon as the applet's script is edited. If you're still developing an applet, or likely to edit it further for any reason, you might like a way to store data persistently with no chance of losing it. The new application bundle format supplies a solution. An application bundle appears and behaves in the Finder just like an applet, but is in reality a folder. When it runs, path to me is the bundle's pathname. This means we can perform persistent data storage in a separate script file inside the bundle; the user won't see this separate file, and as long as we don't deliberately open it in the Script Editor, its data will persist even when we edit the applet's main script. To illustrate, let's return to the example in Section 9.6.2. This code is just the same as in that example, except that we now assume we are an application bundle, and the first line has been changed to store the data inside the bundle: set thePath to (path to me as string) & "myPrefs" script myPrefs property favoriteColor : "" end script try set myPrefs to load script file thePath on error set favoriteColor of myPrefs to text returned of ¬ (display dialog "Favorite Color:" default answer ¬ "" buttons {"OK"} default button "OK") store script myPrefs in file thePath replacing yes end try display dialog "Your favorite color is " & favoriteColor of myPrefs Now we save the script as an application bundle, and we have a single file, an applet, which behaves correctly: the first time it runs, it asks for the user's favorite color; the next time it runs, it remembers the user's favorite color and presents it. And it doesn't forget the user's favorite color if we now edit this script. |
[ Team LiB ] |