widget/cocoa/nsStandaloneNativeMenu.mm

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

michael@0 1 /* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #import <Cocoa/Cocoa.h>
michael@0 7
michael@0 8 #include "nsStandaloneNativeMenu.h"
michael@0 9 #include "nsMenuUtilsX.h"
michael@0 10 #include "nsIDOMElement.h"
michael@0 11 #include "nsIMutationObserver.h"
michael@0 12 #include "nsGkAtoms.h"
michael@0 13 #include "nsObjCExceptions.h"
michael@0 14
michael@0 15
michael@0 16 NS_IMPL_ISUPPORTS(nsStandaloneNativeMenu, nsIMutationObserver, nsIStandaloneNativeMenu)
michael@0 17
michael@0 18 nsStandaloneNativeMenu::nsStandaloneNativeMenu()
michael@0 19 : mMenu(nullptr)
michael@0 20 {
michael@0 21 }
michael@0 22
michael@0 23 nsStandaloneNativeMenu::~nsStandaloneNativeMenu()
michael@0 24 {
michael@0 25 if (mMenu) delete mMenu;
michael@0 26 }
michael@0 27
michael@0 28 NS_IMETHODIMP
michael@0 29 nsStandaloneNativeMenu::Init(nsIDOMElement * aDOMElement)
michael@0 30 {
michael@0 31 NS_ASSERTION(mMenu == nullptr, "nsNativeMenu::Init - mMenu not null!");
michael@0 32
michael@0 33 nsresult rv;
michael@0 34
michael@0 35 nsCOMPtr<nsIContent> content = do_QueryInterface(aDOMElement, &rv);
michael@0 36 NS_ENSURE_SUCCESS(rv, rv);
michael@0 37
michael@0 38 nsIAtom * tag = content->Tag();
michael@0 39 if (!content->IsXUL() ||
michael@0 40 (tag != nsGkAtoms::menu && tag != nsGkAtoms::menupopup))
michael@0 41 return NS_ERROR_FAILURE;
michael@0 42
michael@0 43 rv = nsMenuGroupOwnerX::Create(content);
michael@0 44 if (NS_FAILED(rv))
michael@0 45 return rv;
michael@0 46
michael@0 47 mMenu = new nsMenuX();
michael@0 48 rv = mMenu->Create(this, this, content);
michael@0 49 if (NS_FAILED(rv)) {
michael@0 50 delete mMenu;
michael@0 51 mMenu = nullptr;
michael@0 52 return rv;
michael@0 53 }
michael@0 54
michael@0 55 return NS_OK;
michael@0 56 }
michael@0 57
michael@0 58 static void
michael@0 59 UpdateMenu(nsMenuX * aMenu)
michael@0 60 {
michael@0 61 aMenu->MenuOpened();
michael@0 62 aMenu->MenuClosed();
michael@0 63
michael@0 64 uint32_t itemCount = aMenu->GetItemCount();
michael@0 65 for (uint32_t i = 0; i < itemCount; i++) {
michael@0 66 nsMenuObjectX * menuObject = aMenu->GetItemAt(i);
michael@0 67 if (menuObject->MenuObjectType() == eSubmenuObjectType) {
michael@0 68 UpdateMenu(static_cast<nsMenuX*>(menuObject));
michael@0 69 }
michael@0 70 }
michael@0 71 }
michael@0 72
michael@0 73 NS_IMETHODIMP
michael@0 74 nsStandaloneNativeMenu::MenuWillOpen(bool * aResult)
michael@0 75 {
michael@0 76 NS_ASSERTION(mMenu != nullptr, "nsStandaloneNativeMenu::OnOpen - mMenu is null!");
michael@0 77
michael@0 78 // Force an update on the mMenu by faking an open/close on all of
michael@0 79 // its submenus.
michael@0 80 UpdateMenu(mMenu);
michael@0 81
michael@0 82 *aResult = true;
michael@0 83 return NS_OK;
michael@0 84 }
michael@0 85
michael@0 86 NS_IMETHODIMP
michael@0 87 nsStandaloneNativeMenu::GetNativeMenu(void ** aVoidPointer)
michael@0 88 {
michael@0 89 if (mMenu) {
michael@0 90 *aVoidPointer = mMenu->NativeData();
michael@0 91 [[(NSObject *)(*aVoidPointer) retain] autorelease];
michael@0 92 return NS_OK;
michael@0 93 } else {
michael@0 94 *aVoidPointer = nullptr;
michael@0 95 return NS_ERROR_NOT_INITIALIZED;
michael@0 96 }
michael@0 97 }
michael@0 98
michael@0 99 static NSMenuItem *
michael@0 100 NativeMenuItemWithLocation(NSMenu * currentSubmenu, NSString * locationString)
michael@0 101 {
michael@0 102 NSArray * indexes = [locationString componentsSeparatedByString:@"|"];
michael@0 103 NSUInteger indexCount = [indexes count];
michael@0 104 if (indexCount == 0)
michael@0 105 return nil;
michael@0 106
michael@0 107 for (NSUInteger i = 0; i < indexCount; i++) {
michael@0 108 NSInteger targetIndex = [[indexes objectAtIndex:i] integerValue];
michael@0 109 NSInteger itemCount = [currentSubmenu numberOfItems];
michael@0 110 if (targetIndex < itemCount) {
michael@0 111 NSMenuItem* menuItem = [currentSubmenu itemAtIndex:targetIndex];
michael@0 112
michael@0 113 // If this is the last index, just return the menu item.
michael@0 114 if (i == (indexCount - 1))
michael@0 115 return menuItem;
michael@0 116
michael@0 117 // If this is not the last index, find the submenu and keep going.
michael@0 118 if ([menuItem hasSubmenu])
michael@0 119 currentSubmenu = [menuItem submenu];
michael@0 120 else
michael@0 121 return nil;
michael@0 122 }
michael@0 123 }
michael@0 124
michael@0 125 return nil;
michael@0 126 }
michael@0 127
michael@0 128 NS_IMETHODIMP
michael@0 129 nsStandaloneNativeMenu::ActivateNativeMenuItemAt(const nsAString& indexString)
michael@0 130 {
michael@0 131 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
michael@0 132
michael@0 133 if (!mMenu)
michael@0 134 return NS_ERROR_NOT_INITIALIZED;
michael@0 135
michael@0 136 NSString * locationString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(indexString.BeginReading())
michael@0 137 length:indexString.Length()];
michael@0 138 NSMenu * menu = static_cast<NSMenu *> (mMenu->NativeData());
michael@0 139 NSMenuItem * item = NativeMenuItemWithLocation(menu, locationString);
michael@0 140
michael@0 141 // We can't perform an action on an item with a submenu, that will raise
michael@0 142 // an obj-c exception.
michael@0 143 if (item && ![item hasSubmenu]) {
michael@0 144 NSMenu * parent = [item menu];
michael@0 145 if (parent) {
michael@0 146 // NSLog(@"Performing action for native menu item titled: %@\n",
michael@0 147 // [[currentSubmenu itemAtIndex:targetIndex] title]);
michael@0 148 [parent performActionForItemAtIndex:[parent indexOfItem:item]];
michael@0 149 return NS_OK;
michael@0 150 }
michael@0 151 }
michael@0 152
michael@0 153 return NS_ERROR_FAILURE;
michael@0 154
michael@0 155 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
michael@0 156 }
michael@0 157
michael@0 158 NS_IMETHODIMP
michael@0 159 nsStandaloneNativeMenu::ForceUpdateNativeMenuAt(const nsAString& indexString)
michael@0 160 {
michael@0 161 if (!mMenu)
michael@0 162 return NS_ERROR_NOT_INITIALIZED;
michael@0 163
michael@0 164 NSString* locationString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(indexString.BeginReading())
michael@0 165 length:indexString.Length()];
michael@0 166 NSArray* indexes = [locationString componentsSeparatedByString:@"|"];
michael@0 167 unsigned int indexCount = [indexes count];
michael@0 168 if (indexCount == 0)
michael@0 169 return NS_OK;
michael@0 170
michael@0 171 nsMenuX* currentMenu = mMenu;
michael@0 172
michael@0 173 // now find the correct submenu
michael@0 174 for (unsigned int i = 1; currentMenu && i < indexCount; i++) {
michael@0 175 int targetIndex = [[indexes objectAtIndex:i] intValue];
michael@0 176 int visible = 0;
michael@0 177 uint32_t length = currentMenu->GetItemCount();
michael@0 178 for (unsigned int j = 0; j < length; j++) {
michael@0 179 nsMenuObjectX* targetMenu = currentMenu->GetItemAt(j);
michael@0 180 if (!targetMenu)
michael@0 181 return NS_OK;
michael@0 182 if (!nsMenuUtilsX::NodeIsHiddenOrCollapsed(targetMenu->Content())) {
michael@0 183 visible++;
michael@0 184 if (targetMenu->MenuObjectType() == eSubmenuObjectType && visible == (targetIndex + 1)) {
michael@0 185 currentMenu = static_cast<nsMenuX*>(targetMenu);
michael@0 186 // fake open/close to cause lazy update to happen
michael@0 187 currentMenu->MenuOpened();
michael@0 188 currentMenu->MenuClosed();
michael@0 189 break;
michael@0 190 }
michael@0 191 }
michael@0 192 }
michael@0 193 }
michael@0 194
michael@0 195 return NS_OK;
michael@0 196 }

mercurial