summaryrefslogtreecommitdiff
path: root/widget/cocoa/nsMenuBarX.mm
diff options
context:
space:
mode:
Diffstat (limited to 'widget/cocoa/nsMenuBarX.mm')
-rw-r--r--widget/cocoa/nsMenuBarX.mm979
1 files changed, 0 insertions, 979 deletions
diff --git a/widget/cocoa/nsMenuBarX.mm b/widget/cocoa/nsMenuBarX.mm
deleted file mode 100644
index ff25eb81fc..0000000000
--- a/widget/cocoa/nsMenuBarX.mm
+++ /dev/null
@@ -1,979 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include <objc/objc-runtime.h>
-
-#include "nsMenuBarX.h"
-#include "nsMenuX.h"
-#include "nsMenuItemX.h"
-#include "nsMenuUtilsX.h"
-#include "nsCocoaUtils.h"
-#include "nsCocoaWindow.h"
-#include "nsChildView.h"
-
-#include "nsCOMPtr.h"
-#include "nsString.h"
-#include "nsGkAtoms.h"
-#include "nsObjCExceptions.h"
-#include "nsThreadUtils.h"
-
-#include "nsIContent.h"
-#include "nsIWidget.h"
-#include "nsIDocument.h"
-#include "nsIDOMDocument.h"
-#include "nsIDOMElement.h"
-#include "nsIAppStartup.h"
-#include "nsIStringBundle.h"
-#include "nsToolkitCompsCID.h"
-
-NativeMenuItemTarget* nsMenuBarX::sNativeEventTarget = nil;
-nsMenuBarX* nsMenuBarX::sLastGeckoMenuBarPainted = nullptr;
-NSMenu* sApplicationMenu = nil;
-BOOL sApplicationMenuIsFallback = NO;
-BOOL gSomeMenuBarPainted = NO;
-
-// We keep references to the first quit and pref item content nodes we find, which
-// will be from the hidden window. We use these when the document for the current
-// window does not have a quit or pref item. We don't need strong refs here because
-// these items are always strong ref'd by their owning menu bar (instance variable).
-static nsIContent* sAboutItemContent = nullptr;
-static nsIContent* sPrefItemContent = nullptr;
-static nsIContent* sQuitItemContent = nullptr;
-
-NS_IMPL_ISUPPORTS(nsNativeMenuServiceX, nsINativeMenuService)
-
-NS_IMETHODIMP nsNativeMenuServiceX::CreateNativeMenuBar(nsIWidget* aParent, nsIContent* aMenuBarNode)
-{
- NS_ASSERTION(NS_IsMainThread(), "Attempting to create native menu bar on wrong thread!");
-
- RefPtr<nsMenuBarX> mb = new nsMenuBarX();
- if (!mb)
- return NS_ERROR_OUT_OF_MEMORY;
-
- return mb->Create(aParent, aMenuBarNode);
-}
-
-nsMenuBarX::nsMenuBarX()
-: nsMenuGroupOwnerX(), mParentWindow(nullptr)
-{
- NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
-
- mNativeMenu = [[GeckoNSMenu alloc] initWithTitle:@"MainMenuBar"];
-
- NS_OBJC_END_TRY_ABORT_BLOCK;
-}
-
-nsMenuBarX::~nsMenuBarX()
-{
- NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
-
- if (nsMenuBarX::sLastGeckoMenuBarPainted == this)
- nsMenuBarX::sLastGeckoMenuBarPainted = nullptr;
-
- // the quit/pref items of a random window might have been used if there was no
- // hidden window, thus we need to invalidate the weak references.
- if (sAboutItemContent == mAboutItemContent)
- sAboutItemContent = nullptr;
- if (sQuitItemContent == mQuitItemContent)
- sQuitItemContent = nullptr;
- if (sPrefItemContent == mPrefItemContent)
- sPrefItemContent = nullptr;
-
- // make sure we unregister ourselves as a content observer
- if (mContent) {
- UnregisterForContentChanges(mContent);
- }
-
- // We have to manually clear the array here because clearing causes menu items
- // to call back into the menu bar to unregister themselves. We don't want to
- // depend on member variable ordering to ensure that the array gets cleared
- // before the registration hash table is destroyed.
- mMenuArray.Clear();
-
- [mNativeMenu release];
-
- NS_OBJC_END_TRY_ABORT_BLOCK;
-}
-
-nsresult nsMenuBarX::Create(nsIWidget* aParent, nsIContent* aContent)
-{
- if (!aParent)
- return NS_ERROR_INVALID_ARG;
-
- mParentWindow = aParent;
- mContent = aContent;
-
- if (mContent) {
- AquifyMenuBar();
-
- nsresult rv = nsMenuGroupOwnerX::Create(mContent);
- if (NS_FAILED(rv))
- return rv;
-
- RegisterForContentChanges(mContent, this);
- ConstructNativeMenus();
- } else {
- ConstructFallbackNativeMenus();
- }
-
- // Give this to the parent window. The parent takes ownership.
- static_cast<nsCocoaWindow*>(mParentWindow)->SetMenuBar(this);
-
- return NS_OK;
-}
-
-void nsMenuBarX::ConstructNativeMenus()
-{
- uint32_t count = mContent->GetChildCount();
- for (uint32_t i = 0; i < count; i++) {
- nsIContent *menuContent = mContent->GetChildAt(i);
- if (menuContent &&
- menuContent->IsXULElement(nsGkAtoms::menu)) {
- nsMenuX* newMenu = new nsMenuX();
- if (newMenu) {
- nsresult rv = newMenu->Create(this, this, menuContent);
- if (NS_SUCCEEDED(rv))
- InsertMenuAtIndex(newMenu, GetMenuCount());
- else
- delete newMenu;
- }
- }
- }
-}
-
-void nsMenuBarX::ConstructFallbackNativeMenus()
-{
- if (sApplicationMenu) {
- // Menu has already been built.
- return;
- }
-
- nsCOMPtr<nsIStringBundle> stringBundle;
-
- nsCOMPtr<nsIStringBundleService> bundleSvc = do_GetService(NS_STRINGBUNDLE_CONTRACTID);
- bundleSvc->CreateBundle("chrome://global/locale/fallbackMenubar.properties", getter_AddRefs(stringBundle));
-
- if (!stringBundle) {
- return;
- }
-
- nsXPIDLString labelUTF16;
- nsXPIDLString keyUTF16;
-
- const char16_t* labelProp = u"quitMenuitem.label";
- const char16_t* keyProp = u"quitMenuitem.key";
-
- stringBundle->GetStringFromName(labelProp, getter_Copies(labelUTF16));
- stringBundle->GetStringFromName(keyProp, getter_Copies(keyUTF16));
-
- NSString* labelStr = [NSString stringWithUTF8String:
- NS_ConvertUTF16toUTF8(labelUTF16).get()];
- NSString* keyStr= [NSString stringWithUTF8String:
- NS_ConvertUTF16toUTF8(keyUTF16).get()];
-
- if (!nsMenuBarX::sNativeEventTarget) {
- nsMenuBarX::sNativeEventTarget = [[NativeMenuItemTarget alloc] init];
- }
-
- sApplicationMenu = [[[[NSApp mainMenu] itemAtIndex:0] submenu] retain];
- NSMenuItem* quitMenuItem = [[[NSMenuItem alloc] initWithTitle:labelStr
- action:@selector(menuItemHit:)
- keyEquivalent:keyStr] autorelease];
- [quitMenuItem setTarget:nsMenuBarX::sNativeEventTarget];
- [quitMenuItem setTag:eCommand_ID_Quit];
- [sApplicationMenu addItem:quitMenuItem];
- sApplicationMenuIsFallback = YES;
-}
-
-uint32_t nsMenuBarX::GetMenuCount()
-{
- return mMenuArray.Length();
-}
-
-bool nsMenuBarX::MenuContainsAppMenu()
-{
- NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
-
- return ([mNativeMenu numberOfItems] > 0 &&
- [[mNativeMenu itemAtIndex:0] submenu] == sApplicationMenu);
-
- NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
-}
-
-nsresult nsMenuBarX::InsertMenuAtIndex(nsMenuX* aMenu, uint32_t aIndex)
-{
- NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
-
- // If we've only yet created a fallback global Application menu (using
- // ContructFallbackNativeMenus()), destroy it before recreating it properly.
- if (sApplicationMenu && sApplicationMenuIsFallback) {
- ResetNativeApplicationMenu();
- }
- // If we haven't created a global Application menu yet, do it.
- if (!sApplicationMenu) {
- nsresult rv = NS_OK; // avoid warning about rv being unused
- rv = CreateApplicationMenu(aMenu);
- NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create Application menu");
-
- // Hook the new Application menu up to the menu bar.
- NSMenu* mainMenu = [NSApp mainMenu];
- NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
- [[mainMenu itemAtIndex:0] setSubmenu:sApplicationMenu];
- }
-
- // add menu to array that owns our menus
- mMenuArray.InsertElementAt(aIndex, aMenu);
-
- // hook up submenus
- nsIContent* menuContent = aMenu->Content();
- if (menuContent->GetChildCount() > 0 &&
- !nsMenuUtilsX::NodeIsHiddenOrCollapsed(menuContent)) {
- int insertionIndex = nsMenuUtilsX::CalculateNativeInsertionPoint(this, aMenu);
- if (MenuContainsAppMenu())
- insertionIndex++;
- [mNativeMenu insertItem:aMenu->NativeMenuItem() atIndex:insertionIndex];
- }
-
- return NS_OK;
-
- NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
-}
-
-void nsMenuBarX::RemoveMenuAtIndex(uint32_t aIndex)
-{
- NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
-
- if (mMenuArray.Length() <= aIndex) {
- NS_ERROR("Attempting submenu removal with bad index!");
- return;
- }
-
- // Our native menu and our internal menu object array might be out of sync.
- // This happens, for example, when a submenu is hidden. Because of this we
- // should not assume that a native submenu is hooked up.
- NSMenuItem* nativeMenuItem = mMenuArray[aIndex]->NativeMenuItem();
- int nativeMenuItemIndex = [mNativeMenu indexOfItem:nativeMenuItem];
- if (nativeMenuItemIndex != -1)
- [mNativeMenu removeItemAtIndex:nativeMenuItemIndex];
-
- mMenuArray.RemoveElementAt(aIndex);
-
- NS_OBJC_END_TRY_ABORT_BLOCK;
-}
-
-void nsMenuBarX::ObserveAttributeChanged(nsIDocument* aDocument,
- nsIContent* aContent,
- nsIAtom* aAttribute)
-{
-}
-
-void nsMenuBarX::ObserveContentRemoved(nsIDocument* aDocument,
- nsIContent* aChild,
- int32_t aIndexInContainer)
-{
- RemoveMenuAtIndex(aIndexInContainer);
-}
-
-void nsMenuBarX::ObserveContentInserted(nsIDocument* aDocument,
- nsIContent* aContainer,
- nsIContent* aChild)
-{
- nsMenuX* newMenu = new nsMenuX();
- if (newMenu) {
- nsresult rv = newMenu->Create(this, this, aChild);
- if (NS_SUCCEEDED(rv))
- InsertMenuAtIndex(newMenu, aContainer->IndexOf(aChild));
- else
- delete newMenu;
- }
-}
-
-void nsMenuBarX::ForceUpdateNativeMenuAt(const nsAString& indexString)
-{
- NSString* locationString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(indexString.BeginReading())
- length:indexString.Length()];
- NSArray* indexes = [locationString componentsSeparatedByString:@"|"];
- unsigned int indexCount = [indexes count];
- if (indexCount == 0)
- return;
-
- nsMenuX* currentMenu = NULL;
- int targetIndex = [[indexes objectAtIndex:0] intValue];
- int visible = 0;
- uint32_t length = mMenuArray.Length();
- // first find a menu in the menu bar
- for (unsigned int i = 0; i < length; i++) {
- nsMenuX* menu = mMenuArray[i].get();
- if (!nsMenuUtilsX::NodeIsHiddenOrCollapsed(menu->Content())) {
- visible++;
- if (visible == (targetIndex + 1)) {
- currentMenu = menu;
- break;
- }
- }
- }
-
- if (!currentMenu)
- return;
-
- // fake open/close to cause lazy update to happen so submenus populate
- currentMenu->MenuOpened();
- currentMenu->MenuClosed();
-
- // now find the correct submenu
- for (unsigned int i = 1; currentMenu && i < indexCount; i++) {
- targetIndex = [[indexes objectAtIndex:i] intValue];
- visible = 0;
- length = currentMenu->GetItemCount();
- for (unsigned int j = 0; j < length; j++) {
- nsMenuObjectX* targetMenu = currentMenu->GetItemAt(j);
- if (!targetMenu)
- return;
- if (!nsMenuUtilsX::NodeIsHiddenOrCollapsed(targetMenu->Content())) {
- visible++;
- if (targetMenu->MenuObjectType() == eSubmenuObjectType && visible == (targetIndex + 1)) {
- currentMenu = static_cast<nsMenuX*>(targetMenu);
- // fake open/close to cause lazy update to happen
- currentMenu->MenuOpened();
- currentMenu->MenuClosed();
- break;
- }
- }
- }
- }
-}
-
-// Calling this forces a full reload of the menu system, reloading all native
-// menus and their items.
-// Without this testing is hard because changes to the DOM affect the native
-// menu system lazily.
-void nsMenuBarX::ForceNativeMenuReload()
-{
- // tear down everything
- while (GetMenuCount() > 0)
- RemoveMenuAtIndex(0);
-
- // construct everything
- ConstructNativeMenus();
-}
-
-nsMenuX* nsMenuBarX::GetMenuAt(uint32_t aIndex)
-{
- if (mMenuArray.Length() <= aIndex) {
- NS_ERROR("Requesting menu at invalid index!");
- return NULL;
- }
- return mMenuArray[aIndex].get();
-}
-
-nsMenuX* nsMenuBarX::GetXULHelpMenu()
-{
- // The Help menu is usually (always?) the last one, so we start there and
- // count back.
- for (int32_t i = GetMenuCount() - 1; i >= 0; --i) {
- nsMenuX* aMenu = GetMenuAt(i);
- if (aMenu && nsMenuX::IsXULHelpMenu(aMenu->Content()))
- return aMenu;
- }
- return nil;
-}
-
-// On SnowLeopard and later we must tell the OS which is our Help menu.
-// Otherwise it will only add Spotlight for Help (the Search item) to our
-// Help menu if its label/title is "Help" -- i.e. if the menu is in English.
-// This resolves bugs 489196 and 539317.
-void nsMenuBarX::SetSystemHelpMenu()
-{
- nsMenuX* xulHelpMenu = GetXULHelpMenu();
- if (xulHelpMenu) {
- NSMenu* helpMenu = (NSMenu*)xulHelpMenu->NativeData();
- if (helpMenu)
- [NSApp setHelpMenu:helpMenu];
- }
-}
-
-nsresult nsMenuBarX::Paint()
-{
- NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
-
- // Don't try to optimize anything in this painting by checking
- // sLastGeckoMenuBarPainted because the menubar can be manipulated by
- // native dialogs and sheet code and other things besides this paint method.
-
- // We have to keep the same menu item for the Application menu so we keep
- // passing it along.
- NSMenu* outgoingMenu = [NSApp mainMenu];
- NS_ASSERTION([outgoingMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
-
- NSMenuItem* appMenuItem = [[outgoingMenu itemAtIndex:0] retain];
- [outgoingMenu removeItemAtIndex:0];
- [mNativeMenu insertItem:appMenuItem atIndex:0];
- [appMenuItem release];
-
- // Set menu bar and event target.
- [NSApp setMainMenu:mNativeMenu];
- SetSystemHelpMenu();
- nsMenuBarX::sLastGeckoMenuBarPainted = this;
-
- gSomeMenuBarPainted = YES;
-
- return NS_OK;
-
- NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
-}
-
-// Returns the 'key' attribute of the 'shortcutID' object (if any) in the
-// currently active menubar's DOM document. 'shortcutID' should be the id
-// (i.e. the name) of a component that defines a commonly used (and
-// localized) cmd+key shortcut, and belongs to a keyset containing similar
-// objects. For example "key_selectAll". Returns a value that can be
-// compared to the first character of [NSEvent charactersIgnoringModifiers]
-// when [NSEvent modifierFlags] == NSCommandKeyMask.
-char nsMenuBarX::GetLocalizedAccelKey(const char *shortcutID)
-{
- if (!sLastGeckoMenuBarPainted)
- return 0;
-
- nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(sLastGeckoMenuBarPainted->mContent->OwnerDoc()));
- if (!domDoc)
- return 0;
-
- NS_ConvertASCIItoUTF16 shortcutIDStr((const char *)shortcutID);
- nsCOMPtr<nsIDOMElement> shortcutElement;
- domDoc->GetElementById(shortcutIDStr, getter_AddRefs(shortcutElement));
- nsCOMPtr<nsIContent> shortcutContent = do_QueryInterface(shortcutElement);
- if (!shortcutContent)
- return 0;
-
- nsAutoString key;
- shortcutContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, key);
- NS_LossyConvertUTF16toASCII keyASC(key.get());
- const char *keyASCPtr = keyASC.get();
- if (!keyASCPtr)
- return 0;
- // If keyID's 'key' attribute isn't exactly one character long, it's not
- // what we're looking for.
- if (strlen(keyASCPtr) != sizeof(char))
- return 0;
- // Make sure retval is lower case.
- char retval = tolower(keyASCPtr[0]);
-
- return retval;
-}
-
-/* static */
-void nsMenuBarX::ResetNativeApplicationMenu()
-{
- [sApplicationMenu removeAllItems];
- [sApplicationMenu release];
- sApplicationMenu = nil;
- sApplicationMenuIsFallback = NO;
-}
-
-// Hide the item in the menu by setting the 'hidden' attribute. Returns it in |outHiddenNode| so
-// the caller can hang onto it if they so choose. It is acceptable to pass nsull
-// for |outHiddenNode| if the caller doesn't care about the hidden node.
-void nsMenuBarX::HideItem(nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode)
-{
- nsCOMPtr<nsIDOMElement> menuItem;
- inDoc->GetElementById(inID, getter_AddRefs(menuItem));
- nsCOMPtr<nsIContent> menuContent(do_QueryInterface(menuItem));
- if (menuContent) {
- menuContent->SetAttr(kNameSpaceID_None, nsGkAtoms::hidden, NS_LITERAL_STRING("true"), false);
- if (outHiddenNode) {
- *outHiddenNode = menuContent.get();
- NS_IF_ADDREF(*outHiddenNode);
- }
- }
-}
-
-// Do what is necessary to conform to the Aqua guidelines for menus.
-void nsMenuBarX::AquifyMenuBar()
-{
- nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mContent->GetComposedDoc()));
- if (domDoc) {
- // remove the "About..." item and its separator
- HideItem(domDoc, NS_LITERAL_STRING("aboutSeparator"), nullptr);
- HideItem(domDoc, NS_LITERAL_STRING("aboutName"), getter_AddRefs(mAboutItemContent));
- if (!sAboutItemContent)
- sAboutItemContent = mAboutItemContent;
-
- // remove quit item and its separator
- HideItem(domDoc, NS_LITERAL_STRING("menu_FileQuitSeparator"), nullptr);
- HideItem(domDoc, NS_LITERAL_STRING("menu_FileQuitItem"), getter_AddRefs(mQuitItemContent));
- if (!sQuitItemContent)
- sQuitItemContent = mQuitItemContent;
-
- // remove prefs item and its separator, but save off the pref content node
- // so we can invoke its command later.
- HideItem(domDoc, NS_LITERAL_STRING("menu_PrefsSeparator"), nullptr);
- HideItem(domDoc, NS_LITERAL_STRING("menu_preferences"), getter_AddRefs(mPrefItemContent));
- if (!sPrefItemContent)
- sPrefItemContent = mPrefItemContent;
-
- // hide items that we use for the Application menu
- HideItem(domDoc, NS_LITERAL_STRING("menu_mac_services"), nullptr);
- HideItem(domDoc, NS_LITERAL_STRING("menu_mac_hide_app"), nullptr);
- HideItem(domDoc, NS_LITERAL_STRING("menu_mac_hide_others"), nullptr);
- HideItem(domDoc, NS_LITERAL_STRING("menu_mac_show_all"), nullptr);
- }
-}
-
-// for creating menu items destined for the Application menu
-NSMenuItem* nsMenuBarX::CreateNativeAppMenuItem(nsMenuX* inMenu, const nsAString& nodeID, SEL action,
- int tag, NativeMenuItemTarget* target)
-{
- NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
-
- nsCOMPtr<nsIDocument> doc = inMenu->Content()->GetUncomposedDoc();
- if (!doc) {
- return nil;
- }
-
- nsCOMPtr<nsIDOMDocument> domdoc(do_QueryInterface(doc));
- if (!domdoc) {
- return nil;
- }
-
- // Get information from the gecko menu item
- nsAutoString label;
- nsAutoString modifiers;
- nsAutoString key;
- nsCOMPtr<nsIDOMElement> menuItem;
- domdoc->GetElementById(nodeID, getter_AddRefs(menuItem));
- if (menuItem) {
- menuItem->GetAttribute(NS_LITERAL_STRING("label"), label);
- menuItem->GetAttribute(NS_LITERAL_STRING("modifiers"), modifiers);
- menuItem->GetAttribute(NS_LITERAL_STRING("key"), key);
- }
- else {
- return nil;
- }
-
- // Get more information about the key equivalent. Start by
- // finding the key node we need.
- NSString* keyEquiv = nil;
- unsigned int macKeyModifiers = 0;
- if (!key.IsEmpty()) {
- nsCOMPtr<nsIDOMElement> keyElement;
- domdoc->GetElementById(key, getter_AddRefs(keyElement));
- if (keyElement) {
- nsCOMPtr<nsIContent> keyContent (do_QueryInterface(keyElement));
- // first grab the key equivalent character
- nsAutoString keyChar(NS_LITERAL_STRING(" "));
- keyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyChar);
- if (!keyChar.EqualsLiteral(" ")) {
- keyEquiv = [[NSString stringWithCharacters:reinterpret_cast<const unichar*>(keyChar.get())
- length:keyChar.Length()] lowercaseString];
- }
- // now grab the key equivalent modifiers
- nsAutoString modifiersStr;
- keyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers, modifiersStr);
- uint8_t geckoModifiers = nsMenuUtilsX::GeckoModifiersForNodeAttribute(modifiersStr);
- macKeyModifiers = nsMenuUtilsX::MacModifiersForGeckoModifiers(geckoModifiers);
- }
- }
- // get the label into NSString-form
- NSString* labelString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(label.get())
- length:label.Length()];
-
- if (!labelString)
- labelString = @"";
- if (!keyEquiv)
- keyEquiv = @"";
-
- // put together the actual NSMenuItem
- NSMenuItem* newMenuItem = [[NSMenuItem alloc] initWithTitle:labelString action:action keyEquivalent:keyEquiv];
-
- [newMenuItem setTag:tag];
- [newMenuItem setTarget:target];
- [newMenuItem setKeyEquivalentModifierMask:macKeyModifiers];
-
- MenuItemInfo * info = [[MenuItemInfo alloc] initWithMenuGroupOwner:this];
- [newMenuItem setRepresentedObject:info];
- [info release];
-
- return newMenuItem;
-
- NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
-}
-
-// build the Application menu shared by all menu bars
-nsresult nsMenuBarX::CreateApplicationMenu(nsMenuX* inMenu)
-{
- NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
-
- // At this point, the application menu is the application menu from
- // the nib in cocoa widgets. We do not have a way to create an application
- // menu manually, so we grab the one from the nib and use that.
- sApplicationMenu = [[[[NSApp mainMenu] itemAtIndex:0] submenu] retain];
-
-/*
- We support the following menu items here:
-
- Menu Item DOM Node ID Notes
-
- ========================
- = About This App = <- aboutName
- ========================
- = Preferences... = <- menu_preferences
- ========================
- = Services > = <- menu_mac_services <- (do not define key equivalent)
- ========================
- = Hide App = <- menu_mac_hide_app
- = Hide Others = <- menu_mac_hide_others
- = Show All = <- menu_mac_show_all
- ========================
- = Quit = <- menu_FileQuitItem
- ========================
-
- If any of them are ommitted from the application's DOM, we just don't add
- them. We always add a "Quit" item, but if an app developer does not provide a
- DOM node with the right ID for the Quit item, we add it in English. App
- developers need only add each node with a label and a key equivalent (if they
- want one). Other attributes are optional. Like so:
-
- <menuitem id="menu_preferences"
- label="&preferencesCmdMac.label;"
- key="open_prefs_key"/>
-
- We need to use this system for localization purposes, until we have a better way
- to define the Application menu to be used on Mac OS X.
-*/
-
- if (sApplicationMenu) {
- // This code reads attributes we are going to care about from the DOM elements
-
- NSMenuItem *itemBeingAdded = nil;
- BOOL addAboutSeparator = FALSE;
-
- // Add the About menu item
- itemBeingAdded = CreateNativeAppMenuItem(inMenu, NS_LITERAL_STRING("aboutName"), @selector(menuItemHit:),
- eCommand_ID_About, nsMenuBarX::sNativeEventTarget);
- if (itemBeingAdded) {
- [sApplicationMenu addItem:itemBeingAdded];
- [itemBeingAdded release];
- itemBeingAdded = nil;
-
- addAboutSeparator = TRUE;
- }
-
- // Add separator if either the About item or software update item exists
- if (addAboutSeparator)
- [sApplicationMenu addItem:[NSMenuItem separatorItem]];
-
- // Add the Preferences menu item
- itemBeingAdded = CreateNativeAppMenuItem(inMenu, NS_LITERAL_STRING("menu_preferences"), @selector(menuItemHit:),
- eCommand_ID_Prefs, nsMenuBarX::sNativeEventTarget);
- if (itemBeingAdded) {
- [sApplicationMenu addItem:itemBeingAdded];
- [itemBeingAdded release];
- itemBeingAdded = nil;
-
- // Add separator after Preferences menu
- [sApplicationMenu addItem:[NSMenuItem separatorItem]];
- }
-
- // Add Services menu item
- itemBeingAdded = CreateNativeAppMenuItem(inMenu, NS_LITERAL_STRING("menu_mac_services"), nil,
- 0, nil);
- if (itemBeingAdded) {
- [sApplicationMenu addItem:itemBeingAdded];
-
- // set this menu item up as the Mac OS X Services menu
- NSMenu* servicesMenu = [[GeckoServicesNSMenu alloc] initWithTitle:@""];
- [itemBeingAdded setSubmenu:servicesMenu];
- [NSApp setServicesMenu:servicesMenu];
-
- [itemBeingAdded release];
- itemBeingAdded = nil;
-
- // Add separator after Services menu
- [sApplicationMenu addItem:[NSMenuItem separatorItem]];
- }
-
- BOOL addHideShowSeparator = FALSE;
-
- // Add menu item to hide this application
- itemBeingAdded = CreateNativeAppMenuItem(inMenu, NS_LITERAL_STRING("menu_mac_hide_app"), @selector(menuItemHit:),
- eCommand_ID_HideApp, nsMenuBarX::sNativeEventTarget);
- if (itemBeingAdded) {
- [sApplicationMenu addItem:itemBeingAdded];
- [itemBeingAdded release];
- itemBeingAdded = nil;
-
- addHideShowSeparator = TRUE;
- }
-
- // Add menu item to hide other applications
- itemBeingAdded = CreateNativeAppMenuItem(inMenu, NS_LITERAL_STRING("menu_mac_hide_others"), @selector(menuItemHit:),
- eCommand_ID_HideOthers, nsMenuBarX::sNativeEventTarget);
- if (itemBeingAdded) {
- [sApplicationMenu addItem:itemBeingAdded];
- [itemBeingAdded release];
- itemBeingAdded = nil;
-
- addHideShowSeparator = TRUE;
- }
-
- // Add menu item to show all applications
- itemBeingAdded = CreateNativeAppMenuItem(inMenu, NS_LITERAL_STRING("menu_mac_show_all"), @selector(menuItemHit:),
- eCommand_ID_ShowAll, nsMenuBarX::sNativeEventTarget);
- if (itemBeingAdded) {
- [sApplicationMenu addItem:itemBeingAdded];
- [itemBeingAdded release];
- itemBeingAdded = nil;
-
- addHideShowSeparator = TRUE;
- }
-
- // Add a separator after the hide/show menus if at least one exists
- if (addHideShowSeparator)
- [sApplicationMenu addItem:[NSMenuItem separatorItem]];
-
- // Add quit menu item
- itemBeingAdded = CreateNativeAppMenuItem(inMenu, NS_LITERAL_STRING("menu_FileQuitItem"), @selector(menuItemHit:),
- eCommand_ID_Quit, nsMenuBarX::sNativeEventTarget);
- if (itemBeingAdded) {
- [sApplicationMenu addItem:itemBeingAdded];
- [itemBeingAdded release];
- itemBeingAdded = nil;
- }
- else {
- // the current application does not have a DOM node for "Quit". Add one
- // anyway, in English.
- NSMenuItem* defaultQuitItem = [[[NSMenuItem alloc] initWithTitle:@"Quit" action:@selector(menuItemHit:)
- keyEquivalent:@"q"] autorelease];
- [defaultQuitItem setTarget:nsMenuBarX::sNativeEventTarget];
- [defaultQuitItem setTag:eCommand_ID_Quit];
- [sApplicationMenu addItem:defaultQuitItem];
- }
- }
-
- return (sApplicationMenu) ? NS_OK : NS_ERROR_FAILURE;
-
- NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
-}
-
-void nsMenuBarX::SetParent(nsIWidget* aParent)
-{
- mParentWindow = aParent;
-}
-
-//
-// Objective-C class used to allow us to have keyboard commands
-// look like they are doing something but actually do nothing.
-// We allow mouse actions to work normally.
-//
-
-// Controls whether or not native menu items should invoke their commands.
-static BOOL gMenuItemsExecuteCommands = YES;
-
-@implementation GeckoNSMenu
-
-// Keyboard commands should not cause menu items to invoke their
-// commands when there is a key window because we'd rather send
-// the keyboard command to the window. We still have the menus
-// go through the mechanics so they'll give the proper visual
-// feedback.
-- (BOOL)performKeyEquivalent:(NSEvent *)theEvent
-{
- // We've noticed that Mac OS X expects this check in subclasses before
- // calling NSMenu's "performKeyEquivalent:".
- //
- // There is no case in which we'd need to do anything or return YES
- // when we have no items so we can just do this check first.
- if ([self numberOfItems] <= 0) {
- return NO;
- }
-
- NSWindow *keyWindow = [NSApp keyWindow];
-
- // If there is no key window then just behave normally. This
- // probably means that this menu is associated with Gecko's
- // hidden window.
- if (!keyWindow) {
- return [super performKeyEquivalent:theEvent];
- }
-
- NSResponder *firstResponder = [keyWindow firstResponder];
-
- gMenuItemsExecuteCommands = NO;
- [super performKeyEquivalent:theEvent];
- gMenuItemsExecuteCommands = YES; // return to default
-
- // Return YES if we invoked a command and there is now no key window or we changed
- // the first responder. In this case we do not want to propagate the event because
- // we don't want it handled again.
- if (![NSApp keyWindow] || [[NSApp keyWindow] firstResponder] != firstResponder) {
- return YES;
- }
-
- // Return NO so that we can handle the event via NSView's "keyDown:".
- return NO;
-}
-
-@end
-
-//
-// Objective-C class used as action target for menu items
-//
-
-@implementation NativeMenuItemTarget
-
-// called when some menu item in this menu gets hit
--(IBAction)menuItemHit:(id)sender
-{
- NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
-
- if (!gMenuItemsExecuteCommands) {
- return;
- }
-
- int tag = [sender tag];
-
- nsMenuGroupOwnerX* menuGroupOwner = nullptr;
- nsMenuBarX* menuBar = nullptr;
- MenuItemInfo* info = [sender representedObject];
-
- if (info) {
- menuGroupOwner = [info menuGroupOwner];
- if (!menuGroupOwner) {
- return;
- }
- if (menuGroupOwner->MenuObjectType() == eMenuBarObjectType) {
- menuBar = static_cast<nsMenuBarX*>(menuGroupOwner);
- }
- }
-
- // Do special processing if this is for an app-global command.
- if (tag == eCommand_ID_About) {
- nsIContent* mostSpecificContent = sAboutItemContent;
- if (menuBar && menuBar->mAboutItemContent)
- mostSpecificContent = menuBar->mAboutItemContent;
- nsMenuUtilsX::DispatchCommandTo(mostSpecificContent);
- return;
- }
- else if (tag == eCommand_ID_Prefs) {
- nsIContent* mostSpecificContent = sPrefItemContent;
- if (menuBar && menuBar->mPrefItemContent)
- mostSpecificContent = menuBar->mPrefItemContent;
- nsMenuUtilsX::DispatchCommandTo(mostSpecificContent);
- return;
- }
- else if (tag == eCommand_ID_HideApp) {
- [NSApp hide:sender];
- return;
- }
- else if (tag == eCommand_ID_HideOthers) {
- [NSApp hideOtherApplications:sender];
- return;
- }
- else if (tag == eCommand_ID_ShowAll) {
- [NSApp unhideAllApplications:sender];
- return;
- }
- else if (tag == eCommand_ID_Quit) {
- nsIContent* mostSpecificContent = sQuitItemContent;
- if (menuBar && menuBar->mQuitItemContent)
- mostSpecificContent = menuBar->mQuitItemContent;
- // If we have some content for quit we execute it. Otherwise we send a native app terminate
- // message. If you want to stop a quit from happening, provide quit content and return
- // the event as unhandled.
- if (mostSpecificContent) {
- nsMenuUtilsX::DispatchCommandTo(mostSpecificContent);
- }
- else {
- nsCOMPtr<nsIAppStartup> appStartup = do_GetService(NS_APPSTARTUP_CONTRACTID);
- if (appStartup) {
- appStartup->Quit(nsIAppStartup::eAttemptQuit);
- }
- }
- return;
- }
-
- // given the commandID, look it up in our hashtable and dispatch to
- // that menu item.
- if (menuGroupOwner) {
- nsMenuItemX* menuItem = menuGroupOwner->GetMenuItemForCommandID(static_cast<uint32_t>(tag));
- if (menuItem)
- menuItem->DoCommand();
- }
-
- NS_OBJC_END_TRY_ABORT_BLOCK;
-}
-
-@end
-
-// Objective-C class used for menu items on the Services menu to allow Gecko
-// to override their standard behavior in order to stop key equivalents from
-// firing in certain instances. When gMenuItemsExecuteCommands is NO, we return
-// a dummy target and action instead of the actual target and action.
-
-@implementation GeckoServicesNSMenuItem
-
-- (id) target
-{
- id realTarget = [super target];
- if (gMenuItemsExecuteCommands)
- return realTarget;
- else
- return realTarget ? self : nil;
-}
-
-- (SEL) action
-{
- SEL realAction = [super action];
- if (gMenuItemsExecuteCommands)
- return realAction;
- else
- return realAction ? @selector(_doNothing:) : NULL;
-}
-
-- (void) _doNothing:(id)sender
-{
-}
-
-@end
-
-// Objective-C class used as the Services menu so that Gecko can override the
-// standard behavior of the Services menu in order to stop key equivalents
-// from firing in certain instances.
-
-@implementation GeckoServicesNSMenu
-
-- (void)addItem:(NSMenuItem *)newItem
-{
- [self _overrideClassOfMenuItem:newItem];
- [super addItem:newItem];
-}
-
-- (NSMenuItem *)addItemWithTitle:(NSString *)aString action:(SEL)aSelector keyEquivalent:(NSString *)keyEquiv
-{
- NSMenuItem * newItem = [super addItemWithTitle:aString action:aSelector keyEquivalent:keyEquiv];
- [self _overrideClassOfMenuItem:newItem];
- return newItem;
-}
-
-- (void)insertItem:(NSMenuItem *)newItem atIndex:(NSInteger)index
-{
- [self _overrideClassOfMenuItem:newItem];
- [super insertItem:newItem atIndex:index];
-}
-
-- (NSMenuItem *)insertItemWithTitle:(NSString *)aString action:(SEL)aSelector keyEquivalent:(NSString *)keyEquiv atIndex:(NSInteger)index
-{
- NSMenuItem * newItem = [super insertItemWithTitle:aString action:aSelector keyEquivalent:keyEquiv atIndex:index];
- [self _overrideClassOfMenuItem:newItem];
- return newItem;
-}
-
-- (void) _overrideClassOfMenuItem:(NSMenuItem *)menuItem
-{
- if ([menuItem class] == [NSMenuItem class])
- object_setClass(menuItem, [GeckoServicesNSMenuItem class]);
-}
-
-@end