Take the 2-minute tour ×
Ask Different is a question and answer site for power users of Apple hardware and software. It's 100% free, no registration required.

I need a way to set the following programmatically:

keyboard shortcut

Specifically for shortcuts under Services, if that makes a difference. I'm wondering if this is possible via shell or AppleScript?

EDIT:

I've seen Programmatically add keyboard shortcut to Mac System Preferences but I'm still not clear on how to set a shortcut for an existing service.

share|improve this question
    
Might be a different story for Mavericks, but see stackoverflow.com/questions/7219134/… Or there's the long (GUI scripting) way: rngtng.com/2010/10/29/applescript-to-create-keyboard-shortcuts –  CRGreen Dec 29 '13 at 21:45
    
... but keep in mind that how Mavericks handles allowing GUI scripting using System Events is different than it used to be, in that you have to allow permission to what runs the script (script application, script editor). –  CRGreen Dec 29 '13 at 21:53
    
Sorry, didn't see that you already saw that post ... so what have you tried so far? –  CRGreen Dec 29 '13 at 22:03
    
@CRGreen I've been messing around with several plists trying to find a way to set that specific shortcut. The last link you provided definitely gets me closer. I see that specific property there. –  Chris Knadler Dec 29 '13 at 23:44
add comment

migrated from stackoverflow.com Dec 31 '13 at 22:46

This question came from our site for professional and enthusiast programmers.

5 Answers

up vote 1 down vote accepted
+100

@chrisKnadler made a good point of using Applescript to do this.

I have used this before in the past but discounted it because of the new problem in Mavericks of preference being cached system wide.

Meaning you could only use the unix command

defaults write...

to get the results to actually change instead of manually changing them or using Applescript plist item commands.

But just now having a look around found a suggestion that you can simply flush the cache and initiate a the system to read the preference again by just calling a

defaults read ...

on the plist file you have change. This does indeed seem to work.

  set theServiceName to "(null) - Print to PDF - runWorkflowAsService"
set libPrefPath to POSIX path of (path to preferences folder from user domain as text)
set PlistFile to "pbs.plist"
set PlistPath to libPrefPath & PlistFile as text
set mainPropertyItem to "NSServicesStatus"
set theKey to "key_equivalent"

tell application "System Events"

    set gp to (get property list item theServiceName of property list item mainPropertyItem of property list file PlistPath)

    tell gp
        --set value of property list item "enabled_services_menu" to true
        --set value of property list item "enabled_context_menu" to true
        set value of property list item theKey to "@^h"

    end tell

end tell

do shell script " defaults read " & quoted form of PlistPath & > /dev/null

You may still need to quite individual apps for them to show the change ( as is normal)


UPDATE: Small change.

Added > /dev/null to the end of the defaults command so only errors are returned.

share|improve this answer
    
followed up here apple.stackexchange.com/questions/115992/… –  Chris Knadler Jan 4 at 6:41
add comment

Like most preferences, keyboard shortcuts can be set using "defaults write" commands in the Terminal. Useful if you have lots of shortcuts that you want to define, or if you have more than one Mac to set up. However, the syntax isn't quite the same as the usual commands for setting a key to a value.

For Application-specific commands, use the following:

defaults write com.developer.app NSUserKeyEquivalents -dict-add "Menu Item" -string "@$~^k"

The meta-keys are set as @ for Command, $ for Shift, ~ for Alt and ^ for Ctrl. k in this example is the non-meta-key that you want to use.

For system-wide shortcuts, you can use -g instead of the app identifier, e.g.

defaults write -g NSUserKeyEquivalents -dict-add "Menu Item" -string "@$~^k" 

Note that you'll need to relaunch the app before these will take effect. Also you can see if they've been successful in System Preferences -- which will also need a relaunch to show the changes.

share|improve this answer
    
Does this actually answer the question of adding a shortcut to an existing service menu? –  markhunte Jan 1 at 13:57
add comment

Services keyboard shortcuts are a bit different than Application-specific or system-wide shortcuts, although they can be managed using defaults read and defaults write.

The easiest way I've found to figure this sort of thing out is:

defaults read > /tmp/a

Change the keyboard shortcut manually using System Preferences

defaults read > /tmp/b
diff /tmp/[ab]

For the specific case of keyboard shortcuts for Services, in OS-X 10.8 the important domains are "com.apple.ServicesMenu.Services" and "pbs". You need to look at the com.apple.ServicesMenu.Services domain to get the information needed to create an entry in the pbs domain.

For example:

The CFVendedServices key from com.apple.ServicesMenu.Services for "Unarchive to Current Folder is:

{
    NSBundleIdentifier = "cx.c3.theunarchiver";
    NSBundlePath = "/opt/homebrew-cask/Caskroom/the-unarchiver/3.9.1/The Unarchiver.app";
    NSKeyEquivalent = { };
    NSMenuItem = {
        default = "The Unarchiver/Unarchive To Current Folder";
    };
    NSMessage = unarchiveToCurrentFolderWithPasteboard;
    NSPortName = "The Unarchiver";
    NSSendTypes = (
        NSFilenamesPboardType
    );
    NSUUID = <be1910e8 cb1a3475 90854bc5 09f0c0ab>;
},

To create a keyboard shortcut for it, the pbs NSServices key is constructed from the NSBundleIdentifier, NSMenuItem and NSMessage. Example:

defaults read pbs
{
        "cx.c3.theunarchiver - The Unarchiver/Unarchive To Current Folder - unarchiveToCurrentFolderWithPasteboard" =         {
            "enabled_context_menu" = 1;
            "enabled_services_menu" = 1;
            "key_equivalent" = "@~^u";
        };
        "org.vim.MacVim - MacVim/New MacVim Buffer Here - newFileHere" =         {
            "key_equivalent" = "@~^v";
        };
    };
}

Unfortunately, the pbs domain is constructed as nested dictionaries which cannot be handled by defaults write like this simpler cases for other keyboard shortcuts. You'll need to read the entire NSServicesStatus key, insert your new entry, then write it back:

defaults write pbs NSServicesStatus '{
    "com.cocoatech.PathFinder - Path Finder/Show Info - getInfoInPathFinderService" =     {
        "key_equivalent" = "@~^$i";
    };
    "cx.c3.theunarchiver - The Unarchiver/Unarchive To Current Folder - unarchiveToCurrentFolderWithPasteboard" =     {
        "enabled_context_menu" = 1;
        "enabled_services_menu" = 1;
        "key_equivalent" = "@~^u";
    };
    "org.vim.MacVim - MacVim/New MacVim Buffer Here - newFileHere" =     {
        "key_equivalent" = "@~^v";
    };
}'

It may also be necessary to force pbs to flush its cache:

/System/Library/CoreServices/pbs -flush
share|improve this answer
    
I had a play doing this yesterday but did not post it. ( I used a copy of the bps plist to work on)I read in the NSServicesStatus and used sed to change the part I wanted to change. But although that works in changing the dictionary when I wrote it back to the plist, any number or bool would be changed to a string. Have you over come this. –  markhunte Jan 1 at 18:50
    
From the man pages, it seems like -data would work. I'll give it a shot and see if I cant get something working. This definitely seems like the way to go about it though! ^_^ –  Chris Knadler Jan 2 at 3:42
    
This seems like another way to do it: discussions.apple.com/thread/4709713?tstart=0 –  Chris Knadler Jan 2 at 3:47
add comment

I'm lazy - I use a tool like FastScripts to set global keyboard shortcuts and invoke scripts.

It's free unless you need 11 or more shortcuts defined. Also, the author is a fine gentleman that makes quality software and supports users well beyond what you might expect for a one man shop.

share|improve this answer
    
This is fabulous, but I actually am attempting to make this shortcut as a part of an installer. I want to try to get this done without using any third party software. Still, this is cool and I'll definitely use it in general. Thanks! (would upvote if I could ^_^) –  Chris Knadler Jan 2 at 21:07
    
No worries on the vote. I appreciate good comments and tough questions like this more than any 100 votes that come when the internet attention features one question or answer. I'll keep my eyes peeled for other options, but it looks like the easy solutions have all been proposed in other answers here. –  bmike Jan 2 at 21:33
add comment

You can also use PlistBuddy and defaults:

/usr/libexec/PlistBuddy -c 'Delete NSServicesStatus:"(null) - test2 - runWorkflowAsService"' ~/Library/Preferences/pbs.plist 2>/dev/null;defaults write pbs NSServicesStatus -dict-add '"(null) - test2 - runWorkflowAsService"' '{key_equivalent = "^~@2";}'

The PlistBuddy command is not needed if there is not an existing entry for the service. Replace test2 with the name of the service. Quit and reopen applications to apply the changes.

^~@2 is control-option-command-2. See http://osxnotes.net/keybindings.html.

share|improve this answer
    
I noticed that some of the NSServiceStatus dictionaries have more than one item. I.e enable service menu and context menus. The plistBuddy command would delete all of them. But the defaults is not taking that into account. How important those settings are I am not 100% sure. Not all the dicts have them but may still be enabled. I think it depends how the service is created. I.e by automator or an app. –  markhunte Jan 3 at 3:30
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.