[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / profiles / profile_menu_controller.mm
blob211ed589e0aa2d2e4922d55b036578999a9c9c2c
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "chrome/browser/ui/cocoa/profiles/profile_menu_controller.h"
7 #include "base/mac/scoped_nsobject.h"
8 #include "base/strings/sys_string_conversions.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/profiles/avatar_menu.h"
11 #include "chrome/browser/profiles/avatar_menu_observer.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
14 #include "chrome/browser/profiles/profile_info_cache.h"
15 #include "chrome/browser/profiles/profile_info_interface.h"
16 #include "chrome/browser/profiles/profile_manager.h"
17 #include "chrome/browser/profiles/profile_metrics.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/browser/ui/browser_list.h"
20 #include "chrome/browser/ui/browser_list_observer.h"
21 #include "chrome/browser/ui/cocoa/last_active_browser_cocoa.h"
22 #include "grit/generated_resources.h"
23 #include "ui/base/l10n/l10n_util_mac.h"
24 #include "ui/gfx/image/image.h"
26 @interface ProfileMenuController (Private)
27 - (void)initializeMenu;
28 @end
30 namespace ProfileMenuControllerInternal {
32 class Observer : public chrome::BrowserListObserver,
33                  public AvatarMenuObserver {
34  public:
35   Observer(ProfileMenuController* controller) : controller_(controller) {
36     BrowserList::AddObserver(this);
37   }
39   virtual ~Observer() {
40     BrowserList::RemoveObserver(this);
41   }
43   // chrome::BrowserListObserver:
44   virtual void OnBrowserAdded(Browser* browser) OVERRIDE {}
45   virtual void OnBrowserRemoved(Browser* browser) OVERRIDE {
46     [controller_ activeBrowserChangedTo:chrome::GetLastActiveBrowser()];
47   }
48   virtual void OnBrowserSetLastActive(Browser* browser) OVERRIDE {
49     [controller_ activeBrowserChangedTo:browser];
50   }
52   // AvatarMenuObserver:
53   virtual void OnAvatarMenuChanged(AvatarMenu* menu) OVERRIDE {
54     [controller_ rebuildMenu];
55   }
57  private:
58   ProfileMenuController* controller_;  // Weak; owns this.
61 }  // namespace ProfileMenuControllerInternal
63 ////////////////////////////////////////////////////////////////////////////////
65 @implementation ProfileMenuController
67 - (id)initWithMainMenuItem:(NSMenuItem*)item {
68   if ((self = [super init])) {
69     mainMenuItem_ = item;
71     base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:
72             l10n_util::GetNSStringWithFixup(IDS_PROFILES_OPTIONS_GROUP_NAME)]);
73     [mainMenuItem_ setSubmenu:menu];
75     // This object will be constructed as part of nib loading, which happens
76     // before the message loop starts and g_browser_process is available.
77     // Schedule this on the loop to do work when the browser is ready.
78     [self performSelector:@selector(initializeMenu)
79                withObject:nil
80                afterDelay:0];
81   }
82   return self;
85 - (IBAction)switchToProfileFromMenu:(id)sender {
86   menu_->SwitchToProfile([sender tag], false,
87                          ProfileMetrics::SWITCH_PROFILE_MENU);
90 - (IBAction)switchToProfileFromDock:(id)sender {
91   // Explicitly bring to the foreground when taking action from the dock.
92   [NSApp activateIgnoringOtherApps:YES];
93   menu_->SwitchToProfile([sender tag], false,
94                          ProfileMetrics::SWITCH_PROFILE_DOCK);
97 - (IBAction)editProfile:(id)sender {
98   menu_->EditProfile(menu_->GetActiveProfileIndex());
101 - (IBAction)newProfile:(id)sender {
102   menu_->AddNewProfile(ProfileMetrics::ADD_NEW_USER_MENU);
105 - (BOOL)insertItemsIntoMenu:(NSMenu*)menu
106                    atOffset:(NSInteger)offset
107                    fromDock:(BOOL)dock {
108   if (!menu_ || !menu_->ShouldShowAvatarMenu())
109     return NO;
111   if (dock) {
112     NSString* headerName =
113         l10n_util::GetNSStringWithFixup(IDS_PROFILES_OPTIONS_GROUP_NAME);
114     base::scoped_nsobject<NSMenuItem> header(
115         [[NSMenuItem alloc] initWithTitle:headerName
116                                    action:NULL
117                             keyEquivalent:@""]);
118     [header setEnabled:NO];
119     [menu insertItem:header atIndex:offset++];
120   }
122   for (size_t i = 0; i < menu_->GetNumberOfItems(); ++i) {
123     const AvatarMenu::Item& itemData = menu_->GetItemAt(i);
124     NSString* name = base::SysUTF16ToNSString(itemData.name);
125     SEL action = dock ? @selector(switchToProfileFromDock:)
126                       : @selector(switchToProfileFromMenu:);
127     NSMenuItem* item = [self createItemWithTitle:name
128                                           action:action];
129     [item setTag:itemData.menu_index];
130     if (dock) {
131       [item setIndentationLevel:1];
132     } else {
133       gfx::Image itemIcon = itemData.icon;
134       // The image might be too large and need to be resized (i.e. if this is
135       // a signed-in user using the GAIA profile photo).
136       if (itemIcon.Width() > profiles::kAvatarIconWidth ||
137           itemIcon.Height() > profiles::kAvatarIconHeight) {
138         itemIcon = profiles::GetAvatarIconForWebUI(itemIcon, true);
139       }
140       DCHECK(itemIcon.Width() <= profiles::kAvatarIconWidth);
141       DCHECK(itemIcon.Height() <= profiles::kAvatarIconHeight);
142       [item setImage:itemIcon.ToNSImage()];
143       [item setState:itemData.active ? NSOnState : NSOffState];
144     }
145     [menu insertItem:item atIndex:i + offset];
146   }
148   return YES;
151 - (BOOL)validateMenuItem:(NSMenuItem*)menuItem {
152   // In guest mode, chrome://settings isn't available, so disallow creating
153   // or editing a profile.
154   Profile* activeProfile = ProfileManager::GetLastUsedProfile();
155   if (activeProfile->IsGuestSession()) {
156     return [menuItem action] != @selector(newProfile:) &&
157            [menuItem action] != @selector(editProfile:);
158   }
160   const AvatarMenu::Item& itemData = menu_->GetItemAt(
161       menu_->GetActiveProfileIndex());
162   if ([menuItem action] == @selector(switchToProfileFromDock:) ||
163       [menuItem action] == @selector(switchToProfileFromMenu:)) {
164     if (!itemData.managed)
165       return YES;
167     return [menuItem tag] == static_cast<NSInteger>(itemData.menu_index);
168   }
170   if ([menuItem action] == @selector(newProfile:))
171     return !itemData.managed;
173   return YES;
176 // Private /////////////////////////////////////////////////////////////////////
178 - (NSMenu*)menu {
179   return [mainMenuItem_ submenu];
182 - (void)initializeMenu {
183   observer_.reset(new ProfileMenuControllerInternal::Observer(self));
184   menu_.reset(new AvatarMenu(
185       &g_browser_process->profile_manager()->GetProfileInfoCache(),
186       observer_.get(),
187       NULL));
188   menu_->RebuildMenu();
190   [[self menu] addItem:[NSMenuItem separatorItem]];
192   NSMenuItem* item = [self createItemWithTitle:
193           l10n_util::GetNSStringWithFixup(IDS_PROFILES_CUSTOMIZE_PROFILE)
194                                         action:@selector(editProfile:)];
195   [[self menu] addItem:item];
197   [[self menu] addItem:[NSMenuItem separatorItem]];
198   item = [self createItemWithTitle:l10n_util::GetNSStringWithFixup(
199                                        IDS_PROFILES_CREATE_NEW_PROFILE_OPTION)
200                             action:@selector(newProfile:)];
201   [[self menu] addItem:item];
203   [self rebuildMenu];
206 // Notifies the controller that the active browser has changed and that the
207 // menu item and menu need to be updated to reflect that.
208 - (void)activeBrowserChangedTo:(Browser*)browser {
209   // Tell the menu that the browser has changed.
210   menu_->ActiveBrowserChanged(browser);
212   // If |browser| is NULL, it may be because the current profile was deleted
213   // and there are no other loaded profiles. In this case, calling
214   // |menu_->GetActiveProfileIndex()| may result in a profile being loaded,
215   // which is inappropriate to do on the UI thread.
216   //
217   // An early return provides the desired behavior:
218   //   a) If the profile was deleted, the menu would have been rebuilt and no
219   //      profile will have a check mark.
220   //   b) If the profile was not deleted, but there is no active browser, then
221   //      the previous profile will remain checked.
222   if (!browser)
223     return;
225   // In guest mode, there is no active menu item.
226   size_t activeProfileIndex = browser->profile()->IsGuestSession() ?
227       std::string::npos : menu_->GetActiveProfileIndex();
229   // Update the state for the menu items.
230   for (size_t i = 0; i < menu_->GetNumberOfItems(); ++i) {
231     size_t tag = menu_->GetItemAt(i).menu_index;
232     [[[self menu] itemWithTag:tag]
233         setState:activeProfileIndex == tag ? NSOnState : NSOffState];
234   }
237 - (void)rebuildMenu {
238   NSMenu* menu = [self menu];
240   for (NSMenuItem* item = [menu itemAtIndex:0];
241        ![item isSeparatorItem];
242        item = [menu itemAtIndex:0]) {
243     [menu removeItemAtIndex:0];
244   }
246   BOOL hasContent = [self insertItemsIntoMenu:menu atOffset:0 fromDock:NO];
248   [mainMenuItem_ setHidden:!hasContent];
251 - (NSMenuItem*)createItemWithTitle:(NSString*)title action:(SEL)sel {
252   base::scoped_nsobject<NSMenuItem> item(
253       [[NSMenuItem alloc] initWithTitle:title action:sel keyEquivalent:@""]);
254   [item setTarget:self];
255   return [item.release() autorelease];
258 @end