OK, I tried the suggestion to do the update in windowDidBecomeKey and this works nicely with the following (perhaps a little ugly) code:
- (void) specialiseCloseMenu:(BOOL)state { NSMenu *mainMenu = [[NSApplication sharedApplication] mainMenu]; NSMenu *fileMenu = [[mainMenu itemAtIndex:1] submenu]; NSMenuItem *closeMenuItem = [fileMenu itemWithTag:1090]; NSMenuItem *closeTabMenuItem = [fileMenu itemWithTag:1110]; if (state) { [closeMenuItem setKeyEquivalent:@"W"]; [closeTabMenuItem setKeyEquivalent:@"w"]; } else { [closeMenuItem setKeyEquivalent:@"w"]; [closeTabMenuItem setKeyEquivalent:@""]; } } - (void) windowDidBecomeKey:(NSNotification *)notification { [self specialiseCloseMenu:YES]; } - (void) windowDidResignKey:(NSNotification *)notification { [self specialiseCloseMenu:NO]; } Thanks all for your help and thoughts. Cheers, Martin On 20 Oct 2013, at 01:21 pm, Martin Hewitson <martin.hewit...@aei.mpg.de> wrote: > > On 20 Oct 2013, at 01:15 am, Kyle Sluder <k...@ksluder.com> wrote: > >> Rather than rely on intercepting responder chain-based validation, wouldn't >> it be much easier and more reliable to make some object the delegate of all >> of your NSMenus and implement -menuNeedsUpdate:? >> > > But wouldn’t this object then need to know details about the different > windows which are presented in the app in order to decide which shortcut key > to set? I have the feeling that the tabbed window (or its delegate) is the > place to make changes to the default menu configuration. I think I will try > updating the menu in the windowDidBecomeKey/windowDidResignKey calls of the > window’s delegate. The thing I don’t like about this is that the delegate (my > document subclass) now needs to have a link to the menu items which are > created in the main menu nib. I could try ’scanning' for the relevant menu > items, maybe searching by tag. Let’s see how far I get with that. > > Thanks! > > Martin > > >> --Kyle Sluder >> >>> On Oct 19, 2013, at 1:28 PM, Andy Lee <ag...@mac.com> wrote: >>> >>> Uli and I both remembered the app delegate is checked *after* the >>> window-related objects in the responder chain, which is what you discovered. >>> >>> I *thought* you could work around this by changing the actions of the Close >>> menu items to methods that only the app delegate implements. But you >>> really don't want to do that, because it'll break any parts of the built-in >>> menu validation that assume performClose: is the action for closing windows. >>> >>> So I propose yet another solution, which may well be, again, flawed. It >>> does not go through validateMenuItem:. The following may seem like a lot >>> of explaining, but it is only a few one- or two-line methods. >>> >>> * Have the app delegate listen for NSWindowDidBecomeMainNotification. >>> * Handle the notification by setting the menu item shortcuts appropriately >>> for the main window. >>> >>> There are different approaches you could take to set the shortcuts >>> appropriately for the window. One way would be to add two category methods >>> on NSWindow, something like >>> >>> + (void)updateShortcutForCloseMenuItem:(NSMenuItem *)menuItem >>> { >>> [menuItem setKeyEquivalent:@"w"]; >>> } >>> >>> + (void)updateShortcutForCloseTabMenuItem:(NSMenuItem *)menuItem >>> { >>> [menuItem setKeyEquivalent:@""]; >>> } >>> >>> For your window that has tabs, use an NSWindow subclass that overrides >>> these methods: >>> >>> + (void)updateShortcutForCloseMenuItem:(NSMenuItem *)menuItem >>> { >>> [menuItem setKeyEquivalent:@"W"]; >>> } >>> >>> + (void)updateShortcutForCloseTabMenuItem:(NSMenuItem *)menuItem >>> { >>> [menuItem setKeyEquivalent:@"w"]; >>> } >>> >>> Your app delegate could have outlets to the two menu items in question. >>> Then your notification-handling method can be: >>> >>> - (void)handleWindowDidBecomeMainNotification:(NSNotification *)notif >>> { >>> [[[NSApp mainWindow] class] updateShortcutForCloseMenuItem:_closeMenuItem]; >>> [[[NSApp mainWindow] class] >>> updateShortcutForCloseTabMenuItem:_closeTabMenuItem]; >>> } >>> >>> I think the only case this does not handle is when the very last window is >>> closed. You may or may not care what the shortcuts are, since the menu >>> items will be dimmed. If you do care, you can: >>> >>> * Have the app delegate listen for NSWindowDidResignMainNotification. >>> * Handle the notification by first checking whether [NSApp mainWindow] is >>> nil. If so (and *only* if so, for the reason Uli pointed out), set the >>> shortcuts to whatever you want them to be in that case. For example, you >>> could do this (which is why I made those "update" methods class methods): >>> >>> - (void)handleWindowDidResignMainNotification:(NSNotification *)notif >>> { >>> if ([NSApp mainWindow] == nil) >>> { >>> [NSWindow updateShortcutForCloseMenuItem:_closeMenuItem]; >>> [NSWindow updateShortcutForCloseTabMenuItem:_closeTabMenuItem]; >>> } >>> } >>> >>> --Andy >>> >>> >>>> On Oct 19, 2013, at 2:32 PM, Martin Hewitson <martin.hewit...@aei.mpg.de> >>>> wrote: >>>> >>>> I guess I didn’t understand correctly since my app delegate does not get >>>> asked to validate the Close menu item. So far the only thing that get’s >>>> asked to validate this is the tabbed window object. Even the window’s >>>> delegate is not asked. >>>> >>>> The documentation states: >>>> >>>> For document-based applications, the default responder chain for the main >>>> window consists of the following responders and delegates: >>>> • The main window’s first responder and the successive responder objects >>>> up the view hierarchy >>>> • The main window itself >>>> • The window's NSWindowController object (which inherits from NSResponder) >>>> • The main window’s delegate. >>>> • The NSDocument object (if different from the main window’s delegate) >>>> • The application object, NSApp >>>> • The application object's delegate >>>> • The application's document controller (an NSDocumentController object, >>>> which does not inherit from NSResponder) >>>> >>>> My NSPersistentDocument subclass is the main window’s delegate. >>>> >>>> Clearly my thinking is flawed, but where? If I want the Close menu item to >>>> vary depending on the window that is key, and I have to do this with >>>> validateMenuItem:, then the responder chain above seems to suggest that I >>>> need to have a validateMenuItem: in each and every window in the app. I >>>> hope that’s not the case…. >>>> >>>> Martin >>>> >>>> >>>>> On 19 Oct 2013, at 07:28 pm, Martin Hewitson <martin.hewit...@aei.mpg.de> >>>>> wrote: >>>>> >>>>> OK, so the idea is, >>>>> >>>>> + validateMenuItem in app delegate gets a first shot at setting the >>>>> keyboard shortcuts >>>>> + I override validateMenuItem in my tabbed window and reset the keyboard >>>>> shortcuts >>>>> + Other windows stick with the settings arranged by the app delegate >>>>> >>>>> Did I understand correctly? >>>>> >>>>> Thanks to all who replied. >>>>> >>>>> Cheers, >>>>> >>>>> Martin >>>>> >>>>> >>>>>> On 19, Oct, 2013, at 02:46 pm, Uli Kusterer >>>>>> <witness.of.teacht...@gmx.net> wrote: >>>>>> >>>>>> >>>>>>> On 19 Oct 2013, at 14:27, Andy Lee <ag...@mac.com> wrote: >>>>>>>> On Oct 19, 2013, at 6:58 AM, Martin Hewitson >>>>>>>> <martin.hewit...@aei.mpg.de> wrote: >>>>>>>> Main Window with tabs: >>>>>>>> close (cmd-shift-w) >>>>>>>> close tab (cmd-w) >>>>>>>> >>>>>>>> All other windows: >>>>>>>> close (cmd-w) >>>>>>>> close tab (inactive, no keyboard shortcut) >>>>>>>> >>>>>>>> This is pretty much the way things work in Xcode. >>>>>>>> >>>>>>>> So, my question is, is there a smart way to do this, or do I need to >>>>>>>> implement -validateMenuItem: on every window in the app and set the >>>>>>>> keyboard shortcuts there? >>>>>>> >>>>>>> Untested idea: implement windowDidBecomeKey: and windowDidResignKey: in >>>>>>> the delegate of the window that has tabs and do the switching of >>>>>>> shortcuts there. >>>>>> >>>>>> Would rather recommend against this. I don’t think there’s any guarantee >>>>>> given what gets called first, validateMenuItem: or windowDidResignKey:. >>>>>> You might be obliterating something already set by the incoming window. >>>>>> >>>>>>> If you want to be extra careful you could have two ivars that remember >>>>>>> what the shortcuts were before you changed them to cmd-shift-w and >>>>>>> cmd-w. Then in windowDidResignKey: plug those shortcuts in rather than >>>>>>> hard-code cmd-w and @“”. >>>>>> >>>>>> Also, while I’m not aware of any localization that doesn’t use Cmd-W for >>>>>> close, it’s in general a good idea to keep your shortcuts localizable >>>>>> (e.g. on a German keyboard, there’s no way to type Cmd-~, because it has >>>>>> no ~-key, so window rotation is done using Cmd-< there). >>>>>> >>>>>> I’d recommend adding a validateMenuItem: handler in the application >>>>>> delegate, as long as you’re aware that this won’t be called for modal >>>>>> panels or windows that have sheets on them, but since those generally >>>>>> don’t enable the “Close” menu item, you should be fine. At least then >>>>>> you’re modifying the items. >>>>>> >>>>>> Also, I’m not sure whether menu shortcut disambiguation lets you do >>>>>> that, but have you tried hiding and showing menu items with the same >>>>>> names but different shortcuts instead of actually changing the items’ >>>>>> shortcut? I.e. create your menu as >>>>>> >>>>>> Close Cmd-Shift-W -> -performCloseForTabbedWindow: >>>>>> Close Cmd-W -> -performClose: >>>>>> Close Tab Cmd-W -> -performCloseTab: >>>>>> Close Tab -> -performCloseTabDummy: >>>>>> >>>>>> And then hide/show the “tab” items? That way, localization would be >>>>>> easier, and you’re not hard-coding any shortcuts. Though that doesn’t >>>>>> solve the issue of properly restoring the items after you’ve hidden them. >>>>>> >>>>>> Cheers, >>>>>> -- Uli Kusterer >>>>>> “The Witnesses of TeachText are everywhere...” >>>>>> http://zathras.de >>>>> > _______________________________________________ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com