Cocoa Keyboard Shortcuts in Dialog without an Edit Menu

Kristopher Johnson picture Kristopher Johnson · Jun 9, 2009 · Viewed 13.2k times · Source

I have an LSUIElement application that displays a menubar status item. The application can display a dialog window that contains a text field.

If the user right-clicks/control-clicks the text field, a menu appears that allows cut, copy, paste, etc. However, the standard Command-X, Command-C, and Command-V keyboard shortcuts do not work in the field. I assume this is because my application does not provide an Edit menu with those shortcuts defined.

I've tried adding an Edit menu item to my application's menu, as suggested in the Ship Some Code blog, but that did not work. The menu items in the Edit menu can be used, but keyboard shortcuts still don't work.

I can imagine a few ways to hack the keyboard handling, but is there a "recommended" way to make this work?

(For details about the app, see Menubar Countdown.)

Related question: Copy/Paste Not Working in Modal Window

Answer

Adrian picture Adrian · Jul 5, 2010

Improving on that CocoaRocket solution:

The following saves having to subclass NSTextField and remembering to use the subclass throughout your application; it will also enable copy, paste and friends for other responders that handle them, eg. NSTextView.

Put this in a subclass of NSApplication and alter the principal class in your Info.plist accordingly.

- (void) sendEvent:(NSEvent *)event {
    if ([event type] == NSKeyDown) {
        if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) == NSCommandKeyMask) {
            if ([[event charactersIgnoringModifiers] isEqualToString:@"x"]) {
                if ([self sendAction:@selector(cut:) to:nil from:self])
                    return;
            }
            else if ([[event charactersIgnoringModifiers] isEqualToString:@"c"]) {
                if ([self sendAction:@selector(copy:) to:nil from:self])
                    return;
            }
            else if ([[event charactersIgnoringModifiers] isEqualToString:@"v"]) {
                if ([self sendAction:@selector(paste:) to:nil from:self])
                    return;
            }
            else if ([[event charactersIgnoringModifiers] isEqualToString:@"z"]) {
                if ([self sendAction:@selector(undo:) to:nil from:self])
                    return;
            }
            else if ([[event charactersIgnoringModifiers] isEqualToString:@"a"]) {
                if ([self sendAction:@selector(selectAll:) to:nil from:self])
                    return;
            }
        }
        else if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSShiftKeyMask)) {
            if ([[event charactersIgnoringModifiers] isEqualToString:@"Z"]) {
                if ([self sendAction:@selector(redo:) to:nil from:self])
                    return;
            }
        }
    }
    [super sendEvent:event];
}

// Blank Selectors to silence Xcode warnings: 'Undeclared selector undo:/redo:'
- (IBAction)undo:(id)sender {}
- (IBAction)redo:(id)sender {}