| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "ui/base/cocoa/nsmenu_additions.h" |
| |
| #include "base/check.h" |
| #include "base/mac/scoped_nsobject.h" |
| #import "ui/base/cocoa/nsmenuitem_additions.h" |
| |
| namespace { |
| |
| typedef void (^PreSearchBlock)(void); |
| |
| PreSearchBlock g_pre_search_block; |
| |
| NSMenuItem* MenuItemForKeyEquivalentEventInMenu(NSEvent* event, NSMenu* menu) { |
| NSMenuItem* result = nil; |
| |
| for (NSMenuItem* item in [menu itemArray]) { |
| NSMenu* submenu = [item submenu]; |
| if (submenu) { |
| if (submenu != [NSApp servicesMenu]) |
| result = MenuItemForKeyEquivalentEventInMenu(event, submenu); |
| } else if ([item cr_firesForKeyEquivalentEvent:event]) { |
| result = item; |
| } |
| |
| if (result) |
| break; |
| } |
| |
| return result; |
| } |
| |
| // Searches |menu| and its submenus for a NSMenuItem with |tag|. |
| // Returns the menu item or nil if no menu item matches. |
| NSMenuItem* MenuItemWithTagInMenu(int tag, NSMenu* menu) { |
| for (NSMenuItem* item in [menu itemArray]) { |
| if ([item tag] == tag) |
| return item; |
| |
| if ([item hasSubmenu]) { |
| NSMenuItem* the_item = MenuItemWithTagInMenu(tag, [item submenu]); |
| if (the_item != nil) |
| return the_item; |
| } |
| } |
| |
| return nil; |
| } |
| |
| NSMenuItem* MenuItemWithTag(int tag) { |
| NSMenu* mainMenu = [[NSApplication sharedApplication] mainMenu]; |
| |
| // Validate menu items before searching. |
| [mainMenu update]; |
| |
| return MenuItemWithTagInMenu(tag, mainMenu); |
| } |
| |
| } // namespace |
| |
| @implementation NSMenu (ChromeAdditions) |
| |
| + (void)cr_setMenuItemForKeyEquivalentEventPreSearchBlock: |
| (void (^)(void))block { |
| if (block != nil) |
| CHECK(g_pre_search_block == nil); |
| g_pre_search_block = block; |
| } |
| |
| - (NSMenuItem*)cr_menuItemForKeyEquivalentEvent:(NSEvent*)event { |
| if ([event type] != NSEventTypeKeyDown) |
| return nil; |
| |
| if (g_pre_search_block) { |
| g_pre_search_block(); |
| } |
| |
| // Validate menu items before searching. |
| [self update]; |
| |
| return MenuItemForKeyEquivalentEventInMenu(event, self); |
| } |
| |
| + (BOOL)flashMenuForChromeCommand:(int)chromeCommand { |
| NSMenuItem* menuItem = MenuItemWithTag(chromeCommand); |
| if (menuItem == nil) |
| return NO; |
| |
| // Swap out the menu item's existing target/action for a fake pair, |
| // so that we can flash the menu item without executing anything. |
| id origTarget = [menuItem target]; |
| SEL origAction = [menuItem action]; |
| [menuItem setTarget:self]; |
| [menuItem setAction:@selector(cr_executeDummyCommand:)]; |
| |
| // -performActionForItemAtIndex: is documented as triggering highlighting in |
| // the menu bar as well as sending out appropriate accessibility notifications |
| // indicating the item was selected. |
| NSMenu* owningMenu = [menuItem menu]; |
| [owningMenu performActionForItemAtIndex:[owningMenu indexOfItem:menuItem]]; |
| |
| // Restore. |
| [menuItem setTarget:origTarget]; |
| [menuItem setAction:origAction]; |
| |
| return YES; |
| } |
| |
| + (void)cr_executeDummyCommand:(id)sender { |
| } |
| |
| @end |