From a0f684e00315920d8bc597fd00cc938d008b24d6 Mon Sep 17 00:00:00 2001 From: Bjorn Winckler Date: Sun, 1 Jun 2008 15:57:21 +0200 Subject: [PATCH] Add autoenabling for menus --- src/MacVim/MMAppController.m | 36 +----- src/MacVim/MMTextView.m | 12 ++ src/MacVim/MMVimController.h | 1 - src/MacVim/MMVimController.m | 193 +++++++++----------------------- src/MacVim/MMWindowController.m | 8 ++ src/MacVim/gui_macvim.m | 11 +- 6 files changed, 80 insertions(+), 181 deletions(-) diff --git a/src/MacVim/MMAppController.m b/src/MacVim/MMAppController.m index 2128366fe2..3ad8e2e493 100644 --- a/src/MacVim/MMAppController.m +++ b/src/MacVim/MMAppController.m @@ -86,9 +86,6 @@ static int executeInLoginShell(NSString *path, NSArray *args); - (void)passArguments:(NSDictionary *)args toVimController:(MMVimController*)vc; @end -@interface NSMenu (MMExtras) -- (void)recurseSetAutoenablesItems:(BOOL)on; -@end @interface NSNumber (MMExtras) - (int)tag; @@ -516,16 +513,7 @@ static int executeInLoginShell(NSString *path, NSArray *args); [vimControllers removeObject:controller]; if (![vimControllers count]) { - // Turn on autoenabling of menus (because no Vim is open to handle it), - // but do not touch the MacVim menu. Note that the menus must be - // enabled first otherwise autoenabling does not work. - NSMenu *mainMenu = [NSApp mainMenu]; - int i, count = [mainMenu numberOfItems]; - for (i = 1; i < count; ++i) { - NSMenuItem *item = [mainMenu itemAtIndex:i]; - [item setEnabled:YES]; - [[item submenu] recurseSetAutoenablesItems:YES]; - } + // TODO: change menu to default state } } @@ -1146,28 +1134,6 @@ static int executeInLoginShell(NSString *path, NSArray *args); -@implementation NSMenu (MMExtras) - -- (void)recurseSetAutoenablesItems:(BOOL)on -{ - [self setAutoenablesItems:on]; - - int i, count = [self numberOfItems]; - for (i = 0; i < count; ++i) { - NSMenuItem *item = [self itemAtIndex:i]; - [item setEnabled:YES]; - NSMenu *submenu = [item submenu]; - if (submenu) { - [submenu recurseSetAutoenablesItems:on]; - } - } -} - -@end // NSMenu (MMExtras) - - - - @implementation NSNumber (MMExtras) - (int)tag { diff --git a/src/MacVim/MMTextView.m b/src/MacVim/MMTextView.m index c9b3431bce..7460dbe05d 100644 --- a/src/MacVim/MMTextView.m +++ b/src/MacVim/MMTextView.m @@ -1160,6 +1160,18 @@ enum { [[self windowController] vimMenuItemAction:sender]; } +- (BOOL)validateMenuItem:(NSMenuItem *)item +{ + if ([item action] == @selector(cut:) + || [item action] == @selector(copy:) + || [item action] == @selector(paste:) + || [item action] == @selector(undo:) + || [item action] == @selector(redo:) + || [item action] == @selector(selectAll:)) + return [item tag]; + + return YES; +} @end // MMTextView diff --git a/src/MacVim/MMVimController.h b/src/MacVim/MMVimController.h index 74ef10691a..23f045696d 100644 --- a/src/MacVim/MMVimController.h +++ b/src/MacVim/MMVimController.h @@ -39,7 +39,6 @@ int resendMsgid; NSData *resendData; #endif - NSMenu *lastMenuSearched; NSMenuItem *recentFilesMenuItem; NSMenuItem *recentFilesDummy; NSDictionary *vimState; diff --git a/src/MacVim/MMVimController.m b/src/MacVim/MMVimController.m index 022145f5af..f0dba25e93 100644 --- a/src/MacVim/MMVimController.m +++ b/src/MacVim/MMVimController.m @@ -61,9 +61,6 @@ static NSTimeInterval MMResendInterval = 0.5; - (void)savePanelDidEnd:(NSSavePanel *)panel code:(int)code context:(void *)context; - (void)alertDidEnd:(MMAlert *)alert code:(int)code context:(void *)context; -- (NSMenuItem *)recurseMenuItemForTag:(int)tag rootMenu:(NSMenu *)root; -- (NSMenuItem *)menuItemForTag:(int)tag; -- (NSMenu *)menuForTag:(int)tag; - (NSMenuItem *)menuItemForDescriptor:(NSArray *)desc; - (NSMenu *)parentMenuForDescriptor:(NSArray *)desc; - (NSMenu *)topLevelMenuForTitle:(NSString *)title; @@ -77,7 +74,7 @@ static NSTimeInterval MMResendInterval = 0.5; action:(NSString *)action isAlternate:(BOOL)isAlternate; - (void)removeMenuItemWithDescriptor:(NSArray *)desc; -- (NSToolbarItem *)toolbarItemForTag:(int)tag index:(int *)index; +- (void)enableMenuItemWithDescriptor:(NSArray *)desc state:(BOOL)on; - (void)addToolbarItemToDictionaryWithLabel:(NSString *)title toolTip:(NSString *)tip icon:(NSString *)icon; - (void)addToolbarItemWithLabel:(NSString *)label @@ -92,7 +89,9 @@ static NSTimeInterval MMResendInterval = 0.5; @interface NSToolbar (MMExtras) -- (void)removeItemWithItemIdentifier:(NSString *)identifier; +- (int)indexOfItemWithItemIdentifier:(NSString *)identifier; +- (NSToolbarItem *)itemAtIndex:(int)idx; +- (NSToolbarItem *)itemWithItemIdentifier:(NSString *)identifier; @end @@ -596,12 +595,11 @@ static NSTimeInterval MMResendInterval = 0.5; // TODO! Need to look for 'Window' in all localized languages. NSMenu *windowMenu = [[mainMenu itemWithTitle:@"Window"] submenu]; if (windowMenu) { - // Remove all AppKit owned menu items (tag == 0); they will be added - // again when setWindowsMenu: is called. + // Remove all items that are added when setWindowsMenu: is called. count = [windowMenu numberOfItems]; for (i = count-1; i >= 0; --i) { NSMenuItem *item = [windowMenu itemAtIndex:i]; - if (![item tag]) { + if ([item action] == @selector(makeKeyAndOrderFront:)) { [windowMenu removeItem:item]; } } @@ -680,49 +678,12 @@ static NSTimeInterval MMResendInterval = 0.5; action:[attrs objectForKey:@"action"] isAlternate:[[attrs objectForKey:@"isAlternate"] boolValue]]; } else if (RemoveMenuItemMsgID == msgid) { -#if 0 - const void *bytes = [data bytes]; - int tag = *((int*)bytes); bytes += sizeof(int); - - id item; - int idx; - if ((item = [self toolbarItemForTag:tag index:&idx])) { - [toolbar removeItemAtIndex:idx]; - } else if ((item = [self menuItemForTag:tag])) { - [item retain]; - - if ([item menu] == [NSApp mainMenu] || ![item menu]) { - // NOTE: To be on the safe side we try to remove the item from - // both arrays (it is ok to call removeObject: even if an array - // does not contain the object to remove). - [mainMenuItems removeObject:item]; - [popupMenuItems removeObject:item]; - } - - if ([item menu]) - [[item menu] removeItem:item]; - - [item release]; - } - - // Reset cached menu, just to be on the safe side. - lastMenuSearched = nil; -#else NSDictionary *attrs = [NSDictionary dictionaryWithData:data]; [self removeMenuItemWithDescriptor:[attrs objectForKey:@"descriptor"]]; -#endif } else if (EnableMenuItemMsgID == msgid) { -#if 0 - const void *bytes = [data bytes]; - int tag = *((int*)bytes); bytes += sizeof(int); - int state = *((int*)bytes); bytes += sizeof(int); - - id item = [self toolbarItemForTag:tag index:NULL]; - if (!item) - item = [self menuItemForTag:tag]; - - [item setEnabled:state]; -#endif + NSDictionary *attrs = [NSDictionary dictionaryWithData:data]; + [self enableMenuItemWithDescriptor:[attrs objectForKey:@"descriptor"] + state:[[attrs objectForKey:@"enable"] boolValue]]; } else if (ShowToolbarMsgID == msgid) { const void *bytes = [data bytes]; int enable = *((int*)bytes); bytes += sizeof(int); @@ -928,76 +889,6 @@ static NSTimeInterval MMResendInterval = 0.5; } } -- (NSMenuItem *)recurseMenuItemForTag:(int)tag rootMenu:(NSMenu *)root -{ - if (root) { - NSMenuItem *item = [root itemWithTag:tag]; - if (item) { - lastMenuSearched = root; - return item; - } - - NSArray *items = [root itemArray]; - unsigned i, count = [items count]; - for (i = 0; i < count; ++i) { - item = [items objectAtIndex:i]; - if ([item hasSubmenu]) { - item = [self recurseMenuItemForTag:tag - rootMenu:[item submenu]]; - if (item) { - lastMenuSearched = [item submenu]; - return item; - } - } - } - } - - return nil; -} - -- (NSMenuItem *)menuItemForTag:(int)tag -{ - // First search the same menu that was search last time this method was - // called. Since this method is often called for each menu item in a - // menu this can significantly improve search times. - if (lastMenuSearched) { - NSMenuItem *item = [self recurseMenuItemForTag:tag - rootMenu:lastMenuSearched]; - if (item) return item; - } - - // Search the main menu. - int i, count = [mainMenuItems count]; - for (i = 0; i < count; ++i) { - NSMenuItem *item = [mainMenuItems objectAtIndex:i]; - if ([item tag] == tag) return item; - item = [self recurseMenuItemForTag:tag rootMenu:[item submenu]]; - if (item) { - lastMenuSearched = [item submenu]; - return item; - } - } - - // Search the popup menus. - count = [popupMenuItems count]; - for (i = 0; i < count; ++i) { - NSMenuItem *item = [popupMenuItems objectAtIndex:i]; - if ([item tag] == tag) return item; - item = [self recurseMenuItemForTag:tag rootMenu:[item submenu]]; - if (item) { - lastMenuSearched = [item submenu]; - return item; - } - } - - return nil; -} - -- (NSMenu *)menuForTag:(int)tag -{ - return [[self menuItemForTag:tag] submenu]; -} - - (NSMenuItem *)menuItemForDescriptor:(NSArray *)desc { if (!(desc && [desc count] > 0)) return nil; @@ -1106,7 +997,6 @@ static NSTimeInterval MMResendInterval = 0.5; NSMenuItem *item = [[NSMenuItem alloc] init]; NSMenu *menu = [[NSMenu alloc] initWithTitle:title]; - [menu setAutoenablesItems:NO]; [item setTitle:title]; [item setSubmenu:menu]; @@ -1188,14 +1078,10 @@ static NSTimeInterval MMResendInterval = 0.5; [item setKeyEquivalentModifierMask:modifierMask]; } [item setAlternate:isAlternate]; + [item setTag:1]; // 'tag != 0' means item is enabled } } - // NOTE! The tag is used to idenfity which menu items were - // added by Vim (tag != 0) and which were added by the AppKit - // (tag == 0). - [item setTag:-1]; - if ([parent numberOfItems] <= idx) { [parent addItem:item]; } else { @@ -1215,7 +1101,9 @@ static NSTimeInterval MMResendInterval = 0.5; [windowController setToolbar:nil]; [toolbar release]; toolbar = nil; } else if ([desc count] == 2) { - [toolbar removeItemWithItemIdentifier:title]; + int idx = [toolbar indexOfItemWithItemIdentifier:title]; + if (idx != NSNotFound) + [toolbar removeItemAtIndex:idx]; } } return; @@ -1244,21 +1132,26 @@ static NSTimeInterval MMResendInterval = 0.5; [item release]; } -- (NSToolbarItem *)toolbarItemForTag:(int)tag index:(int *)index +- (void)enableMenuItemWithDescriptor:(NSArray *)desc state:(BOOL)on { - if (!toolbar) return nil; + if (!(desc && [desc count] > 0)) return; - NSArray *items = [toolbar items]; - int i, count = [items count]; - for (i = 0; i < count; ++i) { - NSToolbarItem *item = [items objectAtIndex:i]; - if ([item tag] == tag) { - if (index) *index = i; - return item; + /*NSLog(@"%sable item %@", on ? "En" : "Dis", + [desc componentsJoinedByString:@"->"]);*/ + + NSString *rootName = [desc objectAtIndex:0]; + if ([rootName isEqual:@"ToolBar"]) { + if (toolbar && [desc count] == 2) { + NSString *title = [desc lastObject]; + [[toolbar itemWithItemIdentifier:title] setEnabled:on]; } + } else { + // Use tag to set whether item is enabled or disabled instead of + // calling setEnabled:. This way the menus can autoenable themselves + // but at the same time Vim can set if a menu is enabled whenever it + // wants to. + [[self menuItemForDescriptor:desc] setTag:on]; } - - return nil; } - (void)addToolbarItemToDictionaryWithLabel:(NSString *)title @@ -1272,7 +1165,6 @@ static NSTimeInterval MMResendInterval = 0.5; return; NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:title]; - [item setTag:-1]; [item setLabel:title]; [item setToolTip:tip]; [item setAction:@selector(vimMenuItemAction:)]; @@ -1375,18 +1267,35 @@ static NSTimeInterval MMResendInterval = 0.5; @implementation NSToolbar (MMExtras) -- (void)removeItemWithItemIdentifier:(NSString *)identifier + +- (int)indexOfItemWithItemIdentifier:(NSString *)identifier { NSArray *items = [self items]; int i, count = [items count]; for (i = 0; i < count; ++i) { id item = [items objectAtIndex:i]; - if ([[item identifier] isEqual:identifier]) { - [self removeItemAtIndex:i]; - break; - } + if ([[item itemIdentifier] isEqual:identifier]) + return i; } + + return NSNotFound; } + +- (NSToolbarItem *)itemAtIndex:(int)idx +{ + NSArray *items = [self items]; + if (idx < 0 || idx >= [items count]) + return nil; + + return [items objectAtIndex:idx]; +} + +- (NSToolbarItem *)itemWithItemIdentifier:(NSString *)identifier +{ + int idx = [self indexOfItemWithItemIdentifier:identifier]; + return idx != NSNotFound ? [self itemAtIndex:idx] : nil; +} + @end // NSToolbar (MMExtras) diff --git a/src/MacVim/MMWindowController.m b/src/MacVim/MMWindowController.m index b1b2ecf437..ede1a753a6 100644 --- a/src/MacVim/MMWindowController.m +++ b/src/MacVim/MMWindowController.m @@ -636,6 +636,14 @@ [vimController sendMessage:ExecuteMenuMsgID data:[attrs dictionaryAsData]]; } +- (BOOL)validateMenuItem:(NSMenuItem *)item +{ + if ([item action] == @selector(vimMenuItemAction:) + || [item action] == @selector(performClose:)) + return [item tag]; + + return YES; +} // -- NSWindow delegate ------------------------------------------------------ diff --git a/src/MacVim/gui_macvim.m b/src/MacVim/gui_macvim.m index 63a5cc09ba..99aa689957 100644 --- a/src/MacVim/gui_macvim.m +++ b/src/MacVim/gui_macvim.m @@ -743,12 +743,17 @@ gui_mch_menu_grey(vimmenu_T *menu, int grey) /* Only update menu if the 'grey' state has changed to avoid having to pass * lots of unnecessary data to MacVim. (Skipping this test makes MacVim * pause noticably on mode changes. */ - if (menu->was_grey != grey) return; + NSArray *desc = descriptor_for_menu(menu); + if (menu->was_grey == grey) + return; menu->was_grey = grey; + [[MMBackend sharedInstance] queueMessage:EnableMenuItemMsgID properties: - [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:!grey] - forKey:@"enable"]]; + [NSDictionary dictionaryWithObjectsAndKeys: + desc, @"descriptor", + [NSNumber numberWithInt:!grey], @"enable", + nil]]; }