It’s amazing how easily I can get distracted.
Currently my main distraction is playing with Feed Me, my bayesian news filter, which is a thinly veiled excuse to mess about with Python.
However, yesterday I managed to get distracted from that distraction.
I’ve been thinking that I really need to sort out some productivity tools for myself, to deal with the sort of repetitive administrative tasks that I find myself doing again and again.
To that end, I installed FastScripts, and set about writing some trivial scripts to do some simple things.
One of the scripts I wanted was to find all the recent mails received from a particular person, since this is something that I do manually quite often. The script would only save a few keystrokes and mouse clicks, but I figured that if I could link it to a hot key with FastScripts, it would be handy.
Unfortunately, it turns out that Apple Mail’s scripting support doesn’t allow you to perform searches.
Luckily, there is a work around, in the form of the new GUI scripting capabilities of MacOS X.
Essentially, this allows you to ‘fake’ any user interface operation by scripting it in terms of the menus, buttons and or places that you would click, and the keys that you would press.
Presented below, for your delight and delectation, is a little example of how I used this to script a search in Mail.
The hardest part is working out exactly how to specify the interface widget that you’re trying to activate. A valuable helper here is the Prefab UI Browser which allows you to browse another application’s user interface whilst it’s running, so that you can discover the hierarchy of interface widgets.
The bad thing with all of this, of course, is that it’s a very fragile way to program. By relying on the names and layout of particular widgets, the script becomes very likely to break when a new version of Mail comes along with a different user interface. So I wouldn’t recommend it if there is a useable Applescript interface available instead, but it’s bloody handy when there isn’t one!
The key routine here is searchMailboxForMailFrom() which takes the name of a user and a mailbox, and uses them to perform a find in Mail. The assistive devices support that makes all this possible is only available in OS X 10.3 or higher, and the user has to have turned it on in the system preferences, so it’s necessary to check for that if you want to be a well behaved script and fail gracefully when there’s a problem.
on run if gotInterfaceScripting() then set mailIcon to "/Applications/Mail.app/Contents/Resources/app.icns" display dialog "Search for mail from:" with title "Search Inbox" default answer "" with icon POSIX file mailIcon if the result's button returned is "OK" then searchMailboxForMailFrom("Inbox", the result's text returned) end if end if end run on searchMailboxForMailFrom(searchIn, searchFrom) tell application "Mail" activate end tell tell application "System Events" tell process "Mail" -- select first mailbox in list - should be the inbox select (first row of outline 1 of scroll area 1 of window 1 whose value of static text 1 is searchIn) -- select the "mailbox search" menu item click menu item "Mailbox Search" of menu "Find" of menu item "Find" of menu "Edit" of menu bar item "Edit" of menu bar 1 -- type the search term and hit return keystroke searchFrom keystroke return repeat until exists button searchIn of window 1 -- can get errors if the script runs before the button has been created... -- so we wait here end repeat -- click the mailbox button (as opposed to searching in all mailboxes) click button searchIn of window 1 -- click the from button to search in the From: field click button "From" of window 1 end tell end tell end searchMailboxForMailFrom on gotInterfaceScripting() if gotPanther() then -- check to see if assistive devices is enabled tell application "System Events" if UI elements enabled then return true end if end tell tell application "System Preferences" activate set current pane to ¬ pane "com.apple.preference.universalaccess" set the dialog_message to "This script utilizes " & ¬ "the built-in Graphic User Interface Scripting " & ¬ "architecture of Mac OS X " & ¬ "which is currently disabled." & return & return & ¬ "You can activate GUI Scripting by selecting the " & ¬ "checkbox “Enable access for assistive devices” " & ¬ "in the Universal Access preference pane." display dialog dialog_message buttons {"Cancel"} ¬ default button 1 with icon 1 end tell end if return false end gotInterfaceScripting on gotPanther() -- get the system version set the hexData to system attribute "sysv" set hexString to {} repeat 4 times set hexString to ((hexData mod 16) as string) & hexString set hexData to hexData div 16 end repeat set the OS_version to the hexString as string if the OS_version is less than "1030" then display dialog "This script requires the installation of " & ¬ "Mac OS X 10.3 or higher." buttons {"Cancel"} ¬ default button 1 with icon 2 return false end if return true end gotPanther