summaryrefslogtreecommitdiffstats
path: root/widget/cocoa/nsStandaloneNativeMenu.mm
diff options
context:
space:
mode:
Diffstat (limited to 'widget/cocoa/nsStandaloneNativeMenu.mm')
-rw-r--r--widget/cocoa/nsStandaloneNativeMenu.mm213
1 files changed, 213 insertions, 0 deletions
diff --git a/widget/cocoa/nsStandaloneNativeMenu.mm b/widget/cocoa/nsStandaloneNativeMenu.mm
new file mode 100644
index 000000000..98a5fd8f6
--- /dev/null
+++ b/widget/cocoa/nsStandaloneNativeMenu.mm
@@ -0,0 +1,213 @@
+/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */
+/* 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/. */
+
+#import <Cocoa/Cocoa.h>
+
+#include "nsStandaloneNativeMenu.h"
+#include "nsMenuUtilsX.h"
+#include "nsIDOMElement.h"
+#include "nsIMutationObserver.h"
+#include "nsGkAtoms.h"
+#include "nsObjCExceptions.h"
+
+
+NS_IMPL_ISUPPORTS_INHERITED(nsStandaloneNativeMenu, nsMenuGroupOwnerX,
+ nsIMutationObserver, nsIStandaloneNativeMenu)
+
+nsStandaloneNativeMenu::nsStandaloneNativeMenu()
+: mMenu(nullptr)
+, mContainerStatusBarItem(nil)
+{
+}
+
+nsStandaloneNativeMenu::~nsStandaloneNativeMenu()
+{
+ if (mMenu) delete mMenu;
+}
+
+NS_IMETHODIMP
+nsStandaloneNativeMenu::Init(nsIDOMElement * aDOMElement)
+{
+ NS_ASSERTION(mMenu == nullptr, "nsNativeMenu::Init - mMenu not null!");
+
+ nsresult rv;
+
+ nsCOMPtr<nsIContent> content = do_QueryInterface(aDOMElement, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!content->IsAnyOfXULElements(nsGkAtoms::menu, nsGkAtoms::menupopup))
+ return NS_ERROR_FAILURE;
+
+ rv = nsMenuGroupOwnerX::Create(content);
+ if (NS_FAILED(rv))
+ return rv;
+
+ mMenu = new nsMenuX();
+ rv = mMenu->Create(this, this, content);
+ if (NS_FAILED(rv)) {
+ delete mMenu;
+ mMenu = nullptr;
+ return rv;
+ }
+
+ mMenu->SetupIcon();
+
+ return NS_OK;
+}
+
+static void
+UpdateMenu(nsMenuX * aMenu)
+{
+ aMenu->MenuOpened();
+ aMenu->MenuClosed();
+
+ uint32_t itemCount = aMenu->GetItemCount();
+ for (uint32_t i = 0; i < itemCount; i++) {
+ nsMenuObjectX * menuObject = aMenu->GetItemAt(i);
+ if (menuObject->MenuObjectType() == eSubmenuObjectType) {
+ UpdateMenu(static_cast<nsMenuX*>(menuObject));
+ }
+ }
+}
+
+NS_IMETHODIMP
+nsStandaloneNativeMenu::MenuWillOpen(bool * aResult)
+{
+ NS_ASSERTION(mMenu != nullptr, "nsStandaloneNativeMenu::OnOpen - mMenu is null!");
+
+ // Force an update on the mMenu by faking an open/close on all of
+ // its submenus.
+ UpdateMenu(mMenu);
+
+ *aResult = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStandaloneNativeMenu::GetNativeMenu(void ** aVoidPointer)
+{
+ if (mMenu) {
+ *aVoidPointer = mMenu->NativeData();
+ [[(NSObject *)(*aVoidPointer) retain] autorelease];
+ return NS_OK;
+ } else {
+ *aVoidPointer = nullptr;
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+}
+
+static NSMenuItem *
+NativeMenuItemWithLocation(NSMenu * currentSubmenu, NSString * locationString)
+{
+ NSArray * indexes = [locationString componentsSeparatedByString:@"|"];
+ NSUInteger indexCount = [indexes count];
+ if (indexCount == 0)
+ return nil;
+
+ for (NSUInteger i = 0; i < indexCount; i++) {
+ NSInteger targetIndex = [[indexes objectAtIndex:i] integerValue];
+ NSInteger itemCount = [currentSubmenu numberOfItems];
+ if (targetIndex < itemCount) {
+ NSMenuItem* menuItem = [currentSubmenu itemAtIndex:targetIndex];
+
+ // If this is the last index, just return the menu item.
+ if (i == (indexCount - 1))
+ return menuItem;
+
+ // If this is not the last index, find the submenu and keep going.
+ if ([menuItem hasSubmenu])
+ currentSubmenu = [menuItem submenu];
+ else
+ return nil;
+ }
+ }
+
+ return nil;
+}
+
+NS_IMETHODIMP
+nsStandaloneNativeMenu::ActivateNativeMenuItemAt(const nsAString& indexString)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ if (!mMenu)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ NSString * locationString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(indexString.BeginReading())
+ length:indexString.Length()];
+ NSMenu * menu = static_cast<NSMenu *> (mMenu->NativeData());
+ NSMenuItem * item = NativeMenuItemWithLocation(menu, locationString);
+
+ // We can't perform an action on an item with a submenu, that will raise
+ // an obj-c exception.
+ if (item && ![item hasSubmenu]) {
+ NSMenu * parent = [item menu];
+ if (parent) {
+ // NSLog(@"Performing action for native menu item titled: %@\n",
+ // [[currentSubmenu itemAtIndex:targetIndex] title]);
+ [parent performActionForItemAtIndex:[parent indexOfItem:item]];
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP
+nsStandaloneNativeMenu::ForceUpdateNativeMenuAt(const nsAString& indexString)
+{
+ if (!mMenu)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ 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 NS_OK;
+
+ nsMenuX* currentMenu = mMenu;
+
+ // now find the correct submenu
+ for (unsigned int i = 1; currentMenu && i < indexCount; i++) {
+ int targetIndex = [[indexes objectAtIndex:i] intValue];
+ int visible = 0;
+ uint32_t length = currentMenu->GetItemCount();
+ for (unsigned int j = 0; j < length; j++) {
+ nsMenuObjectX* targetMenu = currentMenu->GetItemAt(j);
+ if (!targetMenu)
+ return NS_OK;
+ 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;
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+void
+nsStandaloneNativeMenu::IconUpdated()
+{
+ if (mContainerStatusBarItem) {
+ [mContainerStatusBarItem setImage:[mMenu->NativeMenuItem() image]];
+ }
+}
+
+void
+nsStandaloneNativeMenu::SetContainerStatusBarItem(NSStatusItem* aItem)
+{
+ mContainerStatusBarItem = aItem;
+ IconUpdated();
+}