A real programmer is lazy, right?
Every now and then you find yourself doing a repetitive tasks consisting of a few small steps.
The steps are fast to perform, but still… it just feels… wrong.
It should be automated!
Thanks to AppleScript this is an “easy” task.
I will show my approach and problems I found.
The task to automate was my Matlab-workflow. I had two applications which needed to be opened and closed. First, my license of Matlab requires me to setup a VPN-connection to my university. Only then the Matlab application can load successfully. After using Matlab there is no need for the connection, so every time I am done with it, I have to terminate the VPN-client.
Second, there is a strange and irritating problem with Matlab and applications that use the Mac OS X support for assistive devices, such as (in my case) Flexiglass. When the checkbox in System Preferences > Accessibility > Enable access for assistive devices is checked, there is somewhere on your system an application that requires it. For me this was Flexiglass, and it produced an Java error in Matlab on almost any GUI action (Although is seems that this only occurs in a multi-monitor setup).
So when opening Matlab, first I would need to close Flexiglass. After the usage, I would open it again. Clearly this are needless steps for any programmer, so I searched a bit for this problem and found a helpful starting point in the following StackOverflow question: Close App A when App B closes: Mac OS X 10.7.3. It shows that AppleScript (which is a pretty funny language) is able to work with the Cocoa framework. This enables us to register for launching and termination of applications, using the shared instance of the NSWorkspace class:
Once a notification is received, we would like to open and close applications. This is pretty easy:
Handle open and close notifications
onappLaunchNotification_(notification)settheLauchedApplicationto(notification's userInfo's NSWorkspaceApplicationKey's localizedName())as textiftheLauchedApplicationis inpTriggerLaunchApplicationsthen-- Open the associated applicationsrepeatwithapplicationToOpeninpOpenOnLaunchApplicationstellapplicationapplicationToOpentoactivateendrepeat-- Close the associated applicationsrepeatwithapplicationtoCloseinpCloseOnLaunchApplicationtellapplicationapplicationtoClosetoquitendrepeatendifendappLaunchNotification_
Here you see I make use of a list of pTriggerLaunchApplications to check if the launched application should trigger actions.
If so, it traverses the list of applications to open, and to quit. Notice the simplicity of the commands. Really nice!
The actions to perform when the trigger applications is terminated are slightly more complicated. When Flexiglass is opened, it shows the preferences window/pane. This is useless, and I want that window to be hidden when it is opened. The normal command would be:
Close all opened windows of an application
(Again, the simplicity!)
Unfortunately, Flexiglass does not respond to AppleScript actions (there is no dictionary for it).
The error you can expect is something like execution error: Flexiglass got an error: every window doesn't understand the close message. (-1708)
This problem is solvable, by modifying the Info.plist file of the application; the flag NSAppleScriptEnabled YES needs to be added.
Here is a nice explanation about how to add AppleScript support to an application.
As that blogpost shows, different actions are needed depending on the Mac OS X version.
The following checks for this and thereby support OS X 10.6, 10.7 and 10.8.
Enable AppleScript support to any applicationExplanation
onenableAppleScripting(theApplication)-- Add AppleScript support to an application by overwriting the Info.plist in-- the Application bundle.-- See http://c-command.com/blog/2009/12/28/capture-from-preview/trysetapplication_pathto(path toapplicationtheApplication)setbundle_identifiertogetbundleidentifierof(info fortheapplication_path)tellapplication"Finder"settheapplication_to_modifyto(applicationfileidbundle_identifier)asaliasendtellsettheapp_pathto(POSIXpathoftheapplication_to_modify)settheapp_info_pathto((POSIXpathoftheapplication_to_modify)&"Contents/Info")settheplist_filepathtothequoted formoftheapp_info_path-- determine which Mac OS X version currently runningsetosvertosystemversionof(system info)-- Make a backup of the Application bundle and overwrite the plist filedo shell script"ditto -c -k --sequesterRsrc --keepParent "&app_path&space&app_path&".quit-open.zip"withadministratorprivilegesdo shell script"defaults write "&app_info_path&space&"NSAppleScriptEnabled -bool YES"withadministratorprivilegesdo shell script"chmod a+r"&space&app_info_path&".plist"withadministratorprivilegesifosver>="10.7"thenifosver>="10.8"then-- Assume Xcode is installeddo shell script"sudo ln -s /Applications/Xcode.app/Contents/Developer/usr/bin/codesign_allocate /usr/bin"withadministratorprivilegesendifdo shell script"codesign -f -s - "&app_pathwithadministratorprivilegesendifonerrormessagenumbererrorNumber-- Something went wrongifmessageis notequal to"ln: /usr/bin/codesign_allocate: File exists"thendisplay dialog"Problem with enabling AppleScript for "&theApplication&": "&message&" -- Error number: "&errorNumberendifendtryendenableAppleScripting
This function uses shell commands to modify the application’s info.plist. It assumes an installation of Xcode (on Mac OS X 10.8) and requires administrator privileges. This block is only executed the first time it tries to hide the windows of Flexiglass. Along the way is creates and backup zip in the /Applications directory.
The result of this AppleScript is an App Bundle. You can make your own version by using the following gist, and open this is the AppleScript editor (by pasting it after AppleScript Editor > File > New from Template > Cocoa-AppleScript applet). The bundle can be placed in /Applications, and to automatically start it, go to System Preferences > Users & Groups > Login Items and add the application. Enable the checkbox to start it hidden. Note the configuration properties in the script, which you can modify to your situation.
To keep the app from from dock, add the following after the first in the Info.plist of your created App bundle:
Add to Info.plist of App bundle
Please feel free to use, reuse and modify the code to your needs. You can find it in my Quit-Open repository. It helped me with a silly repetitive task and I am certain I will use AppleScript in more occasions, since it is pretty easy and is really powerful!