Quarter Life Crisis

The world according to Sven-S. Porst

« Not quite a Fail WhaleMainNice Touches »

Keyboard Equivalents

1374 words on

Pierre Igot writes about his problems with keyboard equivalents on the Mac. In a part of his report he mentions that Mac OS X occasionally loses the custom keyboard equivalents he set up in Keyboard & Mouse preferences.

Settings for custom keyboard equivalents in the Mac OS X System Preferences

That issue sounded familiar to me as I lost my painstakingly set up keyboard equivalents a few times as well. As this only happens once in a year or two, it’s usually not worth investigating, but I had a little theory at the back of my mind which - together with the details mentioned by Pierre - I can make sense of and spell out now.

History

To understand this properly, we need to look back. Cocoa started out back in the NeXt days and back then it already had a very simple and powerful way to let users customise their keyboard equivalents. Each Cocoa application can have a set of preferences - known as User Defaults. If an application finds a user default named NSUserKeyEquivalents which contains a dictionary it will interpret the contents of that dictionary as follows: The names (keys) of the entries correspond to the names of menu items, and the values for those keys are the keyboard equivalents assigned to them:

Example of NSUserKeyEquivalents dictionary

There are a few special characters involved in this: @ represents the Command key, ~ represents the Option key, $ represents the Shift key and ^ represents the control key. A combination of these and a single character following them make up the keyboard equivalent. The example seen above uses the Command-V keyboard equivalent to ‘Paste and Match Style’ while assigning the more complicated Command-Option-Shift-V to the simple ‘Paste’ command, thus reverting the keyboard equivalents of the two (which has proven to be good for my nerves over the years as I Mac OS X’s clipboard management is by far too effective at preserving style information I do not want).

Note that this way of assigning custom keyboard equivalents is no way perfect or even good. As it is based on the names of the menu items it is prone to failures in numerous cases: Switch the language you are using? FAIL. Have application authors change the name of a menu item? FAIL. Have Apple decide to change HIG rules for menu items (e.g. by introducing a complimentary space just before ellipsis characters in German for Mac OS X.5)? FAIL. Have menu items with dynamic names (e.g. the submenus with items for Mail accounts in Mail)? FAIL. Have menu items whose names change depending on the menu item’s state (quite stupidly it seems to be Apple’s policy since Mac OS X.5 to use different verbs in menus items such as those for showing/hiding things rather than the checkmark next to it which is much quicker to see)? Not quite FAIL, but you’ll need custom keyboard equivalents for both menu item names. Have two menu items with the same name (e.g. various submenus of Mail’s Mailbox menu)? FAIL. Despite this world of FAIL, even a hacky way of setting up custom keyboard equivalents is better than no way at all or having to manipulate the NIB files to do it.

Centralised Preferences

While the feature for customising keyboard equivalents has been around ‘forever’ in the Mac OS X universe, it used to be a ‘secret’ that was communicated from the NeXt greybeards to the Mac newbies back in the days. One had to manually use the defaults command or edit the preferences file to set the keyboard equivalents up back then.

But at some stage (Mac OS X.3, think), Apple decided to include a graphical user interface for the feature with the system - the last tab in the Keyboard & Mouse preferences seen above. This never really impressed anyone as it still forces you to manually type the name of the command whose keyboard equivalent you want to set, but at least it saves you the hassle of manually manipulating preference files.

As the keyboard equivalents are stored in the preference files of their respective applications, I always wondered how System Preferences could easily display them. There are hundreds, if not thousands, of preference files in your ~/Library/Preferences folder as pretty much any application you ever launched will have left one there. Going through all of them may be a bit of a drag and things will be even worse once you take into account the system’s global or network preferences as well.

The little trick the system uses here seems to be the following: it keeps a list of the applications which have custom keyboard equivalents set up. Taking the cue from Pierre’s observations, this list is stored in the com.apple.custommenu.apps key of the com.apple.universalaccess preferences (which doesn’t make particularly much sense to me, but there it is). With this being a static list that seems to be edited by System Preferences when you add or remove keyboard equivalents, it particularly means that it will not contain any applications for which you added custom keyboard equivalents manually. It also means that deleting those entries in the Universal Access preferences will lead System Preferences to think that you have no custom keyboard equivalents set up at all.

That isn’t a big deal yet as this won’t keep your custom keyboard equivalents from working - they’re stored in each application’s preference file after all. They just won’t be displayed in System Preferences.

A Small Deal becoming Data Corruption

It takes another error in Apple’s logic to lead to the data corruption you see. I’m guessing this as I don’t have Apple’s code, but I can imagine the following pattern happening here: When you add a keyboard equivalent for an application, System Preferences checks whether there are keyboard equivalents for that application already. If there are, it adds the new one to the existing dictionary of keyboard equivalents; if there aren’t it creates a new dictionary with just the new keyboard equivalent and adds it to the preferences.

I assume that System Preferences makes the mistake of using the cached list from Universal Access preferences to determine whether keyboard equivalents already exist for the application in question. It would be much safer to look into the actual preference file to see whether or not there are custom keyboard equivalents already. As we’re only dealing with the preferences of a single application now, this should be the same speed as the original method.

And with the wrong result stating that the application has no custom keyboard equivalents, System Preferences then writes the new dictionary containing the single new keyboard equivalent to the application’s preference. Only now the original preferences have been deleted.

Extras

The Mac’s defaults system gives you the handy defaults tool to poke around the preferences on your machine. You can get the list of applications whose custom keyboard equivalents the system knows about by running

defaults read com.apple.universalaccess com.apple.custommenu.apps

The command’s result gives you the list of bundle identifiers for the affected applications and NSGlobalDomain for the global keyboard equivalents you can set up in System Preferences (that come handy if you want to set up keyboard equivalents for things like Services or common menu commands).

To get a list of all custom keyboard equivalents set up for your applications you can run

defaults find NSUserKeyEquivalents

which gives a convenient way of seeing whether or not the System chose to start ignoring some of the keyboard equivalents you set up at some stage. In fact, combining all this and throwing in a boatload of escaping and quoting nonsense (tcsh-style in case that matters) in the style of my personal incompetence, led me to this monstrous heap of junk:

defaults find NSUserKeyEquivalents | grep Found | sed "s/.*domain .\(.*\).:.*/'\1'/g" | tr "\n" "," | sed "s/\(.*\),/(\1)/g" | sed "s/Apple Global Domain/NSGlobalDomain/g" | xargs -J appnames defaults write com.apple.universalaccess com.apple.custommenu.apps appnames

Its idea is to grab the bundle IDs of the applications which have custom keyboard equivalents and shove them back into the Universal Access preferences. Worked for me and doubled the number of applications on my system with custom keyboard equivalents noted in System Preferences.

If you want to run it on your system, be sure to quit System Preferences beforehand and don’t blame me if it breaks anything.

July 10, 2009, 2:29

Tagged as defaults, hack, keyboard equivalent, mac.

Comments

Comment by Patrick: User icon

Hi Sven,

thank you so so much for this hint. Several months ago this issue almost drove me mad and I decided to use a macro app for customizing my keyboard shortcuts, instead of the built-in preference Mac OS X provides.

After running your shell command I now have 5 entries in “Application Keyboard Shortcuts” in contrast to 1 before applying your fix.

Thanks and keep up the good writing.

July 11, 2009, 14:42

Add your comment

« Not quite a Fail WhaleMainNice Touches »

Comments on

Photos

Categories

Me

This page

Out & About

pinboard Links

People

Ego-Linking