Enabling keyboard shortcuts with NSStatusItems

January 4th, 2007

It all started when a friend who has been helping me out with testing and ideas for improving MythGrowl noticed that it was not possible to paste into either the ‘MythTV Server IP/Hostname’ or ‘MythTV Port’ box which are both NSTextFields. When you push APPLE+V the system would just make it’s standard ‘ding’ noise and whatever was on the clipboard wouldn’t appear, I imagined that changing some property of the NSTextFields would fix the problem right up.

As it happened I couldn’t have been more wrong and a seemingly simple problem took me an entire morning to solve. The next thing I noticed was that if you right clicked in the field and clicked paste from the menu then whatever text that was currently on the clipboard would appear in the text field just fine. Strange, copy and pasting is working fine, but the shortcuts aren’t working at all. After some more reading and fiddling around I figured out that modifier key shortcuts (Apple+whatever, Option+whatever, Ctrl+whatever) are handled a long time before they get to an NSTextField.

OK, fine, so why aren’t they being handled and why aren’t the blasted shortcuts working?

The one major thing different about my application (and any application with only an NSStatusItem) is that it has no main application menu with an ‘Application’, ‘File’ and ‘Edit’ etc. menu showing along the top. Now, the application ‘Edit’ menu is the one with the ‘Copy’ and ‘Paste’ operations and corresponding key shortcuts. If your application has no ‘Edit’ menu with the operations you need under it then, believe it or not, the keyboard shortcuts won’t work anywhere in your application.

So, I tried dragging an ‘Edit’ menu onto my MythGrowl status item menu using the interface builder and then tried running my app and copying and pasting into the boxes. And it works! But now I have a problem, I don’t want my app to have an ugly, unneeded ‘Edit’ menu, I just want to enable the keyboard shortcuts. So, I created a ‘dummy’ menu with an ‘Edit’ menu in it and added it as an outlet, now I just needed to figure out a way to tell my application that my ‘dummy’ menu will be taking over the role of my main application menu. After some more messing around I found that you can do this with [NSApp setMainMenu:yourDummyMenuName]; which I called when setting up my NSStatusItem.

I recompiled and to my absolute joy all the keyboard shortcuts worked. I could copy and paste in and out of the NSTextfields using the keyboard to my hearts content. Problem solved. The dummy menu seems a but of hack, but I really cannot see how it is to be avoided if you don’t want a visible ‘Edit’ menu.

I imagined that this must be a problem that other Cocoa developers using an NSStatusItem would have had and surely there would be a better way to fix this somehow. Expecting some enlightenment I checked to see if I was able to copy the text in the about boxes of Jumpcut and Alarm Clock 2 (two of my favorite OS X applications) to the clipboard using only the keyboard shortcuts. To my amazement I heard the familiar ‘ding’ noise and the text wouldn’t copy, I right clicked and copied some text to the clipboard using the mouse and it worked just fine. So it would seem I’m not the only developer who has had this problem. The creators of both Jumpcut and Alarm Clock 2 either:

1) Didn’t notice the problem or
2) Didn’t think it was enough of a problem to fix (I have to agree) given that both Alarm Clock 2 and Jumpcut hardly use any NSTextFields and copying text from the about boxes is not something a lot of people would need to do

How many other applications with an NSStatusItem have this problem I wonder? Was I really the first to find it? All of this sure seems totally insane to make something so trivial as copy and pasting from a couple of text fields work.

So, next time you open the preferences window of MythGrowl, be sure to copy and paste in out of the fields a couple of times, it will make all my efforts that much more worth while :-) and I sure hope this post saves some other Cocoa developer a lot of pain.

10 Responses to “Enabling keyboard shortcuts with NSStatusItems”

  1. Steve Says:

    Hey, the Jumpcut developer here — you’re right the first time; I never even considered this as a problem. (Thinking about it, I’m not sure that I do. What are the odds that someone would want to cut and paste out of my about box?) Thanks for the nice words re: Jumpcut, and best of luck with MythGrowl.

  2. John Says:

    Wow, thanks for the comment Steve. I use your application *every* day and really couldn’t work efficiently without it. Yes I don’t think it’s a problem at all in the case of your application. Thanks for your feedback and I’ll be sure to subscribe to your blog.

  3. blago Says:

    God bless you and Google. I was working on a menulet to intercept Apple Remote signals and forward them through XMPP to a remote machine when I noticed the same problem with my settings fields. You just saved me from what could have been hours of frustration and work.

  4. Administrator Says:

    Awesome! That’s great to hear. Thanks for commenting.

  5. Kristopher Johnson Says:

    Just had a user report this problem with my little Menubar Countdown application. Thanks for providing the answer.

  6. Max Howell Says:

    Thanks, worked a treat.

    I also went to the trouble instantiating the other menus in case people try to use those shortcuts too. Well I removed the stuff that made no sense for my app, like Find.

  7. Aaron Smith Says:

    thanks for this. i had to do some snooping around too. I figured out a slightly easier way.

    -Make a new cocoa application in xcode.
    -Drag the MainMenu into your real applications’ MainMenu nib. (Copys it)
    -Delete the sub items from MainMenu. (leave Main Menu, and Main Menu > Menu Item (Edit)).
    -Open the disclosure triangles on the menu item so you see the copy, paste, etc items.
    -Select the Copy item
    -Ctrl+Click on the Copy Item, and Drag it’s action to FirstResponder.
    -The action of “copy:” will automatically be selected.
    -Continue doing this for the rest of the items you want enabled.

    Now you end up with just the edit in the nib, it’s hooked up, and no code to set the menu.

  8. Stefan Klieme Says:

    An easier way is to subclass NSTextField and override its performKeyEquivalent: method with

    - (BOOL)performKeyEquivalent:(NSEvent *)event {
    if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) == NSCommandKeyMask) {
    if ([[event charactersIgnoringModifiers] isEqualToString:@”x”]) {
    return [NSApp sendAction:@selector(cut:) to:[[self window] firstResponder] from:self];
    } else if ([[event charactersIgnoringModifiers] isEqualToString:@”c”]) {
    return [NSApp sendAction:@selector(copy:) to:[[self window] firstResponder] from:self];
    } else if ([[event charactersIgnoringModifiers] isEqualToString:@”v”]) {
    return [NSApp sendAction:@selector(paste:) to:[[self window] firstResponder] from:self];
    } else if ([[event charactersIgnoringModifiers] isEqualToString:@”a”]) {
    return [NSApp sendAction:@selector(selectAll:) to:[[self window] firstResponder] from:self];
    }
    }
    return [super performKeyEquivalent:event];
    }

  9. alcomtilt Says:

    “And it works! But now I have a problem, I don’t want my app to have an ugly, unneeded ‘Edit’ menu, I just want to enable the keyboard shortcuts. So, I created a ‘dummy’ menu with an ‘Edit’ menu in it and added it as an outlet, now I just needed to figure out a way to tell my application that my ‘dummy’ menu will be taking over the role of my main application menu. After some more messing around I found that you can do this with [NSApp setMainMenu:yourDummyMenuName]; which I called when setting up my NSStatusItem.”
    You can read more about it?

  10. Ernst Cline Says:

    The easiest way I found around this problem is just putting some NSButton Objects on the layout, give them the appropriate Key Equivalent in the editor and hook ‘m up to the corresponding inputs from First Responder.
    After checking that everything works as expected make them transparent, non bordered and change the size to 0 by 0 pixels.

    Ugly but easy!

Leave a Reply