diff options
Diffstat (limited to 'widget/cocoa/nsMenuUtilsX.mm')
-rw-r--r-- | widget/cocoa/nsMenuUtilsX.mm | 223 |
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; +} |