widget/cocoa/nsStandaloneNativeMenu.mm

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/widget/cocoa/nsStandaloneNativeMenu.mm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,196 @@
     1.4 +/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */
     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 +#import <Cocoa/Cocoa.h>
    1.10 +
    1.11 +#include "nsStandaloneNativeMenu.h"
    1.12 +#include "nsMenuUtilsX.h"
    1.13 +#include "nsIDOMElement.h"
    1.14 +#include "nsIMutationObserver.h"
    1.15 +#include "nsGkAtoms.h"
    1.16 +#include "nsObjCExceptions.h"
    1.17 +
    1.18 +
    1.19 +NS_IMPL_ISUPPORTS(nsStandaloneNativeMenu, nsIMutationObserver, nsIStandaloneNativeMenu)
    1.20 +
    1.21 +nsStandaloneNativeMenu::nsStandaloneNativeMenu()
    1.22 +: mMenu(nullptr)
    1.23 +{
    1.24 +}
    1.25 +
    1.26 +nsStandaloneNativeMenu::~nsStandaloneNativeMenu()
    1.27 +{
    1.28 +  if (mMenu) delete mMenu;
    1.29 +}
    1.30 +
    1.31 +NS_IMETHODIMP
    1.32 +nsStandaloneNativeMenu::Init(nsIDOMElement * aDOMElement)
    1.33 +{
    1.34 +  NS_ASSERTION(mMenu == nullptr, "nsNativeMenu::Init - mMenu not null!");
    1.35 +
    1.36 +  nsresult rv;
    1.37 +
    1.38 +  nsCOMPtr<nsIContent> content = do_QueryInterface(aDOMElement, &rv);
    1.39 +  NS_ENSURE_SUCCESS(rv, rv);
    1.40 +
    1.41 +  nsIAtom * tag = content->Tag();
    1.42 +  if (!content->IsXUL() ||
    1.43 +      (tag != nsGkAtoms::menu && tag != nsGkAtoms::menupopup))
    1.44 +    return NS_ERROR_FAILURE;
    1.45 +
    1.46 +  rv = nsMenuGroupOwnerX::Create(content);
    1.47 +  if (NS_FAILED(rv))
    1.48 +    return rv;
    1.49 +
    1.50 +  mMenu = new nsMenuX();
    1.51 +  rv = mMenu->Create(this, this, content);
    1.52 +  if (NS_FAILED(rv)) {
    1.53 +    delete mMenu;
    1.54 +    mMenu = nullptr;
    1.55 +    return rv;
    1.56 +  }
    1.57 +
    1.58 +  return NS_OK;
    1.59 +}
    1.60 +
    1.61 +static void
    1.62 +UpdateMenu(nsMenuX * aMenu)
    1.63 +{
    1.64 +  aMenu->MenuOpened();
    1.65 +  aMenu->MenuClosed();
    1.66 +
    1.67 +  uint32_t itemCount = aMenu->GetItemCount();
    1.68 +  for (uint32_t i = 0; i < itemCount; i++) {
    1.69 +    nsMenuObjectX * menuObject = aMenu->GetItemAt(i);
    1.70 +    if (menuObject->MenuObjectType() == eSubmenuObjectType) {
    1.71 +      UpdateMenu(static_cast<nsMenuX*>(menuObject));
    1.72 +    }
    1.73 +  }
    1.74 +}
    1.75 +
    1.76 +NS_IMETHODIMP
    1.77 +nsStandaloneNativeMenu::MenuWillOpen(bool * aResult)
    1.78 +{
    1.79 +  NS_ASSERTION(mMenu != nullptr, "nsStandaloneNativeMenu::OnOpen - mMenu is null!");
    1.80 +
    1.81 +  // Force an update on the mMenu by faking an open/close on all of
    1.82 +  // its submenus.
    1.83 +  UpdateMenu(mMenu);
    1.84 +
    1.85 +  *aResult = true;
    1.86 +  return NS_OK;
    1.87 +}
    1.88 +
    1.89 +NS_IMETHODIMP
    1.90 +nsStandaloneNativeMenu::GetNativeMenu(void ** aVoidPointer)
    1.91 +{
    1.92 +  if (mMenu) {
    1.93 +    *aVoidPointer = mMenu->NativeData();
    1.94 +    [[(NSObject *)(*aVoidPointer) retain] autorelease];
    1.95 +    return NS_OK;
    1.96 +  }  else {
    1.97 +    *aVoidPointer = nullptr;
    1.98 +    return NS_ERROR_NOT_INITIALIZED;
    1.99 +  }
   1.100 +}
   1.101 +
   1.102 +static NSMenuItem *
   1.103 +NativeMenuItemWithLocation(NSMenu * currentSubmenu, NSString * locationString)
   1.104 +{
   1.105 +  NSArray * indexes = [locationString componentsSeparatedByString:@"|"];
   1.106 +  NSUInteger indexCount = [indexes count];
   1.107 +  if (indexCount == 0)
   1.108 +    return nil;
   1.109 +
   1.110 +  for (NSUInteger i = 0; i < indexCount; i++) {
   1.111 +    NSInteger targetIndex = [[indexes objectAtIndex:i] integerValue];
   1.112 +    NSInteger itemCount = [currentSubmenu numberOfItems];
   1.113 +    if (targetIndex < itemCount) {
   1.114 +      NSMenuItem* menuItem = [currentSubmenu itemAtIndex:targetIndex];
   1.115 +
   1.116 +      // If this is the last index, just return the menu item.
   1.117 +      if (i == (indexCount - 1))
   1.118 +        return menuItem;
   1.119 +
   1.120 +      // If this is not the last index, find the submenu and keep going.
   1.121 +      if ([menuItem hasSubmenu])
   1.122 +        currentSubmenu = [menuItem submenu];
   1.123 +      else
   1.124 +        return nil;
   1.125 +    }
   1.126 +  }
   1.127 +
   1.128 +  return nil;
   1.129 +}
   1.130 +
   1.131 +NS_IMETHODIMP
   1.132 +nsStandaloneNativeMenu::ActivateNativeMenuItemAt(const nsAString& indexString)
   1.133 +{
   1.134 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   1.135 +  
   1.136 +  if (!mMenu)
   1.137 +    return NS_ERROR_NOT_INITIALIZED;
   1.138 +
   1.139 +  NSString * locationString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(indexString.BeginReading())
   1.140 +                                                      length:indexString.Length()];
   1.141 +  NSMenu * menu = static_cast<NSMenu *> (mMenu->NativeData());
   1.142 +  NSMenuItem * item = NativeMenuItemWithLocation(menu, locationString);
   1.143 +
   1.144 +  // We can't perform an action on an item with a submenu, that will raise
   1.145 +  // an obj-c exception.
   1.146 +  if (item && ![item hasSubmenu]) {
   1.147 +    NSMenu * parent = [item menu];
   1.148 +    if (parent) {
   1.149 +      // NSLog(@"Performing action for native menu item titled: %@\n",
   1.150 +      //       [[currentSubmenu itemAtIndex:targetIndex] title]);
   1.151 +      [parent performActionForItemAtIndex:[parent indexOfItem:item]];
   1.152 +      return NS_OK;
   1.153 +    }
   1.154 +  }
   1.155 +
   1.156 +  return NS_ERROR_FAILURE;
   1.157 +  
   1.158 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   1.159 +}
   1.160 +
   1.161 +NS_IMETHODIMP
   1.162 +nsStandaloneNativeMenu::ForceUpdateNativeMenuAt(const nsAString& indexString)
   1.163 +{
   1.164 +  if (!mMenu)
   1.165 +    return NS_ERROR_NOT_INITIALIZED;
   1.166 +
   1.167 +  NSString* locationString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(indexString.BeginReading())
   1.168 +                                                     length:indexString.Length()];
   1.169 +  NSArray* indexes = [locationString componentsSeparatedByString:@"|"];
   1.170 +  unsigned int indexCount = [indexes count];
   1.171 +  if (indexCount == 0)
   1.172 +    return NS_OK;
   1.173 +
   1.174 +  nsMenuX* currentMenu = mMenu;
   1.175 +
   1.176 +  // now find the correct submenu
   1.177 +  for (unsigned int i = 1; currentMenu && i < indexCount; i++) {
   1.178 +    int targetIndex = [[indexes objectAtIndex:i] intValue];
   1.179 +    int visible = 0;
   1.180 +    uint32_t length = currentMenu->GetItemCount();
   1.181 +    for (unsigned int j = 0; j < length; j++) {
   1.182 +      nsMenuObjectX* targetMenu = currentMenu->GetItemAt(j);
   1.183 +      if (!targetMenu)
   1.184 +        return NS_OK;
   1.185 +      if (!nsMenuUtilsX::NodeIsHiddenOrCollapsed(targetMenu->Content())) {
   1.186 +        visible++;
   1.187 +        if (targetMenu->MenuObjectType() == eSubmenuObjectType && visible == (targetIndex + 1)) {
   1.188 +          currentMenu = static_cast<nsMenuX*>(targetMenu);
   1.189 +          // fake open/close to cause lazy update to happen
   1.190 +          currentMenu->MenuOpened();
   1.191 +          currentMenu->MenuClosed();
   1.192 +          break;
   1.193 +        }
   1.194 +      }
   1.195 +    }
   1.196 +  }
   1.197 +
   1.198 +  return NS_OK;
   1.199 +}

mercurial