Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / widget / cocoa / nsStandaloneNativeMenu.mm
blob490c9eec72a397fba03a24866ee13d822d842dba
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()
20 : mMenu(nullptr)
21 , mContainerStatusBarItem(nil)
25 nsStandaloneNativeMenu::~nsStandaloneNativeMenu()
27   if (mMenu) delete mMenu;
30 NS_IMETHODIMP
31 nsStandaloneNativeMenu::Init(nsIDOMElement * aDOMElement)
33   NS_ASSERTION(mMenu == nullptr, "nsNativeMenu::Init - mMenu not null!");
35   nsresult rv;
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);
46   if (NS_FAILED(rv))
47     return rv;
49   mMenu = new nsMenuX();
50   rv = mMenu->Create(this, this, content);
51   if (NS_FAILED(rv)) {
52     delete mMenu;
53     mMenu = nullptr;
54     return rv;
55   }
57   mMenu->SetupIcon();
59   return NS_OK;
62 static void
63 UpdateMenu(nsMenuX * aMenu)
65   aMenu->MenuOpened();
66   aMenu->MenuClosed();
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));
73     }
74   }
77 NS_IMETHODIMP
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
83   // its submenus.
84   UpdateMenu(mMenu);
86   *aResult = true;
87   return NS_OK;
90 NS_IMETHODIMP
91 nsStandaloneNativeMenu::GetNativeMenu(void ** aVoidPointer)
93   if (mMenu) {
94     *aVoidPointer = mMenu->NativeData();
95     [[(NSObject *)(*aVoidPointer) retain] autorelease];
96     return NS_OK;
97   }  else {
98     *aVoidPointer = nullptr;
99     return NS_ERROR_NOT_INITIALIZED;
100   }
103 static NSMenuItem *
104 NativeMenuItemWithLocation(NSMenu * currentSubmenu, NSString * locationString)
106   NSArray * indexes = [locationString componentsSeparatedByString:@"|"];
107   NSUInteger indexCount = [indexes count];
108   if (indexCount == 0)
109     return nil;
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))
119         return menuItem;
121       // If this is not the last index, find the submenu and keep going.
122       if ([menuItem hasSubmenu])
123         currentSubmenu = [menuItem submenu];
124       else
125         return nil;
126     }
127   }
129   return nil;
132 NS_IMETHODIMP
133 nsStandaloneNativeMenu::ActivateNativeMenuItemAt(const nsAString& indexString)
135   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
136   
137   if (!mMenu)
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];
149     if (parent) {
150       // NSLog(@"Performing action for native menu item titled: %@\n",
151       //       [[currentSubmenu itemAtIndex:targetIndex] title]);
152       [parent performActionForItemAtIndex:[parent indexOfItem:item]];
153       return NS_OK;
154     }
155   }
157   return NS_ERROR_FAILURE;
158   
159   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
162 NS_IMETHODIMP
163 nsStandaloneNativeMenu::ForceUpdateNativeMenuAt(const nsAString& indexString)
165   if (!mMenu)
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];
172   if (indexCount == 0)
173     return NS_OK;
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];
180     int visible = 0;
181     uint32_t length = currentMenu->GetItemCount();
182     for (unsigned int j = 0; j < length; j++) {
183       nsMenuObjectX* targetMenu = currentMenu->GetItemAt(j);
184       if (!targetMenu)
185         return NS_OK;
186       if (!nsMenuUtilsX::NodeIsHiddenOrCollapsed(targetMenu->Content())) {
187         visible++;
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();
193           break;
194         }
195       }
196     }
197   }
199   return NS_OK;
202 void
203 nsStandaloneNativeMenu::IconUpdated()
205   if (mContainerStatusBarItem) {
206     [mContainerStatusBarItem setImage:[mMenu->NativeMenuItem() image]];
207   }
210 void
211 nsStandaloneNativeMenu::SetContainerStatusBarItem(NSStatusItem* aItem)
213   mContainerStatusBarItem = aItem;
214   IconUpdated();