widget/cocoa/nsMenuUtilsX.mm

changeset 0
6474c204b198
     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 +}

mercurial