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