Friday, December 18, 2009

Useful Keyboard Shortcuts with AppleScript on Mac OS X

I've been using Ubuntu as my main OS for a while now. Gnome has a GUI for configuring global keyboard shortcuts (System -> Preferences -> Keyboard Shortcuts). For example I've set "Launch email client" to Ctrl+Alt+M and "Launch web browser" to Ctrl+Alt+W. I've also added a custom shortcut for launching GVim, another application that I frequently launch and close. Another action that I often perform via a shortcut is "Maximize window vertically" (Shift+Ctrl+Alt+Down). On high resolution screens (like 1920x1200), maximizing windows becomes less useful. When I need to see a lot of output in a terminal window for example, maximizing vertically makes much more sense. Being able to toggle play/pause on your music player of choice is also very useful when clicking on youtube videos embedded in blog posts or answering the phone. Banshee hooks into the Gnome mechanism, so I set Ctrl-F8 to toggle play/pause. What's interesting is that some other applications also support this mechanism. So when I launch Totem, it hijacks my shortcut. This is not really what I want most of the time.

The other day I was using my old Macbook and found myself using these shortcuts. Of course, nothing happened so I set out to fix this. Actually, I started using the shortcuts for launching a browser, shell and email client on Mac OS X. Back then I was using Quicksilver and had those shortcuts configured as global triggers. (Don't use the Quicksilver app from that link, BTW. Use Google Quick Search Box or Launchbar.) For toggling iTunes I used Sizzling Keys, which still works nicely. Nowadays, Quicksilver is bitrotting, so I was looking for something else. Surprisingly (to me, anyway), AppleScript is actually pretty useful. One nice feature of the Quicksilver triggers was that applications that are already running are not launched again. Instead, the running instance was activated (like when alt-tabbing to it). This was probably due to the one-instance-per-app behaviour that is common on Mac OS X. In my scripts, I check whether the app is running, which might not be neccessary. But it doesn't hurt and might be faster, so whatever. At any rate, for most apps I prefer this behaviour to the one on Ubuntu, where you can easily launch two instances of Thunderbird, for example. Which usually doesn't make sense.

Well, launching apps via AppleScript is not all that interesting. What about "Maximize window vertically"? The below code fragment determines the size of your screen, grabs the window that has the focus and resizes it vertically. You don't even need to figure out whether the dock is visible and how large it is. It will automatically do the right thing.
tell application "Finder"
    set desktopFrame to bounds of window of desktop
    set screenHeight to item 4 of desktopFrame
end tell

tell application "System Events"
    set activeApplication to name of the first process whose frontmost is true
end tell

tell application activeApplication
    set appFrame to bounds of the first window
    set xpos to item 1 of appFrame
    set ypos to 0
    set width to item 3 of appFrame
    set height to screenHeight
    set bounds of the first window to {xpos, ypos, width, height}
end tell
If you don't consider this to be the right thing, try Alt+Cmd+D to hide the dock, then maximize vertically again and unhide the dock (Alt+Cmd+D again). But I'm getting ahead of myself. So you have a bunch of scripts for manipulating the active window. How do you put those on global keyboard shortcuts? This might be possible without any third-party tools. I think you can put custom scripts in the services menu and assign those to keyboard shortcuts in System Preferences. I didn't try that though. There's a nice free tool called FastScripts Lite from the awesome indie mac developer Daniel Jalkut. As the "Lite" suggests, there is an even better version available that costs some money.
While we're at it, let's add some more useful window manipulation. As you can see in the screenshot above, there is a script to maximize the active window. Admit it, you sometimes miss a simple, consistent maximize function on the Mac. Yes, as a proper fanboy I should be claiming that the mac "maximize" behaviour is much smarter, resizes windows to the optimal size, etc. etc. Bullshit. As pointed out above, on a very large monitor maximizing a window to take up the whole screen is often useless. But my Macbook only does 1280x800. And oftentimes I want Safari to maximize, dammit. So now it does. The other two, "Maximize Window Left" and "Maximize Window Right" replicate a feature of Windows 7 that I found to be quite useful. "Maximize Window Left" puts the active window on the left side of the screen, maximizes it vertically and resizes it horizontally to take up half of the space. "Maximize Window Right" does the same thing on the right half of the screen. I already miss this on Ubuntu. It probably can be done in a window manager independent way by sending the right kind of data to the X server. This python library seems to be useful for doing that. I'm too lazy to try right now, though.

For the most part I like the whole shortcut functionality on the Mac better. Not launching apps that are already running for example is nice. Of course, everything can be done on Linux, but when you find yourself knee-deep in yak hair, consider putting down the text editor, scripting language and man page and go back to doing something useful. ;) What sucks about the AppleScript solution is that it doesn't work with all applications. I had some issues with MacVim and Firefox, IIRC. Anyway, Good Enough(tm), 80/20 and all that. Ah, almost forgot; you can download my scripts here.