tags:

views:

54

answers:

2

my Application provides a Global Service. I'd like to install the service with a command-alt-key combination. the thing i do now is not very error prone and really hard to debug as don't really see what's happening:

inside Info.plist:
 <key>NSServices</key>
 <array>
  <dict>
   <key>NSSendTypes</key>
   <array>
    <string></string>
   </array>
   <key>NSReturnTypes</key>
   <array>
    <string></string>
   </array>
   <key>NSMenuItem</key>
   <dict>
    <key>default</key>
    <string>Go To Window in ${PRODUCT_NAME}</string>
   </dict>
   <key>NSMessage</key>
   <string>bringZFToForegroundZoomOut</string>
   <key>NSPortName</key>
   <string>com.raskinformac.${PRODUCT_NAME:identifier}</string>
  </dict>
 </array>

and in the code:

CFStringRef serviceStatusName = (CFStringRef)[NSString stringWithFormat:@"%@ - %@ - %@", appIdentifier, appName, methodNameForService];
CFStringRef serviceStatusRoot =  CFSTR("NSServicesStatus");
CFPropertyListRef pbsAllServices = (CFPropertyListRef) CFMakeCollectable ( CFPreferencesCopyAppValue(serviceStatusRoot, CFSTR("pbs")) );
// the user did not configure any custom services
BOOL otherServicesDefined = pbsAllServices != NULL;
BOOL ourServiceDefined = NO;
if ( otherServicesDefined ) {
    ourServiceDefined = NULL != CFDictionaryGetValue((CFDictionaryRef)pbsAllServices, serviceStatusName);
}

NSUpdateDynamicServices();
NSMutableDictionary *pbsAllServicesNew = nil;
if (otherServicesDefined) {
    pbsAllServicesNew = [NSMutableDictionary dictionaryWithDictionary:(NSDictionary*)pbsAllServices];
} else {
   pbsAllServicesNew = [NSMutableDictionary dictionaryWithCapacity:1];
}

NSDictionary *serviceStatus = [NSDictionary dictionaryWithObjectsAndKeys:
                               (id)kCFBooleanTrue, @"enabled_context_menu", 
                               (id)kCFBooleanTrue, @"enabled_services_menu", 
                               @"@~r", @"key_equivalent", nil];
[pbsAllServicesNew setObject:serviceStatus forKey:(NSString*)serviceStatusName];
CFPreferencesSetAppValue (
                          serviceStatusRoot,
                          (CFPropertyListRef) pbsAllServicesNew,
                          CFSTR("pbs"));
Boolean result = CFPreferencesAppSynchronize(CFSTR("pbs"));
if (result) {
    NSUpdateDynamicServices();
    JLog(@"successfully installed our alt-command-R service");
} else {
    ALog(@"couldn't install our alt-command-R service");
}

and to change the service:

// once installed, its's a bit tricky to set new ones (works only in RELEASE somehow?)
// quit finder
// open "~/Library/Preferences/pbs.plist" and remove ch.ana.Zoom - Reveal Window in Zoom - bringZFToForegroundZoomOut inside NSServicesStatus and save
// start app
// /System/Library/CoreServices/pbs -dump_pboard (to see if it hat actually done what we wanted, might be empty)
// /System/Library/CoreServices/pbs  (to add the new services)
// /System/Library/CoreServices/pbs -dump_pboard  (see new linking)
// and then /System/Library/CoreServices/Finder.app/Contents/MacOS/Finder  -NSDebugServices MY.APP.IDENTIFIER  to restart finder

so my question: is there a easier way to enable a Service with cmd-option-key? if yes, i'd gladly implement it in my software.

A: 

You might try just setting your key equivalent string with ‘~’ as the value for NSKeyEquivalent in the service dictionary. The Services Implementation Guide claims it must be a single character, but I'd consider it worth trying the full key equivalent string.

Remember that, like NSMenuItem, it must be wrapped in a dictionary:

<key>NSKeyEquivalent</key>
<dict>
    <key>default</key>
    <string>~r</string>
</dict>

(I've omitted the ‘@’; if it doesn't work without it, you could try it with it.)

If it does work, I'd suggest filing a bug against the documentation.

It's possible that Apple wants all services to use either ⌘-something or ⇧⌘-something key equivalents, leaving other modifiers for menu items and global hot keys. So, while I'd try the above, I wouldn't be too surprised if it doesn't work.

Peter Hosey
Peter, thank you very much for the answer. i tried ~ and @~ which both did not work out. But i found the problem: see my answer.
mahal tertin
A: 
<key>NSMenuItem</key>
       <dict>
        <key>default</key>
        <string>Go To Window in ${PRODUCT_NAME}</string>
       </dict>

and the middle component of serviceStatusName have to be exactly the same. i changed the code in the question.

NSString *appServiceName = @"Go To Window in ${PRODUCT_NAME}"
CFStringRef serviceStatusName = (CFStringRef)[NSString stringWithFormat:@"%@ - %@ - %@", appIdentifier, appServiceName, methodNameForService];
mahal tertin