1 /* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #import <Cocoa/Cocoa.h>
8 #include "nsStandaloneNativeMenu.h"
9 #include "nsMenuUtilsX.h"
10 #include "nsIDOMElement.h"
11 #include "nsIMutationObserver.h"
12 #include "nsGkAtoms.h"
13 #include "nsObjCExceptions.h"
16 NS_IMPL_ISUPPORTS_INHERITED(nsStandaloneNativeMenu, nsMenuGroupOwnerX,
17 nsIMutationObserver, nsIStandaloneNativeMenu)
19 nsStandaloneNativeMenu::nsStandaloneNativeMenu()
21 , mContainerStatusBarItem(nil)
25 nsStandaloneNativeMenu::~nsStandaloneNativeMenu()
27 if (mMenu) delete mMenu;
31 nsStandaloneNativeMenu::Init(nsIDOMElement * aDOMElement)
33 NS_ASSERTION(mMenu == nullptr, "nsNativeMenu::Init - mMenu not null!");
37 nsCOMPtr<nsIContent> content = do_QueryInterface(aDOMElement, &rv);
38 NS_ENSURE_SUCCESS(rv, rv);
40 nsIAtom * tag = content->Tag();
41 if (!content->IsXUL() ||
42 (tag != nsGkAtoms::menu && tag != nsGkAtoms::menupopup))
43 return NS_ERROR_FAILURE;
45 rv = nsMenuGroupOwnerX::Create(content);
49 mMenu = new nsMenuX();
50 rv = mMenu->Create(this, this, content);
63 UpdateMenu(nsMenuX * aMenu)
68 uint32_t itemCount = aMenu->GetItemCount();
69 for (uint32_t i = 0; i < itemCount; i++) {
70 nsMenuObjectX * menuObject = aMenu->GetItemAt(i);
71 if (menuObject->MenuObjectType() == eSubmenuObjectType) {
72 UpdateMenu(static_cast<nsMenuX*>(menuObject));
78 nsStandaloneNativeMenu::MenuWillOpen(bool * aResult)
80 NS_ASSERTION(mMenu != nullptr, "nsStandaloneNativeMenu::OnOpen - mMenu is null!");
82 // Force an update on the mMenu by faking an open/close on all of
91 nsStandaloneNativeMenu::GetNativeMenu(void ** aVoidPointer)
94 *aVoidPointer = mMenu->NativeData();
95 [[(NSObject *)(*aVoidPointer) retain] autorelease];
98 *aVoidPointer = nullptr;
99 return NS_ERROR_NOT_INITIALIZED;
104 NativeMenuItemWithLocation(NSMenu * currentSubmenu, NSString * locationString)
106 NSArray * indexes = [locationString componentsSeparatedByString:@"|"];
107 NSUInteger indexCount = [indexes count];
111 for (NSUInteger i = 0; i < indexCount; i++) {
112 NSInteger targetIndex = [[indexes objectAtIndex:i] integerValue];
113 NSInteger itemCount = [currentSubmenu numberOfItems];
114 if (targetIndex < itemCount) {
115 NSMenuItem* menuItem = [currentSubmenu itemAtIndex:targetIndex];
117 // If this is the last index, just return the menu item.
118 if (i == (indexCount - 1))
121 // If this is not the last index, find the submenu and keep going.
122 if ([menuItem hasSubmenu])
123 currentSubmenu = [menuItem submenu];
133 nsStandaloneNativeMenu::ActivateNativeMenuItemAt(const nsAString& indexString)
135 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
138 return NS_ERROR_NOT_INITIALIZED;
140 NSString * locationString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(indexString.BeginReading())
141 length:indexString.Length()];
142 NSMenu * menu = static_cast<NSMenu *> (mMenu->NativeData());
143 NSMenuItem * item = NativeMenuItemWithLocation(menu, locationString);
145 // We can't perform an action on an item with a submenu, that will raise
146 // an obj-c exception.
147 if (item && ![item hasSubmenu]) {
148 NSMenu * parent = [item menu];
150 // NSLog(@"Performing action for native menu item titled: %@\n",
151 // [[currentSubmenu itemAtIndex:targetIndex] title]);
152 [parent performActionForItemAtIndex:[parent indexOfItem:item]];
157 return NS_ERROR_FAILURE;
159 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
163 nsStandaloneNativeMenu::ForceUpdateNativeMenuAt(const nsAString& indexString)
166 return NS_ERROR_NOT_INITIALIZED;
168 NSString* locationString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(indexString.BeginReading())
169 length:indexString.Length()];
170 NSArray* indexes = [locationString componentsSeparatedByString:@"|"];
171 unsigned int indexCount = [indexes count];
175 nsMenuX* currentMenu = mMenu;
177 // now find the correct submenu
178 for (unsigned int i = 1; currentMenu && i < indexCount; i++) {
179 int targetIndex = [[indexes objectAtIndex:i] intValue];
181 uint32_t length = currentMenu->GetItemCount();
182 for (unsigned int j = 0; j < length; j++) {
183 nsMenuObjectX* targetMenu = currentMenu->GetItemAt(j);
186 if (!nsMenuUtilsX::NodeIsHiddenOrCollapsed(targetMenu->Content())) {
188 if (targetMenu->MenuObjectType() == eSubmenuObjectType && visible == (targetIndex + 1)) {
189 currentMenu = static_cast<nsMenuX*>(targetMenu);
190 // fake open/close to cause lazy update to happen
191 currentMenu->MenuOpened();
192 currentMenu->MenuClosed();
203 nsStandaloneNativeMenu::IconUpdated()
205 if (mContainerStatusBarItem) {
206 [mContainerStatusBarItem setImage:[mMenu->NativeMenuItem() image]];
211 nsStandaloneNativeMenu::SetContainerStatusBarItem(NSStatusItem* aItem)
213 mContainerStatusBarItem = aItem;