| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #import "chrome/browser/ui/cocoa/profile_menu_controller.h" | |
| 6 | |
| 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_info_util.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" | |
| 25 | |
| 26 @interface ProfileMenuController (Private) | |
| 27 - (void)initializeMenu; | |
| 28 @end | |
| 29 | |
| 30 namespace ProfileMenuControllerInternal { | |
| 31 | |
| 32 class Observer : public chrome::BrowserListObserver, | |
| 33 public AvatarMenuObserver { | |
| 34 public: | |
| 35 Observer(ProfileMenuController* controller) : controller_(controller) { | |
| 36 BrowserList::AddObserver(this); | |
| 37 } | |
| 38 | |
| 39 virtual ~Observer() { | |
| 40 BrowserList::RemoveObserver(this); | |
| 41 } | |
| 42 | |
| 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 } | |
| 51 | |
| 52 // AvatarMenuObserver: | |
| 53 virtual void OnAvatarMenuChanged(AvatarMenu* menu) OVERRIDE { | |
| 54 [controller_ rebuildMenu]; | |
| 55 } | |
| 56 | |
| 57 private: | |
| 58 ProfileMenuController* controller_; // Weak; owns this. | |
| 59 }; | |
| 60 | |
| 61 } // namespace ProfileMenuControllerInternal | |
| 62 | |
| 63 //////////////////////////////////////////////////////////////////////////////// | |
| 64 | |
| 65 @implementation ProfileMenuController | |
| 66 | |
| 67 - (id)initWithMainMenuItem:(NSMenuItem*)item { | |
| 68 if ((self = [super init])) { | |
| 69 mainMenuItem_ = item; | |
| 70 | |
| 71 base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle: | |
| 72 l10n_util::GetNSStringWithFixup(IDS_PROFILES_OPTIONS_GROUP_NAME)]); | |
| 73 [mainMenuItem_ setSubmenu:menu]; | |
| 74 | |
| 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; | |
| 83 } | |
| 84 | |
| 85 - (IBAction)switchToProfileFromMenu:(id)sender { | |
| 86 menu_->SwitchToProfile([sender tag], false, | |
| 87 ProfileMetrics::SWITCH_PROFILE_MENU); | |
| 88 } | |
| 89 | |
| 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); | |
| 95 } | |
| 96 | |
| 97 - (IBAction)editProfile:(id)sender { | |
| 98 menu_->EditProfile(menu_->GetActiveProfileIndex()); | |
| 99 } | |
| 100 | |
| 101 - (IBAction)newProfile:(id)sender { | |
| 102 menu_->AddNewProfile(ProfileMetrics::ADD_NEW_USER_MENU); | |
| 103 } | |
| 104 | |
| 105 - (BOOL)insertItemsIntoMenu:(NSMenu*)menu | |
| 106 atOffset:(NSInteger)offset | |
| 107 fromDock:(BOOL)dock { | |
| 108 if (!menu_ || !menu_->ShouldShowAvatarMenu()) | |
| 109 return NO; | |
| 110 | |
| 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 } | |
| 121 | |
| 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 } | |
| 147 | |
| 148 return YES; | |
| 149 } | |
| 150 | |
| 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 } | |
| 159 | |
| 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; | |
| 166 | |
| 167 return [menuItem tag] == static_cast<NSInteger>(itemData.menu_index); | |
| 168 } | |
| 169 | |
| 170 if ([menuItem action] == @selector(newProfile:)) | |
| 171 return !itemData.managed; | |
| 172 | |
| 173 return YES; | |
| 174 } | |
| 175 | |
| 176 // Private ///////////////////////////////////////////////////////////////////// | |
| 177 | |
| 178 - (NSMenu*)menu { | |
| 179 return [mainMenuItem_ submenu]; | |
| 180 } | |
| 181 | |
| 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(); | |
| 189 | |
| 190 [[self menu] addItem:[NSMenuItem separatorItem]]; | |
| 191 | |
| 192 NSMenuItem* item = [self createItemWithTitle: | |
| 193 l10n_util::GetNSStringWithFixup(IDS_PROFILES_CUSTOMIZE_PROFILE) | |
| 194 action:@selector(editProfile:)]; | |
| 195 [[self menu] addItem:item]; | |
| 196 | |
| 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]; | |
| 202 | |
| 203 [self rebuildMenu]; | |
| 204 } | |
| 205 | |
| 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); | |
| 211 | |
| 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; | |
| 224 | |
| 225 // In guest mode, there is no active menu item. | |
| 226 size_t activeProfileIndex = browser->profile()->IsGuestSession() ? | |
| 227 std::string::npos : menu_->GetActiveProfileIndex(); | |
| 228 | |
| 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 } | |
| 235 } | |
| 236 | |
| 237 - (void)rebuildMenu { | |
| 238 NSMenu* menu = [self menu]; | |
| 239 | |
| 240 for (NSMenuItem* item = [menu itemAtIndex:0]; | |
| 241 ![item isSeparatorItem]; | |
| 242 item = [menu itemAtIndex:0]) { | |
| 243 [menu removeItemAtIndex:0]; | |
| 244 } | |
| 245 | |
| 246 BOOL hasContent = [self insertItemsIntoMenu:menu atOffset:0 fromDock:NO]; | |
| 247 | |
| 248 [mainMenuItem_ setHidden:!hasContent]; | |
| 249 } | |
| 250 | |
| 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]; | |
| 256 } | |
| 257 | |
| 258 @end | |
| OLD | NEW |