Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / profile_menu_controller.mm
blob50a40db8f6f2816d2dcfb692572741579dd26f79
1 // Copyright (c) 2012 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/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_info_cache.h"
14 #include "chrome/browser/profiles/profile_info_interface.h"
15 #include "chrome/browser/profiles/profile_manager.h"
16 #include "chrome/browser/profiles/profile_metrics.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/browser_list.h"
19 #include "chrome/browser/ui/browser_list_observer.h"
20 #include "chrome/browser/ui/cocoa/last_active_browser_cocoa.h"
21 #include "grit/generated_resources.h"
22 #include "ui/base/l10n/l10n_util_mac.h"
23 #include "ui/gfx/image/image.h"
25 @interface ProfileMenuController (Private)
26 - (void)initializeMenu;
27 @end
29 namespace ProfileMenuControllerInternal {
31 class Observer : public chrome::BrowserListObserver,
32                  public AvatarMenuObserver {
33  public:
34   Observer(ProfileMenuController* controller) : controller_(controller) {
35     BrowserList::AddObserver(this);
36   }
38   virtual ~Observer() {
39     BrowserList::RemoveObserver(this);
40   }
42   // chrome::BrowserListObserver:
43   virtual void OnBrowserAdded(Browser* browser) OVERRIDE {}
44   virtual void OnBrowserRemoved(Browser* browser) OVERRIDE {
45     [controller_ activeBrowserChangedTo:chrome::GetLastActiveBrowser()];
46   }
47   virtual void OnBrowserSetLastActive(Browser* browser) OVERRIDE {
48     [controller_ activeBrowserChangedTo:browser];
49   }
51   // AvatarMenuObserver:
52   virtual void OnAvatarMenuChanged(AvatarMenu* menu) OVERRIDE {
53     [controller_ rebuildMenu];
54   }
56  private:
57   ProfileMenuController* controller_;  // Weak; owns this.
60 }  // namespace ProfileMenuControllerInternal
62 ////////////////////////////////////////////////////////////////////////////////
64 @implementation ProfileMenuController
66 - (id)initWithMainMenuItem:(NSMenuItem*)item {
67   if ((self = [super init])) {
68     mainMenuItem_ = item;
70     base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:
71             l10n_util::GetNSStringWithFixup(IDS_PROFILES_OPTIONS_GROUP_NAME)]);
72     [mainMenuItem_ setSubmenu:menu];
74     // This object will be constructed as part of nib loading, which happens
75     // before the message loop starts and g_browser_process is available.
76     // Schedule this on the loop to do work when the browser is ready.
77     [self performSelector:@selector(initializeMenu)
78                withObject:nil
79                afterDelay:0];
80   }
81   return self;
84 - (IBAction)switchToProfileFromMenu:(id)sender {
85   menu_->SwitchToProfile([sender tag], false);
86   ProfileMetrics::LogProfileSwitchUser(ProfileMetrics::SWITCH_PROFILE_MENU);
89 - (IBAction)switchToProfileFromDock:(id)sender {
90   // Explicitly bring to the foreground when taking action from the dock.
91   [NSApp activateIgnoringOtherApps:YES];
92   menu_->SwitchToProfile([sender tag], false);
93   ProfileMetrics::LogProfileSwitchUser(ProfileMetrics::SWITCH_PROFILE_DOCK);
96 - (IBAction)editProfile:(id)sender {
97   menu_->EditProfile(menu_->GetActiveProfileIndex());
100 - (IBAction)newProfile:(id)sender {
101   menu_->AddNewProfile(ProfileMetrics::ADD_NEW_USER_MENU);
104 - (BOOL)insertItemsIntoMenu:(NSMenu*)menu
105                    atOffset:(NSInteger)offset
106                    fromDock:(BOOL)dock {
107   if (!menu_ || !menu_->ShouldShowAvatarMenu())
108     return NO;
110   if (dock) {
111     NSString* headerName =
112         l10n_util::GetNSStringWithFixup(IDS_PROFILES_OPTIONS_GROUP_NAME);
113     base::scoped_nsobject<NSMenuItem> header(
114         [[NSMenuItem alloc] initWithTitle:headerName
115                                    action:NULL
116                             keyEquivalent:@""]);
117     [header setEnabled:NO];
118     [menu insertItem:header atIndex:offset++];
119   }
121   for (size_t i = 0; i < menu_->GetNumberOfItems(); ++i) {
122     const AvatarMenu::Item& itemData = menu_->GetItemAt(i);
123     NSString* name = base::SysUTF16ToNSString(itemData.name);
124     SEL action = dock ? @selector(switchToProfileFromDock:)
125                       : @selector(switchToProfileFromMenu:);
126     NSMenuItem* item = [self createItemWithTitle:name
127                                           action:action];
128     [item setTag:itemData.menu_index];
129     if (dock) {
130       [item setIndentationLevel:1];
131     } else {
132       [item setImage:itemData.icon.ToNSImage()];
133       [item setState:itemData.active ? NSOnState : NSOffState];
134     }
135     [menu insertItem:item atIndex:i + offset];
136   }
138   return YES;
141 - (BOOL)validateMenuItem:(NSMenuItem*)menuItem {
142   // In guest mode, chrome://settings isn't available, so disallow creating
143   // or editing a profile.
144   Profile* activeProfile = ProfileManager::GetLastUsedProfile();
145   if (activeProfile->IsGuestSession()) {
146     return [menuItem action] != @selector(newProfile:) &&
147            [menuItem action] != @selector(editProfile:);
148   }
150   const AvatarMenu::Item& itemData = menu_->GetItemAt(
151       menu_->GetActiveProfileIndex());
152   if ([menuItem action] == @selector(switchToProfileFromDock:) ||
153       [menuItem action] == @selector(switchToProfileFromMenu:)) {
154     if (!itemData.managed)
155       return YES;
157     return [menuItem tag] == static_cast<NSInteger>(itemData.menu_index);
158   }
160   if ([menuItem action] == @selector(newProfile:))
161     return !itemData.managed;
163   return YES;
166 // Private /////////////////////////////////////////////////////////////////////
168 - (NSMenu*)menu {
169   return [mainMenuItem_ submenu];
172 - (void)initializeMenu {
173   observer_.reset(new ProfileMenuControllerInternal::Observer(self));
174   menu_.reset(new AvatarMenu(
175       &g_browser_process->profile_manager()->GetProfileInfoCache(),
176       observer_.get(),
177       NULL));
178   menu_->RebuildMenu();
180   [[self menu] addItem:[NSMenuItem separatorItem]];
182   NSMenuItem* item = [self createItemWithTitle:
183           l10n_util::GetNSStringWithFixup(IDS_PROFILES_CUSTOMIZE_PROFILE)
184                                         action:@selector(editProfile:)];
185   [[self menu] addItem:item];
187   [[self menu] addItem:[NSMenuItem separatorItem]];
188   item = [self createItemWithTitle:l10n_util::GetNSStringWithFixup(
189                                        IDS_PROFILES_CREATE_NEW_PROFILE_OPTION)
190                             action:@selector(newProfile:)];
191   [[self menu] addItem:item];
193   [self rebuildMenu];
196 // Notifies the controller that the active browser has changed and that the
197 // menu item and menu need to be updated to reflect that.
198 - (void)activeBrowserChangedTo:(Browser*)browser {
199   // Tell the menu that the browser has changed.
200   menu_->ActiveBrowserChanged(browser);
202   // If |browser| is NULL, it may be because the current profile was deleted
203   // and there are no other loaded profiles. In this case, calling
204   // |menu_->GetActiveProfileIndex()| may result in a profile being loaded,
205   // which is inappropriate to do on the UI thread.
206   //
207   // An early return provides the desired behavior:
208   //   a) If the profile was deleted, the menu would have been rebuilt and no
209   //      profile will have a check mark.
210   //   b) If the profile was not deleted, but there is no active browser, then
211   //      the previous profile will remain checked.
212   if (!browser)
213     return;
215   // In guest mode, there is no active menu item.
216   size_t activeProfileIndex = browser->profile()->IsGuestSession() ?
217       std::string::npos : menu_->GetActiveProfileIndex();
219   // Update the state for the menu items.
220   for (size_t i = 0; i < menu_->GetNumberOfItems(); ++i) {
221     size_t tag = menu_->GetItemAt(i).menu_index;
222     [[[self menu] itemWithTag:tag]
223         setState:activeProfileIndex == tag ? NSOnState : NSOffState];
224   }
227 - (void)rebuildMenu {
228   NSMenu* menu = [self menu];
230   for (NSMenuItem* item = [menu itemAtIndex:0];
231        ![item isSeparatorItem];
232        item = [menu itemAtIndex:0]) {
233     [menu removeItemAtIndex:0];
234   }
236   BOOL hasContent = [self insertItemsIntoMenu:menu atOffset:0 fromDock:NO];
238   [mainMenuItem_ setHidden:!hasContent];
241 - (NSMenuItem*)createItemWithTitle:(NSString*)title action:(SEL)sel {
242   base::scoped_nsobject<NSMenuItem> item(
243       [[NSMenuItem alloc] initWithTitle:title action:sel keyEquivalent:@""]);
244   [item setTarget:self];
245   return [item.release() autorelease];
248 @end