summaryrefslogtreecommitdiffstats
path: root/widget/cocoa/nsMenuUtilsX.mm
diff options
context:
space:
mode:
Diffstat (limited to 'widget/cocoa/nsMenuUtilsX.mm')
-rw-r--r--widget/cocoa/nsMenuUtilsX.mm223
1 files changed, 223 insertions, 0 deletions
diff --git a/widget/cocoa/nsMenuUtilsX.mm b/widget/cocoa/nsMenuUtilsX.mm
new file mode 100644
index 000000000..db6471712
--- /dev/null
+++ b/widget/cocoa/nsMenuUtilsX.mm
@@ -0,0 +1,223 @@
+/* -*- 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 "mozilla/dom/Event.h"
+#include "nsMenuUtilsX.h"
+#include "nsMenuBarX.h"
+#include "nsMenuX.h"
+#include "nsMenuItemX.h"
+#include "nsStandaloneNativeMenu.h"
+#include "nsObjCExceptions.h"
+#include "nsCocoaUtils.h"
+#include "nsCocoaWindow.h"
+#include "nsGkAtoms.h"
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMXULCommandEvent.h"
+#include "nsPIDOMWindow.h"
+#include "nsQueryObject.h"
+
+using namespace mozilla;
+
+void nsMenuUtilsX::DispatchCommandTo(nsIContent* aTargetContent)
+{
+ NS_PRECONDITION(aTargetContent, "null ptr");
+
+ nsIDocument* doc = aTargetContent->OwnerDoc();
+ if (doc) {
+ ErrorResult rv;
+ RefPtr<dom::Event> event =
+ doc->CreateEvent(NS_LITERAL_STRING("xulcommandevent"), rv);
+ nsCOMPtr<nsIDOMXULCommandEvent> command = do_QueryObject(event);
+
+ // FIXME: Should probably figure out how to init this with the actual
+ // pressed keys, but this is a big old edge case anyway. -dwh
+ if (command &&
+ NS_SUCCEEDED(command->InitCommandEvent(NS_LITERAL_STRING("command"),
+ true, true,
+ doc->GetInnerWindow(), 0,
+ false, false, false,
+ false, nullptr))) {
+ event->SetTrusted(true);
+ bool dummy;
+ aTargetContent->DispatchEvent(event, &dummy);
+ }
+ }
+}
+
+NSString* nsMenuUtilsX::GetTruncatedCocoaLabel(const nsString& itemLabel)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ // We want to truncate long strings to some reasonable pixel length but there is no
+ // good API for doing that which works for all OS versions and architectures. For now
+ // we'll do nothing for consistency and depend on good user interface design to limit
+ // string lengths.
+ return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(itemLabel.get())
+ length:itemLabel.Length()];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+uint8_t nsMenuUtilsX::GeckoModifiersForNodeAttribute(const nsString& modifiersAttribute)
+{
+ uint8_t modifiers = knsMenuItemNoModifier;
+ char* str = ToNewCString(modifiersAttribute);
+ char* newStr;
+ char* token = strtok_r(str, ", \t", &newStr);
+ while (token != NULL) {
+ if (strcmp(token, "shift") == 0)
+ modifiers |= knsMenuItemShiftModifier;
+ else if (strcmp(token, "alt") == 0)
+ modifiers |= knsMenuItemAltModifier;
+ else if (strcmp(token, "control") == 0)
+ modifiers |= knsMenuItemControlModifier;
+ else if ((strcmp(token, "accel") == 0) ||
+ (strcmp(token, "meta") == 0)) {
+ modifiers |= knsMenuItemCommandModifier;
+ }
+ token = strtok_r(newStr, ", \t", &newStr);
+ }
+ free(str);
+
+ return modifiers;
+}
+
+unsigned int nsMenuUtilsX::MacModifiersForGeckoModifiers(uint8_t geckoModifiers)
+{
+ unsigned int macModifiers = 0;
+
+ if (geckoModifiers & knsMenuItemShiftModifier)
+ macModifiers |= NSShiftKeyMask;
+ if (geckoModifiers & knsMenuItemAltModifier)
+ macModifiers |= NSAlternateKeyMask;
+ if (geckoModifiers & knsMenuItemControlModifier)
+ macModifiers |= NSControlKeyMask;
+ if (geckoModifiers & knsMenuItemCommandModifier)
+ macModifiers |= NSCommandKeyMask;
+
+ return macModifiers;
+}
+
+nsMenuBarX* nsMenuUtilsX::GetHiddenWindowMenuBar()
+{
+ nsIWidget* hiddenWindowWidgetNoCOMPtr = nsCocoaUtils::GetHiddenWindowWidget();
+ if (hiddenWindowWidgetNoCOMPtr)
+ return static_cast<nsCocoaWindow*>(hiddenWindowWidgetNoCOMPtr)->GetMenuBar();
+ else
+ return nullptr;
+}
+
+// It would be nice if we could localize these edit menu names.
+NSMenuItem* nsMenuUtilsX::GetStandardEditMenuItem()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ // In principle we should be able to allocate this once and then always
+ // return the same object. But weird interactions happen between native
+ // app-modal dialogs and Gecko-modal dialogs that open above them. So what
+ // we return here isn't always released before it needs to be added to
+ // another menu. See bmo bug 468393.
+ NSMenuItem* standardEditMenuItem =
+ [[[NSMenuItem alloc] initWithTitle:@"Edit" action:nil keyEquivalent:@""] autorelease];
+ NSMenu* standardEditMenu = [[NSMenu alloc] initWithTitle:@"Edit"];
+ [standardEditMenuItem setSubmenu:standardEditMenu];
+ [standardEditMenu release];
+
+ // Add Undo
+ NSMenuItem* undoItem = [[NSMenuItem alloc] initWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:@"z"];
+ [standardEditMenu addItem:undoItem];
+ [undoItem release];
+
+ // Add Redo
+ NSMenuItem* redoItem = [[NSMenuItem alloc] initWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:@"Z"];
+ [standardEditMenu addItem:redoItem];
+ [redoItem release];
+
+ // Add separator
+ [standardEditMenu addItem:[NSMenuItem separatorItem]];
+
+ // Add Cut
+ NSMenuItem* cutItem = [[NSMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"];
+ [standardEditMenu addItem:cutItem];
+ [cutItem release];
+
+ // Add Copy
+ NSMenuItem* copyItem = [[NSMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"];
+ [standardEditMenu addItem:copyItem];
+ [copyItem release];
+
+ // Add Paste
+ NSMenuItem* pasteItem = [[NSMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"];
+ [standardEditMenu addItem:pasteItem];
+ [pasteItem release];
+
+ // Add Delete
+ NSMenuItem* deleteItem = [[NSMenuItem alloc] initWithTitle:@"Delete" action:@selector(delete:) keyEquivalent:@""];
+ [standardEditMenu addItem:deleteItem];
+ [deleteItem release];
+
+ // Add Select All
+ NSMenuItem* selectAllItem = [[NSMenuItem alloc] initWithTitle:@"Select All" action:@selector(selectAll:) keyEquivalent:@"a"];
+ [standardEditMenu addItem:selectAllItem];
+ [selectAllItem release];
+
+ return standardEditMenuItem;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+bool nsMenuUtilsX::NodeIsHiddenOrCollapsed(nsIContent* inContent)
+{
+ return (inContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
+ nsGkAtoms::_true, eCaseMatters) ||
+ inContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::collapsed,
+ nsGkAtoms::_true, eCaseMatters));
+}
+
+// Determines how many items are visible among the siblings in a menu that are
+// before the given child. This will not count the application menu.
+int nsMenuUtilsX::CalculateNativeInsertionPoint(nsMenuObjectX* aParent,
+ nsMenuObjectX* aChild)
+{
+ int insertionPoint = 0;
+ nsMenuObjectTypeX parentType = aParent->MenuObjectType();
+ if (parentType == eMenuBarObjectType) {
+ nsMenuBarX* menubarParent = static_cast<nsMenuBarX*>(aParent);
+ uint32_t numMenus = menubarParent->GetMenuCount();
+ for (uint32_t i = 0; i < numMenus; i++) {
+ nsMenuX* currMenu = menubarParent->GetMenuAt(i);
+ if (currMenu == aChild)
+ return insertionPoint; // we found ourselves, break out
+ if (currMenu && [currMenu->NativeMenuItem() menu])
+ insertionPoint++;
+ }
+ }
+ else if (parentType == eSubmenuObjectType ||
+ parentType == eStandaloneNativeMenuObjectType) {
+ nsMenuX* menuParent;
+ if (parentType == eSubmenuObjectType)
+ menuParent = static_cast<nsMenuX*>(aParent);
+ else
+ menuParent = static_cast<nsStandaloneNativeMenu*>(aParent)->GetMenuXObject();
+
+ uint32_t numItems = menuParent->GetItemCount();
+ for (uint32_t i = 0; i < numItems; i++) {
+ // Using GetItemAt instead of GetVisibleItemAt to avoid O(N^2)
+ nsMenuObjectX* currItem = menuParent->GetItemAt(i);
+ if (currItem == aChild)
+ return insertionPoint; // we found ourselves, break out
+ NSMenuItem* nativeItem = nil;
+ nsMenuObjectTypeX currItemType = currItem->MenuObjectType();
+ if (currItemType == eSubmenuObjectType)
+ nativeItem = static_cast<nsMenuX*>(currItem)->NativeMenuItem();
+ else
+ nativeItem = (NSMenuItem*)(currItem->NativeData());
+ if ([nativeItem menu])
+ insertionPoint++;
+ }
+ }
+ return insertionPoint;
+}