1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/cocoa/nsMenuUtilsX.mm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,222 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/dom/Event.h" 1.10 +#include "nsMenuUtilsX.h" 1.11 +#include "nsMenuBarX.h" 1.12 +#include "nsMenuX.h" 1.13 +#include "nsMenuItemX.h" 1.14 +#include "nsStandaloneNativeMenu.h" 1.15 +#include "nsObjCExceptions.h" 1.16 +#include "nsCocoaUtils.h" 1.17 +#include "nsCocoaWindow.h" 1.18 +#include "nsGkAtoms.h" 1.19 +#include "nsIDocument.h" 1.20 +#include "nsIDOMDocument.h" 1.21 +#include "nsIDOMXULCommandEvent.h" 1.22 +#include "nsPIDOMWindow.h" 1.23 + 1.24 +using namespace mozilla; 1.25 + 1.26 +void nsMenuUtilsX::DispatchCommandTo(nsIContent* aTargetContent) 1.27 +{ 1.28 + NS_PRECONDITION(aTargetContent, "null ptr"); 1.29 + 1.30 + nsIDocument* doc = aTargetContent->OwnerDoc(); 1.31 + if (doc) { 1.32 + ErrorResult rv; 1.33 + nsRefPtr<dom::Event> event = 1.34 + doc->CreateEvent(NS_LITERAL_STRING("xulcommandevent"), rv); 1.35 + nsCOMPtr<nsIDOMXULCommandEvent> command = do_QueryObject(event); 1.36 + 1.37 + // FIXME: Should probably figure out how to init this with the actual 1.38 + // pressed keys, but this is a big old edge case anyway. -dwh 1.39 + if (command && 1.40 + NS_SUCCEEDED(command->InitCommandEvent(NS_LITERAL_STRING("command"), 1.41 + true, true, 1.42 + doc->GetWindow(), 0, 1.43 + false, false, false, 1.44 + false, nullptr))) { 1.45 + event->SetTrusted(true); 1.46 + bool dummy; 1.47 + aTargetContent->DispatchEvent(event, &dummy); 1.48 + } 1.49 + } 1.50 +} 1.51 + 1.52 +NSString* nsMenuUtilsX::GetTruncatedCocoaLabel(const nsString& itemLabel) 1.53 +{ 1.54 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.55 + 1.56 + // We want to truncate long strings to some reasonable pixel length but there is no 1.57 + // good API for doing that which works for all OS versions and architectures. For now 1.58 + // we'll do nothing for consistency and depend on good user interface design to limit 1.59 + // string lengths. 1.60 + return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(itemLabel.get()) 1.61 + length:itemLabel.Length()]; 1.62 + 1.63 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.64 +} 1.65 + 1.66 +uint8_t nsMenuUtilsX::GeckoModifiersForNodeAttribute(const nsString& modifiersAttribute) 1.67 +{ 1.68 + uint8_t modifiers = knsMenuItemNoModifier; 1.69 + char* str = ToNewCString(modifiersAttribute); 1.70 + char* newStr; 1.71 + char* token = strtok_r(str, ", \t", &newStr); 1.72 + while (token != NULL) { 1.73 + if (strcmp(token, "shift") == 0) 1.74 + modifiers |= knsMenuItemShiftModifier; 1.75 + else if (strcmp(token, "alt") == 0) 1.76 + modifiers |= knsMenuItemAltModifier; 1.77 + else if (strcmp(token, "control") == 0) 1.78 + modifiers |= knsMenuItemControlModifier; 1.79 + else if ((strcmp(token, "accel") == 0) || 1.80 + (strcmp(token, "meta") == 0)) { 1.81 + modifiers |= knsMenuItemCommandModifier; 1.82 + } 1.83 + token = strtok_r(newStr, ", \t", &newStr); 1.84 + } 1.85 + free(str); 1.86 + 1.87 + return modifiers; 1.88 +} 1.89 + 1.90 +unsigned int nsMenuUtilsX::MacModifiersForGeckoModifiers(uint8_t geckoModifiers) 1.91 +{ 1.92 + unsigned int macModifiers = 0; 1.93 + 1.94 + if (geckoModifiers & knsMenuItemShiftModifier) 1.95 + macModifiers |= NSShiftKeyMask; 1.96 + if (geckoModifiers & knsMenuItemAltModifier) 1.97 + macModifiers |= NSAlternateKeyMask; 1.98 + if (geckoModifiers & knsMenuItemControlModifier) 1.99 + macModifiers |= NSControlKeyMask; 1.100 + if (geckoModifiers & knsMenuItemCommandModifier) 1.101 + macModifiers |= NSCommandKeyMask; 1.102 + 1.103 + return macModifiers; 1.104 +} 1.105 + 1.106 +nsMenuBarX* nsMenuUtilsX::GetHiddenWindowMenuBar() 1.107 +{ 1.108 + nsIWidget* hiddenWindowWidgetNoCOMPtr = nsCocoaUtils::GetHiddenWindowWidget(); 1.109 + if (hiddenWindowWidgetNoCOMPtr) 1.110 + return static_cast<nsCocoaWindow*>(hiddenWindowWidgetNoCOMPtr)->GetMenuBar(); 1.111 + else 1.112 + return nullptr; 1.113 +} 1.114 + 1.115 +// It would be nice if we could localize these edit menu names. 1.116 +NSMenuItem* nsMenuUtilsX::GetStandardEditMenuItem() 1.117 +{ 1.118 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.119 + 1.120 + // In principle we should be able to allocate this once and then always 1.121 + // return the same object. But wierd interactions happen between native 1.122 + // app-modal dialogs and Gecko-modal dialogs that open above them. So what 1.123 + // we return here isn't always released before it needs to be added to 1.124 + // another menu. See bmo bug 468393. 1.125 + NSMenuItem* standardEditMenuItem = 1.126 + [[[NSMenuItem alloc] initWithTitle:@"Edit" action:nil keyEquivalent:@""] autorelease]; 1.127 + NSMenu* standardEditMenu = [[NSMenu alloc] initWithTitle:@"Edit"]; 1.128 + [standardEditMenuItem setSubmenu:standardEditMenu]; 1.129 + [standardEditMenu release]; 1.130 + 1.131 + // Add Undo 1.132 + NSMenuItem* undoItem = [[NSMenuItem alloc] initWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:@"z"]; 1.133 + [standardEditMenu addItem:undoItem]; 1.134 + [undoItem release]; 1.135 + 1.136 + // Add Redo 1.137 + NSMenuItem* redoItem = [[NSMenuItem alloc] initWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:@"Z"]; 1.138 + [standardEditMenu addItem:redoItem]; 1.139 + [redoItem release]; 1.140 + 1.141 + // Add separator 1.142 + [standardEditMenu addItem:[NSMenuItem separatorItem]]; 1.143 + 1.144 + // Add Cut 1.145 + NSMenuItem* cutItem = [[NSMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"]; 1.146 + [standardEditMenu addItem:cutItem]; 1.147 + [cutItem release]; 1.148 + 1.149 + // Add Copy 1.150 + NSMenuItem* copyItem = [[NSMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"]; 1.151 + [standardEditMenu addItem:copyItem]; 1.152 + [copyItem release]; 1.153 + 1.154 + // Add Paste 1.155 + NSMenuItem* pasteItem = [[NSMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"]; 1.156 + [standardEditMenu addItem:pasteItem]; 1.157 + [pasteItem release]; 1.158 + 1.159 + // Add Delete 1.160 + NSMenuItem* deleteItem = [[NSMenuItem alloc] initWithTitle:@"Delete" action:@selector(delete:) keyEquivalent:@""]; 1.161 + [standardEditMenu addItem:deleteItem]; 1.162 + [deleteItem release]; 1.163 + 1.164 + // Add Select All 1.165 + NSMenuItem* selectAllItem = [[NSMenuItem alloc] initWithTitle:@"Select All" action:@selector(selectAll:) keyEquivalent:@"a"]; 1.166 + [standardEditMenu addItem:selectAllItem]; 1.167 + [selectAllItem release]; 1.168 + 1.169 + return standardEditMenuItem; 1.170 + 1.171 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.172 +} 1.173 + 1.174 +bool nsMenuUtilsX::NodeIsHiddenOrCollapsed(nsIContent* inContent) 1.175 +{ 1.176 + return (inContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden, 1.177 + nsGkAtoms::_true, eCaseMatters) || 1.178 + inContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::collapsed, 1.179 + nsGkAtoms::_true, eCaseMatters)); 1.180 +} 1.181 + 1.182 +// Determines how many items are visible among the siblings in a menu that are 1.183 +// before the given child. This will not count the application menu. 1.184 +int nsMenuUtilsX::CalculateNativeInsertionPoint(nsMenuObjectX* aParent, 1.185 + nsMenuObjectX* aChild) 1.186 +{ 1.187 + int insertionPoint = 0; 1.188 + nsMenuObjectTypeX parentType = aParent->MenuObjectType(); 1.189 + if (parentType == eMenuBarObjectType) { 1.190 + nsMenuBarX* menubarParent = static_cast<nsMenuBarX*>(aParent); 1.191 + uint32_t numMenus = menubarParent->GetMenuCount(); 1.192 + for (uint32_t i = 0; i < numMenus; i++) { 1.193 + nsMenuX* currMenu = menubarParent->GetMenuAt(i); 1.194 + if (currMenu == aChild) 1.195 + return insertionPoint; // we found ourselves, break out 1.196 + if (currMenu && [currMenu->NativeMenuItem() menu]) 1.197 + insertionPoint++; 1.198 + } 1.199 + } 1.200 + else if (parentType == eSubmenuObjectType || 1.201 + parentType == eStandaloneNativeMenuObjectType) { 1.202 + nsMenuX* menuParent; 1.203 + if (parentType == eSubmenuObjectType) 1.204 + menuParent = static_cast<nsMenuX*>(aParent); 1.205 + else 1.206 + menuParent = static_cast<nsStandaloneNativeMenu*>(aParent)->GetMenuXObject(); 1.207 + 1.208 + uint32_t numItems = menuParent->GetItemCount(); 1.209 + for (uint32_t i = 0; i < numItems; i++) { 1.210 + // Using GetItemAt instead of GetVisibleItemAt to avoid O(N^2) 1.211 + nsMenuObjectX* currItem = menuParent->GetItemAt(i); 1.212 + if (currItem == aChild) 1.213 + return insertionPoint; // we found ourselves, break out 1.214 + NSMenuItem* nativeItem = nil; 1.215 + nsMenuObjectTypeX currItemType = currItem->MenuObjectType(); 1.216 + if (currItemType == eSubmenuObjectType) 1.217 + nativeItem = static_cast<nsMenuX*>(currItem)->NativeMenuItem(); 1.218 + else 1.219 + nativeItem = (NSMenuItem*)(currItem->NativeData()); 1.220 + if ([nativeItem menu]) 1.221 + insertionPoint++; 1.222 + } 1.223 + } 1.224 + return insertionPoint; 1.225 +}