Hello everyone and welcome to the elaborations on Robert Howe and Laura Lunetta's March-April 1999 MacToday's Under the Hood column. If you've read the printed version and just want to go directly to the elaborations, here's the links:
The mainframe emulation program we used.Hello, Scriptors! By now you've seen how easy it is to record and tweak scripts. You've gone to our elaborations web page at <http://homepage.mac.com/rhowehmd/Inreach/MacToday/> for detailed explanations of our articles. It's time to go beyond simple scripts. It's time to make decisions. It's time for "IF" statements!
What's an "IF" statement used for? To make decisions. (Duh!) When would that matter? It depends on your circumstances. (Double-Duh!) Here's an example:
Back in '94, Laura and I talked management into getting us each a Powerbook Duo 280c. (Whooo-hoo!). Since our real job is mainframe programming, we needed a NuBus 3270 emulation card to connect to the mainframe. On a Duo, NuBus cards fit only in the DuoDock. Pay attention now, because here's where it gets tricky: we wanted the mainframe emulation program to start automatically only when a Duo was in the DuoDock. Starting the mainframe emulation program without the emulation card--that is, when the Duo was not docked--caused a zillion error messages to appear. We needed--don't be shocked--an AppleScript in the Startup Items folder to (1) determine if the Duo was in the dock (2) if docked, start the 3270 program (3) if not docked, don't start the 3270 program.
Not that you asked, but at the time we were using "MacIRMA" by DCA. We had/still have a love-hate relationship with MacIRMA. It did the job, but its interface sure could have used some work. Then MacIRMA versions 4.02 through 4.04 had a coding bug we discovered using the handy programming tool "EvenBetterBusError", which explained a lot of the mysterious crashes folks got; going back to version 4.01 stopped the crashes. But that's all moof (!) now, since Attachmate has since bought up DCA and discontinued MacIRMA.
End elaboration. Next elaboration. To the top.
Since Laura and I don't know your particular situation, we'll walk you through the steps involved in creating an "If" statement. Tailor these steps to fit your needs. Ready? Here we go!
1. Determine the choice involved. In our example, it was "start up the mainframe-emulation program when docked; don't start up when not docked".
2. (a) Determine what you need to know in order to decide, and (b) how you can get (or at least infer) the information. In our example, we needed to know if the Duo was docked--that's step 2a. For step 2b, if you can't get information directly, pick a substitute. With the Duo, I had to use the screen size (big monitor verses built-in screen) as an indirect method of determining if the duo was docked. (How? See the elaborations page.)
I've told you my indirect criteria (the big-vs-little screen) for determining if a Duo's docked or not. Now the question is "How does AppleScript know the screen size?" The "Monitor Depth" only tells us how many colors the screen can present,which isn't quite good enough (It'll do in a pinch, but I want better). Jon's Commands, by Jon Pugh <http://www.seanet.com/~jonpugh/JonsCommandsDocs.html> has the command I need: Screen List. Screen List returns information about all connected monitors, one of which is the bounds. Our script went something like this:
-- assumes "Jon's Commands" is in the Scripting Addition FolderNow the explanation:
From Jon's commands, each item of the screen list contains the following:Thus, our script's first line...
copy bounds of (item 1 of (screen list)) to screenSize
takes the 'bounds' info from the first screen in the screen list and puts it into our variable screenSize.
ScreenSize is a list, similar to {0, 0, 640, 480}. These number represent coordinates on a vast imaginary screen, where the points represent {(left edge)(top edge)(right edge)(bottom edge)}. The difference between the top edge and the bottom edge represents the height of the screen; between the left and right edges represents the width of the screen. Anything the size or smaller than a screen of 640 x 480 and we assumed the duo was not docked. Actually, we figured anything the equal to or shorter than 480 and we weren't docked.
Our script's second line:
if ((item 4 of screenSize) - (item 2 of screenSize)) < 481 then
...just calculates the height of the screen. If it's less than 481...that is, it's 480 or less...and we were not docked.
The rest of the script opens (or doesn't open) the emulation program, depending if we thought the duo was docked or not.
End Elaboration. Next Elaboration. To the Top.
3. Make your choice.
This is almost anti-climactical. Use an 'IF' statement in the form of:
if <put your true/false condition here> thenSuppose every Wednesday you want to update a status report; all other days you want to open up the documents in the Apple Menu's "Recent Documents" folder. Based on our steps above, we...
1. Determine the choices involved.
That's not too difficult--the choice is this: On Wednesdays, open the status report. On any other day, open what's in the 'Recent Documents' folder.
2. (a) Determine what you need to know in order to decide, and (b) how you can get (or at least infer) the information.
For 2a, we need to know if it's Wednesday. For 2b, Apple's provided the "Current Date" as scripting addition. (We've mentioned scripting additions in earlier articles. Go to our elaborations page if you need a memory jog.) Entering "weekday of (current date)" into the script editor returns "Wednesday" whenever the Mac's internal clock says it's a Wednesday. Unfortunately, because of a quirk in AppleScript, the result of "weekday of (current date)" is not a string, and you cannot make it into a string. What to do? Put the weekday of a known Wednesday into a variable, and test the current date against that!
Ah, scripting additions. Ambiguous little critters, both a blessing and a curse. A blessing, because they allow you to do things not possible through plain ol' AppleScript. A curse because you may start to rely on an addition, only to find your scripts will not run on machines without the scripting addition. Oh, I didn't tell you what they are yet, did I? Scripting additions are bits of compiled code--think of them as mini-applications--that extend AppleScript's functionality. Think of scripting additions as being similar to external commands (XCMDs) in HyperCard. Apple provides about a dozen or two with AppleScript, and many more are available. One of my favorites is "Jon's Commands", which should be available at <http://www.seanet.com/~jonpugh/JonsCommandsDocs.html>. You've seen one thing Jon's Commands does earlier in this elaboration. It does a lot more.
3. Make the choice, using an "If" statement.
(Remember, go to our elaborations page if something isn't clear.) Here's our script:
copy "3/3/1999" to aKnownWednesday -- that's a Wednesday, yup
Elaboration: The Script Line-By-Line
(1) copy "3/3/1999" to aKnownWednesday
You could combine this line and the next. However, Applescript automatically converts an expression like (date "3/3/1998") to (date "Wednesday, March 3, 1999 12:00:00 AM"). I like to keep the string separate so I can change it later, if necessary. (And I don't know what would happen to the script if my English version was ported to a Mac running, say, the German AppleScript dialect.
(2) set nonStringWed to weekday of date aKnownWednesday
Here's the followthrough--we get the weekday of our known Wednesday date, and put it in a variable. Note that this variable does not contain a string, so it can handle the comparison in the next statement properly.
(3) if (weekday of (current date) is nonStringWed) then
This one line consists of 3 components. The First component is the scripting addition of "Current Date". That by itself will return a date object similar to (at least with my date/time settings) this:
date "Monday, December 14, 1998 6:30:40 AM"
(Yeah, I know it says 6:30 AM. But you see, I'm up early and have a long, long commuter bus ride into work. Might as well do my writing.)
The second component takes the date object and finds the weekday. You can turn the date object into a string, then extract the first word to get the day of the week, but that is not reliable considering the MacOS localizes the string to the date/time control panel's settings. By having AppleScript fish out the weekday -- via the "weekday of (date object)" component -- you can be sure of having the weekday.
Skip Digression and carry on with this elaboration.
Top of Page.
Therefore, if you don't want to worry about if the date needs to be in M/D/Y or D/M/Y format, just pick the year where 1/1 is the weekday you want, and copy the weekday to a variable. Here's a list:
1/1/1994 = SaturdayIf we want to test for Wednesday, we start our script like so...
copy weekday of date "1/1/1997" to dayToDoStuff -- puts whatever weekday string is proper into our variable. (Note that "1/1/1997" will convert to the date expression. Try it! That's why I copy the string to a variable, then copy the variable to a date expression.)
then follow it with a modified line 1 from our script:
if (weekday of (current date) is dayToDoStuff) then....
End Digression.
Top of Page
Elaboration: the Script Line by Line (continued)
The third component is the guts of our test: the 'if' test. It's in the form
...if (an item) is (the same as another item) then...
Here we test to see if the current weekday is "Wednesday". If it is, then we do the lines following, up to the "Else" line. (If we don't have an 'else' line, we go to the 'end if' line).
(4) open file "Macintosh HD:Documents:Status Reports:Current Status" -- assuming that's where the status report is
This line (and I've included the comment) opens up a single document at the location we've specified. You'll have to modify the location to point to the one you really want.
(5) else
This line signifies the end of what AppleScript performs when our condition in (3) is true and starts the code that gets performed when our condition in (3) is not true. Note that you don't need to have an <else> in an 'If' statement...if you don't, then nothing happens if the condition is not true.
(6) -- readers of the November/December MacToday will notice thatThese are comments. But you knew that.
(7) copy ((path to apple menu items folder as string) & "Recent Documents") to recentFolder
Our goal, if the day is not Wednesday, is to open all documents in the "recent documents" folder. But to do that, we have to know the full path to the folder. The "File Commands" scripting addition can get us the path to some special folders, but the Recent Documents folder is not one of them. Lucky for us, we know where that folder lives (in the Apple Menu Items folder) and the "File Commands" scripting addition can give us that path. So we'll take what we can get and build on it. Thus, we get the path to the apple menu items folder and append the name of the folder we do want.
We really don't need to copy the path to a variable, but I'm doing so to make the steps clearer.
(8) copy (list folder (recentFolder) invisibles false) to openUpAllThese
The 'file commands" scripting addition also can list the content of a folder. We'll use that now to get the names of all the items in the "Recent Documents" folder...which whose path name is held by the variable "recentfolder".
The "invisibles false" tells the scripting addition to skip invisible files. After all, if you can't see it, why try to open it? More importantly, if you try to open an invisible file, you'll have an error on your hands. So skip those invisible files!
(9 thru 18)
These lines are similar to the Nov/Dec article. See that elaboration for what's happening. But the 'better error checker' is shown below. (skip to the better error message)
repeat with LoopCount from 1 to number of items in openUpAllThese
Beeping is a quick-n-dirty error checker. And more important, "beep" takes just one word whereas a nicer error checker will eat up our word count. Here, though, we present a way to let the user know there was a problem. Replace "beep" with the following:
display dialog "The file " & item LoopCount of openUpAllThese & " could not be opened for some reason."
This won't tell the user why it didn't work, but it at least lets the user know it won't open.
End Elaboration. Top of Page
That's it! To summarize: we determined the choice involved, we figured out what we needed in order to decide, we found a way to get the information, and then we made the choice with an 'If' statement.
Visit our elaborations page for more examples. Need something scripted? Write us at <rhowehmd@inreach.com>; we'll try to help, and maybe even get you mentioned in an article or on our elaborations page. Join us next time for more scripting adventures "Under the hood."
Robert Howe and Laura Lunetta