| // 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" |
| #import "ui/base/cocoa/nsmenuitem_additions.h" |
| |
| namespace { |
| |
| void (^g_pre_search_block)(void); |
| |
| 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 = NSApp.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.target = self; |
| menuItem.action = @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.target = origTarget; |
| menuItem.action = origAction; |
| |
| return YES; |
| } |
| |
| + (void)cr_executeDummyCommand:(id)sender { |
| } |
| |
| @end |