| 
									
										
										
										
											2014-10-31 11:17:05 -07:00
										 |  |  | // Copyright (c) 2013 GitHub, Inc. | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
					
						
							| 
									
										
										
										
											2014-04-25 17:49:37 +08:00
										 |  |  | // Use of this source code is governed by the MIT license that can be | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  | // found in the LICENSE file. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 13:46:59 -07:00
										 |  |  | #import "shell/browser/ui/cocoa/atom_menu_controller.h" | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "base/logging.h" | 
					
						
							|  |  |  | #include "base/strings/sys_string_conversions.h" | 
					
						
							| 
									
										
										
										
											2015-09-01 19:48:11 +08:00
										 |  |  | #include "base/strings/utf_string_conversions.h" | 
					
						
							| 
									
										
										
										
											2019-01-12 06:30:43 +05:30
										 |  |  | #include "base/task/post_task.h" | 
					
						
							|  |  |  | #include "content/public/browser/browser_task_traits.h" | 
					
						
							| 
									
										
										
										
											2018-02-25 14:19:40 -08:00
										 |  |  | #include "content/public/browser/browser_thread.h" | 
					
						
							| 
									
										
										
										
											2019-06-19 13:46:59 -07:00
										 |  |  | #include "shell/browser/mac/atom_application.h" | 
					
						
							|  |  |  | #include "shell/browser/ui/atom_menu_model.h" | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  | #include "ui/base/accelerators/accelerator.h" | 
					
						
							|  |  |  | #include "ui/base/accelerators/platform_accelerator_cocoa.h" | 
					
						
							|  |  |  | #include "ui/base/l10n/l10n_util_mac.h" | 
					
						
							| 
									
										
										
										
											2015-07-29 12:01:27 +08:00
										 |  |  | #include "ui/events/cocoa/cocoa_event_utils.h" | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  | #include "ui/gfx/image/image.h" | 
					
						
							| 
									
										
										
										
											2019-02-08 13:19:01 -08:00
										 |  |  | #include "ui/strings/grit/ui_strings.h" | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 14:19:40 -08:00
										 |  |  | using content::BrowserThread; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-01 19:48:11 +08:00
										 |  |  | namespace { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct Role { | 
					
						
							|  |  |  |   SEL selector; | 
					
						
							|  |  |  |   const char* role; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | Role kRolesMap[] = { | 
					
						
							| 
									
										
										
										
											2018-04-20 14:47:04 -04:00
										 |  |  |     {@selector(orderFrontStandardAboutPanel:), "about"}, | 
					
						
							|  |  |  |     {@selector(hide:), "hide"}, | 
					
						
							|  |  |  |     {@selector(hideOtherApplications:), "hideothers"}, | 
					
						
							|  |  |  |     {@selector(unhideAllApplications:), "unhide"}, | 
					
						
							|  |  |  |     {@selector(arrangeInFront:), "front"}, | 
					
						
							|  |  |  |     {@selector(undo:), "undo"}, | 
					
						
							|  |  |  |     {@selector(redo:), "redo"}, | 
					
						
							|  |  |  |     {@selector(cut:), "cut"}, | 
					
						
							|  |  |  |     {@selector(copy:), "copy"}, | 
					
						
							|  |  |  |     {@selector(paste:), "paste"}, | 
					
						
							|  |  |  |     {@selector(delete:), "delete"}, | 
					
						
							|  |  |  |     {@selector(pasteAndMatchStyle:), "pasteandmatchstyle"}, | 
					
						
							|  |  |  |     {@selector(selectAll:), "selectall"}, | 
					
						
							|  |  |  |     {@selector(startSpeaking:), "startspeaking"}, | 
					
						
							|  |  |  |     {@selector(stopSpeaking:), "stopspeaking"}, | 
					
						
							|  |  |  |     {@selector(performMiniaturize:), "minimize"}, | 
					
						
							|  |  |  |     {@selector(performClose:), "close"}, | 
					
						
							|  |  |  |     {@selector(performZoom:), "zoom"}, | 
					
						
							|  |  |  |     {@selector(terminate:), "quit"}, | 
					
						
							|  |  |  |     // ↓ is intentionally not `toggleFullScreen`. The macOS full screen menu | 
					
						
							|  |  |  |     // item behaves weird. If we use `toggleFullScreen`, then the menu item will | 
					
						
							|  |  |  |     // use the default label, and not take the one provided. | 
					
						
							|  |  |  |     {@selector(toggleFullScreenMode:), "togglefullscreen"}, | 
					
						
							|  |  |  |     {@selector(toggleTabBar:), "toggletabbar"}, | 
					
						
							|  |  |  |     {@selector(selectNextTab:), "selectnexttab"}, | 
					
						
							|  |  |  |     {@selector(selectPreviousTab:), "selectprevioustab"}, | 
					
						
							|  |  |  |     {@selector(mergeAllWindows:), "mergeallwindows"}, | 
					
						
							|  |  |  |     {@selector(moveTabToNewWindow:), "movetabtonewwindow"}, | 
					
						
							|  |  |  |     {@selector(clearRecentDocuments:), "clearrecentdocuments"}, | 
					
						
							| 
									
										
										
										
											2015-09-01 19:48:11 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-08 13:19:01 -08:00
										 |  |  | // Called when adding a submenu to the menu and checks if the submenu, via its | 
					
						
							|  |  |  | // |model|, has visible child items. | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  | bool MenuHasVisibleItems(const electron::AtomMenuModel* model) { | 
					
						
							| 
									
										
										
										
											2019-02-08 13:19:01 -08:00
										 |  |  |   int count = model->GetItemCount(); | 
					
						
							|  |  |  |   for (int index = 0; index < count; index++) { | 
					
						
							|  |  |  |     if (model->IsVisibleAt(index)) | 
					
						
							|  |  |  |       return true; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Called when an empty submenu is created. This inserts a menu item labeled | 
					
						
							|  |  |  | // "(empty)" into the submenu. Matches Windows behavior. | 
					
						
							|  |  |  | NSMenu* MakeEmptySubmenu() { | 
					
						
							|  |  |  |   base::scoped_nsobject<NSMenu> submenu([[NSMenu alloc] initWithTitle:@""]); | 
					
						
							|  |  |  |   NSString* empty_menu_title = | 
					
						
							|  |  |  |       l10n_util::GetNSString(IDS_APP_MENU_EMPTY_SUBMENU); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   [submenu addItemWithTitle:empty_menu_title action:NULL keyEquivalent:@""]; | 
					
						
							|  |  |  |   [[submenu itemAtIndex:0] setEnabled:NO]; | 
					
						
							|  |  |  |   return submenu.autorelease(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-01 19:48:11 +08:00
										 |  |  | }  // namespace | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-20 16:15:43 -08:00
										 |  |  | // Menu item is located for ease of removing it from the parent owner | 
					
						
							| 
									
										
										
										
											2017-11-21 10:52:24 -08:00
										 |  |  | static base::scoped_nsobject<NSMenuItem> recentDocumentsMenuItem_; | 
					
						
							| 
									
										
										
										
											2017-11-20 16:15:43 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-20 18:07:11 -08:00
										 |  |  | // Submenu retained to be swapped back to |recentDocumentsMenuItem_| | 
					
						
							| 
									
										
										
										
											2017-11-21 10:52:24 -08:00
										 |  |  | static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_; | 
					
						
							| 
									
										
										
										
											2017-11-20 16:15:43 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  | @implementation AtomMenuController | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @synthesize model = model_; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  | - (id)initWithModel:(electron::AtomMenuModel*)model | 
					
						
							| 
									
										
										
										
											2018-04-20 14:47:04 -04:00
										 |  |  |     useDefaultAccelerator:(BOOL)use { | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |   if ((self = [super init])) { | 
					
						
							|  |  |  |     model_ = model; | 
					
						
							| 
									
										
										
										
											2016-07-02 11:47:40 +09:00
										 |  |  |     isMenuOpen_ = NO; | 
					
						
							|  |  |  |     useDefaultAccelerator_ = use; | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |     [self menu]; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return self; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)dealloc { | 
					
						
							|  |  |  |   [menu_ setDelegate:nil]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Close the menu if it is still open. This could happen if a tab gets closed | 
					
						
							|  |  |  |   // while its context menu is still open. | 
					
						
							|  |  |  |   [self cancel]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-18 21:52:52 -08:00
										 |  |  |   model_ = nil; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |   [super dealloc]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-16 10:58:02 -08:00
										 |  |  | - (void)setCloseCallback:(const base::Callback<void()>&)callback { | 
					
						
							|  |  |  |   closeCallback = callback; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  | - (void)populateWithModel:(electron::AtomMenuModel*)model { | 
					
						
							| 
									
										
										
										
											2014-11-16 20:24:29 +08:00
										 |  |  |   if (!menu_) | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-20 16:15:43 -08:00
										 |  |  |   if (!recentDocumentsMenuItem_) { | 
					
						
							|  |  |  |     // Locate & retain the recent documents menu item | 
					
						
							| 
									
										
										
										
											2018-04-20 14:47:04 -04:00
										 |  |  |     recentDocumentsMenuItem_.reset( | 
					
						
							|  |  |  |         [[[[[NSApp mainMenu] itemWithTitle:@"Electron"] submenu] | 
					
						
							|  |  |  |             itemWithTitle:@"Open Recent"] retain]); | 
					
						
							| 
									
										
										
										
											2017-11-18 21:52:52 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-16 20:24:29 +08:00
										 |  |  |   model_ = model; | 
					
						
							|  |  |  |   [menu_ removeAllItems]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const int count = model->GetItemCount(); | 
					
						
							|  |  |  |   for (int index = 0; index < count; index++) { | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  |     if (model->GetTypeAt(index) == electron::AtomMenuModel::TYPE_SEPARATOR) | 
					
						
							| 
									
										
										
										
											2014-11-16 20:24:29 +08:00
										 |  |  |       [self addSeparatorToMenu:menu_ atIndex:index]; | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       [self addItemToMenu:menu_ atIndex:index fromModel:model]; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  | - (void)cancel { | 
					
						
							|  |  |  |   if (isMenuOpen_) { | 
					
						
							|  |  |  |     [menu_ cancelTracking]; | 
					
						
							|  |  |  |     isMenuOpen_ = NO; | 
					
						
							| 
									
										
										
										
											2018-01-01 16:49:25 +09:00
										 |  |  |     model_->MenuWillClose(); | 
					
						
							| 
									
										
										
										
											2018-06-29 14:48:26 -07:00
										 |  |  |     if (!closeCallback.is_null()) { | 
					
						
							| 
									
										
										
										
											2019-09-18 15:58:00 -04:00
										 |  |  |       base::PostTask(FROM_HERE, {BrowserThread::UI}, closeCallback); | 
					
						
							| 
									
										
										
										
											2018-06-29 14:48:26 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Creates a NSMenu from the given model. If the model has submenus, this can | 
					
						
							|  |  |  | // be invoked recursively. | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  | - (NSMenu*)menuFromModel:(electron::AtomMenuModel*)model { | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |   NSMenu* menu = [[[NSMenu alloc] initWithTitle:@""] autorelease]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const int count = model->GetItemCount(); | 
					
						
							|  |  |  |   for (int index = 0; index < count; index++) { | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  |     if (model->GetTypeAt(index) == electron::AtomMenuModel::TYPE_SEPARATOR) | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |       [self addSeparatorToMenu:menu atIndex:index]; | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       [self addItemToMenu:menu atIndex:index fromModel:model]; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return menu; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Adds a separator item at the given index. As the separator doesn't need | 
					
						
							|  |  |  | // anything from the model, this method doesn't need the model index as the | 
					
						
							|  |  |  | // other method below does. | 
					
						
							| 
									
										
										
										
											2018-04-20 14:47:04 -04:00
										 |  |  | - (void)addSeparatorToMenu:(NSMenu*)menu atIndex:(int)index { | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |   NSMenuItem* separator = [NSMenuItem separatorItem]; | 
					
						
							|  |  |  |   [menu insertItem:separator atIndex:index]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-20 18:07:11 -08:00
										 |  |  | // Empties the source menu items to the destination. | 
					
						
							| 
									
										
										
										
											2018-04-20 14:47:04 -04:00
										 |  |  | - (void)moveMenuItems:(NSMenu*)source to:(NSMenu*)destination { | 
					
						
							| 
									
										
										
										
											2019-05-02 14:05:37 +02:00
										 |  |  |   const NSInteger count = [source numberOfItems]; | 
					
						
							|  |  |  |   for (NSInteger index = 0; index < count; index++) { | 
					
						
							| 
									
										
										
										
											2017-11-20 16:15:43 -08:00
										 |  |  |     NSMenuItem* removedItem = [[[source itemAtIndex:0] retain] autorelease]; | 
					
						
							|  |  |  |     [source removeItemAtIndex:0]; | 
					
						
							|  |  |  |     [destination addItem:removedItem]; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-20 18:07:11 -08:00
										 |  |  | // Replaces the item's submenu instance with the singleton recent documents | 
					
						
							|  |  |  | // menu. Previously replaced menu items will be recovered. | 
					
						
							| 
									
										
										
										
											2017-11-20 16:15:43 -08:00
										 |  |  | - (void)replaceSubmenuShowingRecentDocuments:(NSMenuItem*)item { | 
					
						
							| 
									
										
										
										
											2018-04-20 14:47:04 -04:00
										 |  |  |   NSMenu* recentDocumentsMenu = | 
					
						
							|  |  |  |       [[[recentDocumentsMenuItem_ submenu] retain] autorelease]; | 
					
						
							| 
									
										
										
										
											2017-11-20 16:15:43 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Remove menu items in recent documents back to swap menu | 
					
						
							| 
									
										
										
										
											2018-04-20 14:47:04 -04:00
										 |  |  |   [self moveMenuItems:recentDocumentsMenu to:recentDocumentsMenuSwap_]; | 
					
						
							| 
									
										
										
										
											2017-11-20 16:15:43 -08:00
										 |  |  |   // Swap back the submenu | 
					
						
							| 
									
										
										
										
											2017-11-20 18:07:11 -08:00
										 |  |  |   [recentDocumentsMenuItem_ setSubmenu:recentDocumentsMenuSwap_]; | 
					
						
							| 
									
										
										
										
											2017-11-20 16:15:43 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Retain the item's submenu for a future recovery | 
					
						
							| 
									
										
										
										
											2017-11-21 10:52:24 -08:00
										 |  |  |   recentDocumentsMenuSwap_.reset([[item submenu] retain]); | 
					
						
							| 
									
										
										
										
											2017-11-20 16:15:43 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Repopulate with items from the submenu to be replaced | 
					
						
							| 
									
										
										
										
											2018-04-20 14:47:04 -04:00
										 |  |  |   [self moveMenuItems:recentDocumentsMenuSwap_ to:recentDocumentsMenu]; | 
					
						
							| 
									
										
										
										
											2017-11-28 19:38:07 -08:00
										 |  |  |   // Update the submenu's title | 
					
						
							|  |  |  |   [recentDocumentsMenu setTitle:[recentDocumentsMenuSwap_ title]]; | 
					
						
							| 
									
										
										
										
											2017-11-20 16:15:43 -08:00
										 |  |  |   // Replace submenu | 
					
						
							|  |  |  |   [item setSubmenu:recentDocumentsMenu]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Remember the new menu item that carries the recent documents menu | 
					
						
							| 
									
										
										
										
											2017-11-21 10:52:24 -08:00
										 |  |  |   recentDocumentsMenuItem_.reset([item retain]); | 
					
						
							| 
									
										
										
										
											2017-11-20 16:15:43 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  | // Adds an item or a hierarchical menu to the item at the |index|, | 
					
						
							|  |  |  | // associated with the entry in the model identified by |modelIndex|. | 
					
						
							|  |  |  | - (void)addItemToMenu:(NSMenu*)menu | 
					
						
							|  |  |  |               atIndex:(NSInteger)index | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  |             fromModel:(electron::AtomMenuModel*)model { | 
					
						
							| 
									
										
										
										
											2014-06-28 22:33:00 +08:00
										 |  |  |   base::string16 label16 = model->GetLabelAt(index); | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |   NSString* label = l10n_util::FixUpWindowsStyleLabel(label16); | 
					
						
							| 
									
										
										
										
											2017-11-18 21:52:52 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-20 14:47:04 -04:00
										 |  |  |   base::scoped_nsobject<NSMenuItem> item([[NSMenuItem alloc] | 
					
						
							|  |  |  |       initWithTitle:label | 
					
						
							|  |  |  |              action:@selector(itemSelected:) | 
					
						
							|  |  |  |       keyEquivalent:@""]); | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // If the menu item has an icon, set it. | 
					
						
							|  |  |  |   gfx::Image icon; | 
					
						
							|  |  |  |   if (model->GetIconAt(index, &icon) && !icon.IsEmpty()) | 
					
						
							|  |  |  |     [item setImage:icon.ToNSImage()]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-11 01:56:22 -07:00
										 |  |  |   base::string16 toolTip = model->GetToolTipAt(index); | 
					
						
							|  |  |  |   [item setToolTip:base::SysUTF16ToNSString(toolTip)]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-20 16:29:16 -08:00
										 |  |  |   base::string16 role = model->GetRoleAt(index); | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  |   electron::AtomMenuModel::ItemType type = model->GetTypeAt(index); | 
					
						
							| 
									
										
										
										
											2018-11-10 12:47:55 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (role == base::ASCIIToUTF16("services")) { | 
					
						
							|  |  |  |     base::string16 title = base::ASCIIToUTF16("Services"); | 
					
						
							|  |  |  |     NSString* label = l10n_util::FixUpWindowsStyleLabel(title); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     [item setTarget:nil]; | 
					
						
							|  |  |  |     [item setAction:nil]; | 
					
						
							|  |  |  |     NSMenu* submenu = [[NSMenu alloc] initWithTitle:label]; | 
					
						
							|  |  |  |     [item setSubmenu:submenu]; | 
					
						
							|  |  |  |     [NSApp setServicesMenu:submenu]; | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  |   } else if (type == electron::AtomMenuModel::TYPE_SUBMENU && | 
					
						
							| 
									
										
										
										
											2019-02-08 12:54:39 -08:00
										 |  |  |              model->IsVisibleAt(index)) { | 
					
						
							| 
									
										
										
										
											2019-02-08 18:07:08 -08:00
										 |  |  |     // We need to specifically check that the submenu top-level item has been | 
					
						
							|  |  |  |     // enabled as it's not validated by validateUserInterfaceItem | 
					
						
							|  |  |  |     if (!model->IsEnabledAt(index)) | 
					
						
							|  |  |  |       [item setEnabled:NO]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |     // Recursively build a submenu from the sub-model at this index. | 
					
						
							|  |  |  |     [item setTarget:nil]; | 
					
						
							|  |  |  |     [item setAction:nil]; | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  |     electron::AtomMenuModel* submenuModel = | 
					
						
							|  |  |  |         static_cast<electron::AtomMenuModel*>(model->GetSubmenuModelAt(index)); | 
					
						
							| 
									
										
										
										
											2019-02-08 13:19:01 -08:00
										 |  |  |     NSMenu* submenu = MenuHasVisibleItems(submenuModel) | 
					
						
							|  |  |  |                           ? [self menuFromModel:submenuModel] | 
					
						
							|  |  |  |                           : MakeEmptySubmenu(); | 
					
						
							| 
									
										
										
										
											2013-08-14 22:33:18 +08:00
										 |  |  |     [submenu setTitle:[item title]]; | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |     [item setSubmenu:submenu]; | 
					
						
							| 
									
										
										
										
											2013-08-14 22:33:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-01 19:48:11 +08:00
										 |  |  |     // Set submenu's role. | 
					
						
							| 
									
										
										
										
											2019-01-14 17:27:52 +01:00
										 |  |  |     if ((role == base::ASCIIToUTF16("window") || | 
					
						
							|  |  |  |          role == base::ASCIIToUTF16("windowmenu")) && | 
					
						
							|  |  |  |         [submenu numberOfItems]) | 
					
						
							| 
									
										
										
										
											2013-08-14 22:33:18 +08:00
										 |  |  |       [NSApp setWindowsMenu:submenu]; | 
					
						
							| 
									
										
										
										
											2015-09-01 19:48:11 +08:00
										 |  |  |     else if (role == base::ASCIIToUTF16("help")) | 
					
						
							| 
									
										
										
										
											2013-10-17 10:15:57 +08:00
										 |  |  |       [NSApp setHelpMenu:submenu]; | 
					
						
							| 
									
										
										
										
											2017-11-20 16:31:21 -08:00
										 |  |  |     else if (role == base::ASCIIToUTF16("recentdocuments")) | 
					
						
							| 
									
										
										
										
											2017-11-20 16:15:43 -08:00
										 |  |  |       [self replaceSubmenuShowingRecentDocuments:item]; | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |   } else { | 
					
						
							|  |  |  |     // The MenuModel works on indexes so we can't just set the command id as the | 
					
						
							|  |  |  |     // tag like we do in other menus. Also set the represented object to be | 
					
						
							|  |  |  |     // the model so hierarchical menus check the correct index in the correct | 
					
						
							|  |  |  |     // model. Setting the target to |self| allows this class to participate | 
					
						
							|  |  |  |     // in validation of the menu items. | 
					
						
							|  |  |  |     [item setTag:index]; | 
					
						
							|  |  |  |     NSValue* modelObject = [NSValue valueWithPointer:model]; | 
					
						
							|  |  |  |     [item setRepresentedObject:modelObject];  // Retains |modelObject|. | 
					
						
							|  |  |  |     ui::Accelerator accelerator; | 
					
						
							| 
									
										
										
										
											2018-04-20 14:47:04 -04:00
										 |  |  |     if (model->GetAcceleratorAtWithParams(index, useDefaultAccelerator_, | 
					
						
							|  |  |  |                                           &accelerator)) { | 
					
						
							| 
									
										
										
										
											2018-10-02 15:22:48 -07:00
										 |  |  |       NSString* key_equivalent; | 
					
						
							|  |  |  |       NSUInteger modifier_mask; | 
					
						
							|  |  |  |       GetKeyEquivalentAndModifierMaskFromAccelerator( | 
					
						
							|  |  |  |           accelerator, &key_equivalent, &modifier_mask); | 
					
						
							|  |  |  |       [item setKeyEquivalent:key_equivalent]; | 
					
						
							|  |  |  |       [item setKeyEquivalentModifierMask:modifier_mask]; | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-09-01 19:48:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-28 17:00:54 +00:00
										 |  |  |     if (@available(macOS 10.13, *)) { | 
					
						
							|  |  |  |       [(id)item | 
					
						
							|  |  |  |           setAllowsKeyEquivalentWhenHidden:(model->WorksWhenHiddenAt(index))]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-01 19:48:11 +08:00
										 |  |  |     // Set menu item's role. | 
					
						
							| 
									
										
										
										
											2016-08-08 10:09:45 -07:00
										 |  |  |     [item setTarget:self]; | 
					
						
							|  |  |  |     if (!role.empty()) { | 
					
						
							| 
									
										
										
										
											2015-09-01 19:48:11 +08:00
										 |  |  |       for (const Role& pair : kRolesMap) { | 
					
						
							|  |  |  |         if (role == base::ASCIIToUTF16(pair.role)) { | 
					
						
							| 
									
										
										
										
											2016-08-08 10:09:45 -07:00
										 |  |  |           [item setTarget:nil]; | 
					
						
							| 
									
										
										
										
											2015-09-01 19:48:11 +08:00
										 |  |  |           [item setAction:pair.selector]; | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   [menu insertItem:item atIndex:index]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Called before the menu is to be displayed to update the state (enabled, | 
					
						
							| 
									
										
										
										
											2019-02-28 17:00:54 +00:00
										 |  |  | // radio, etc) of each item in the menu. | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  | - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item { | 
					
						
							|  |  |  |   SEL action = [item action]; | 
					
						
							|  |  |  |   if (action != @selector(itemSelected:)) | 
					
						
							|  |  |  |     return NO; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   NSInteger modelIndex = [item tag]; | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  |   electron::AtomMenuModel* model = static_cast<electron::AtomMenuModel*>( | 
					
						
							| 
									
										
										
										
											2018-04-20 14:47:04 -04:00
										 |  |  |       [[(id)item representedObject] pointerValue]); | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |   DCHECK(model); | 
					
						
							|  |  |  |   if (model) { | 
					
						
							|  |  |  |     BOOL checked = model->IsItemCheckedAt(modelIndex); | 
					
						
							|  |  |  |     DCHECK([(id)item isKindOfClass:[NSMenuItem class]]); | 
					
						
							| 
									
										
										
										
											2018-10-03 00:24:28 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |     [(id)item setState:(checked ? NSOnState : NSOffState)]; | 
					
						
							|  |  |  |     [(id)item setHidden:(!model->IsVisibleAt(modelIndex))]; | 
					
						
							| 
									
										
										
										
											2018-10-03 00:24:28 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |     return model->IsEnabledAt(modelIndex); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return NO; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Called when the user chooses a particular menu item. |sender| is the menu | 
					
						
							|  |  |  | // item chosen. | 
					
						
							|  |  |  | - (void)itemSelected:(id)sender { | 
					
						
							|  |  |  |   NSInteger modelIndex = [sender tag]; | 
					
						
							| 
									
										
										
										
											2019-06-19 14:23:04 -07:00
										 |  |  |   electron::AtomMenuModel* model = static_cast<electron::AtomMenuModel*>( | 
					
						
							| 
									
										
										
										
											2018-04-20 14:47:04 -04:00
										 |  |  |       [[sender representedObject] pointerValue]); | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |   DCHECK(model); | 
					
						
							|  |  |  |   if (model) { | 
					
						
							| 
									
										
										
										
											2015-07-29 12:01:27 +08:00
										 |  |  |     NSEvent* event = [NSApp currentEvent]; | 
					
						
							| 
									
										
										
										
											2019-07-11 01:56:22 -07:00
										 |  |  |     model->ActivatedAt(modelIndex, ui::EventFlagsFromNSEventWithModifiers( | 
					
						
							|  |  |  |                                        event, [event modifierFlags])); | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (NSMenu*)menu { | 
					
						
							| 
									
										
										
										
											2014-11-16 21:06:16 +08:00
										 |  |  |   if (menu_) | 
					
						
							|  |  |  |     return menu_.get(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   menu_.reset([[NSMenu alloc] initWithTitle:@""]); | 
					
						
							|  |  |  |   [menu_ setDelegate:self]; | 
					
						
							|  |  |  |   if (model_) | 
					
						
							|  |  |  |     [self populateWithModel:model_]; | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |   return menu_.get(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (BOOL)isMenuOpen { | 
					
						
							|  |  |  |   return isMenuOpen_; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)menuWillOpen:(NSMenu*)menu { | 
					
						
							|  |  |  |   isMenuOpen_ = YES; | 
					
						
							|  |  |  |   model_->MenuWillShow(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)menuDidClose:(NSMenu*)menu { | 
					
						
							|  |  |  |   if (isMenuOpen_) { | 
					
						
							|  |  |  |     isMenuOpen_ = NO; | 
					
						
							| 
									
										
										
										
											2019-09-04 13:42:23 -07:00
										 |  |  |     if (model_) | 
					
						
							|  |  |  |       model_->MenuWillClose(); | 
					
						
							| 
									
										
										
										
											2018-04-20 14:47:04 -04:00
										 |  |  |     // Post async task so that itemSelected runs before the close callback | 
					
						
							|  |  |  |     // deletes the controller from the map which deallocates it | 
					
						
							| 
									
										
										
										
											2018-02-25 14:19:40 -08:00
										 |  |  |     if (!closeCallback.is_null()) { | 
					
						
							| 
									
										
										
										
											2019-09-18 15:58:00 -04:00
										 |  |  |       base::PostTask(FROM_HERE, {BrowserThread::UI}, closeCallback); | 
					
						
							| 
									
										
										
										
											2018-02-25 14:19:40 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-08-14 22:24:21 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end |