Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / profiles / profile_menu_controller.mm
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.
4
5 #import "chrome/browser/ui/cocoa/profiles/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_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 "chrome/grit/generated_resources.h"
23 #include "components/signin/core/common/profile_management_switches.h"
24 #include "ui/base/l10n/l10n_util_mac.h"
25 #include "ui/gfx/image/image.h"
26
27 @interface ProfileMenuController (Private)
28 - (void)initializeMenu;
29 @end
30
31 namespace ProfileMenuControllerInternal {
32
33 class Observer : public chrome::BrowserListObserver,
34                  public AvatarMenuObserver {
35  public:
36   Observer(ProfileMenuController* controller) : controller_(controller) {
37     BrowserList::AddObserver(this);
38   }
39
40   virtual ~Observer() {
41     BrowserList::RemoveObserver(this);
42   }
43
44   // chrome::BrowserListObserver:
45   virtual void OnBrowserAdded(Browser* browser) OVERRIDE {}
46   virtual void OnBrowserRemoved(Browser* browser) OVERRIDE {
47     [controller_ activeBrowserChangedTo:chrome::GetLastActiveBrowser()];
48   }
49   virtual void OnBrowserSetLastActive(Browser* browser) OVERRIDE {
50     [controller_ activeBrowserChangedTo:browser];
51   }
52
53   // AvatarMenuObserver:
54   virtual void OnAvatarMenuChanged(AvatarMenu* menu) OVERRIDE {
55     [controller_ rebuildMenu];
56   }
57
58  private:
59   ProfileMenuController* controller_;  // Weak; owns this.
60 };
61
62 }  // namespace ProfileMenuControllerInternal
63
64 ////////////////////////////////////////////////////////////////////////////////
65
66 @implementation ProfileMenuController
67
68 - (id)initWithMainMenuItem:(NSMenuItem*)item {
69   if ((self = [super init])) {
70     mainMenuItem_ = item;
71
72     base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:
73         l10n_util::GetNSStringWithFixup(IDS_PROFILES_OPTIONS_GROUP_NAME)]);
74     [mainMenuItem_ setSubmenu:menu];
75
76     // This object will be constructed as part of nib loading, which happens
77     // before the message loop starts and g_browser_process is available.
78     // Schedule this on the loop to do work when the browser is ready.
79     [self performSelector:@selector(initializeMenu)
80                withObject:nil
81                afterDelay:0];
82   }
83   return self;
84 }
85
86 - (IBAction)switchToProfileFromMenu:(id)sender {
87   menu_->SwitchToProfile([sender tag], false,
88                          ProfileMetrics::SWITCH_PROFILE_MENU);
89 }
90
91 - (IBAction)switchToProfileFromDock:(id)sender {
92   // Explicitly bring to the foreground when taking action from the dock.
93   [NSApp activateIgnoringOtherApps:YES];
94   menu_->SwitchToProfile([sender tag], false,
95                          ProfileMetrics::SWITCH_PROFILE_DOCK);
96 }
97
98 - (IBAction)editProfile:(id)sender {
99   menu_->EditProfile(menu_->GetActiveProfileIndex());
100 }
101
102 - (IBAction)newProfile:(id)sender {
103   menu_->AddNewProfile(ProfileMetrics::ADD_NEW_USER_MENU);
104 }
105
106 - (BOOL)insertItemsIntoMenu:(NSMenu*)menu
107                    atOffset:(NSInteger)offset
108                    fromDock:(BOOL)dock {
109   if (!menu_ || !menu_->ShouldShowAvatarMenu())
110     return NO;
111
112   if (dock) {
113     NSString* headerName =
114         l10n_util::GetNSStringWithFixup(IDS_PROFILES_OPTIONS_GROUP_NAME);
115     base::scoped_nsobject<NSMenuItem> header(
116         [[NSMenuItem alloc] initWithTitle:headerName
117                                    action:NULL
118                             keyEquivalent:@""]);
119     [header setEnabled:NO];
120     [menu insertItem:header atIndex:offset++];
121   }
122
123   for (size_t i = 0; i < menu_->GetNumberOfItems(); ++i) {
124     const AvatarMenu::Item& itemData = menu_->GetItemAt(i);
125     NSString* name = base::SysUTF16ToNSString(itemData.name);
126     SEL action = dock ? @selector(switchToProfileFromDock:)
127                       : @selector(switchToProfileFromMenu:);
128     NSMenuItem* item = [self createItemWithTitle:name
129                                           action:action];
130     [item setTag:itemData.menu_index];
131     if (dock) {
132       [item setIndentationLevel:1];
133     } else {
134       gfx::Image itemIcon = itemData.icon;
135       // The image might be too large and need to be resized (i.e. if this is
136       // a signed-in user using the GAIA profile photo).
137       if (itemIcon.Width() > profiles::kAvatarIconWidth ||
138           itemIcon.Height() > profiles::kAvatarIconHeight) {
139         itemIcon = profiles::GetAvatarIconForWebUI(itemIcon, true);
140       }
141       DCHECK(itemIcon.Width() <= profiles::kAvatarIconWidth);
142       DCHECK(itemIcon.Height() <= profiles::kAvatarIconHeight);
143       [item setImage:itemIcon.ToNSImage()];
144       [item setState:itemData.active ? NSOnState : NSOffState];
145     }
146     [menu insertItem:item atIndex:i + offset];
147   }
148
149   return YES;
150 }
151
152 - (BOOL)validateMenuItem:(NSMenuItem*)menuItem {
153   // In guest mode, chrome://settings isn't available, so disallow creating
154   // or editing a profile.
155   Profile* activeProfile = ProfileManager::GetLastUsedProfile();
156   if (activeProfile->IsGuestSession()) {
157     return [menuItem action] != @selector(newProfile:) &&
158            [menuItem action] != @selector(editProfile:);
159   }
160
161   const AvatarMenu::Item& itemData = menu_->GetItemAt(
162       menu_->GetActiveProfileIndex());
163   if ([menuItem action] == @selector(switchToProfileFromDock:) ||
164       [menuItem action] == @selector(switchToProfileFromMenu:)) {
165     if (!itemData.supervised)
166       return YES;
167
168     return [menuItem tag] == static_cast<NSInteger>(itemData.menu_index);
169   }
170
171   if ([menuItem action] == @selector(newProfile:))
172     return !itemData.supervised;
173
174   return YES;
175 }
176
177 // Private /////////////////////////////////////////////////////////////////////
178
179 - (NSMenu*)menu {
180   return [mainMenuItem_ submenu];
181 }
182
183 - (void)initializeMenu {
184   observer_.reset(new ProfileMenuControllerInternal::Observer(self));
185   menu_.reset(new AvatarMenu(
186       &g_browser_process->profile_manager()->GetProfileInfoCache(),
187       observer_.get(),
188       NULL));
189   menu_->RebuildMenu();
190
191   [[self menu] addItem:[NSMenuItem separatorItem]];
192
193   NSMenuItem* item = [self createItemWithTitle:
194       l10n_util::GetNSStringWithFixup(IDS_PROFILES_MANAGE_BUTTON_LABEL)
195                                         action:@selector(editProfile:)];
196   [[self menu] addItem:item];
197
198   [[self menu] addItem:[NSMenuItem separatorItem]];
199   item = [self createItemWithTitle:l10n_util::GetNSStringWithFixup(
200       IDS_PROFILES_CREATE_NEW_PROFILE_OPTION)
201                             action:@selector(newProfile:)];
202   [[self menu] addItem:item];
203
204   [self rebuildMenu];
205 }
206
207 // Notifies the controller that the active browser has changed and that the
208 // menu item and menu need to be updated to reflect that.
209 - (void)activeBrowserChangedTo:(Browser*)browser {
210   // Tell the menu that the browser has changed.
211   menu_->ActiveBrowserChanged(browser);
212
213   // If |browser| is NULL, it may be because the current profile was deleted
214   // and there are no other loaded profiles. In this case, calling
215   // |menu_->GetActiveProfileIndex()| may result in a profile being loaded,
216   // which is inappropriate to do on the UI thread.
217   //
218   // An early return provides the desired behavior:
219   //   a) If the profile was deleted, the menu would have been rebuilt and no
220   //      profile will have a check mark.
221   //   b) If the profile was not deleted, but there is no active browser, then
222   //      the previous profile will remain checked.
223   if (!browser)
224     return;
225
226   // In guest mode, there is no active menu item.
227   size_t activeProfileIndex = browser->profile()->IsGuestSession() ?
228       std::string::npos : menu_->GetActiveProfileIndex();
229
230   // Update the state for the menu items.
231   for (size_t i = 0; i < menu_->GetNumberOfItems(); ++i) {
232     size_t tag = menu_->GetItemAt(i).menu_index;
233     [[[self menu] itemWithTag:tag]
234         setState:activeProfileIndex == tag ? NSOnState : NSOffState];
235   }
236 }
237
238 - (void)rebuildMenu {
239   NSMenu* menu = [self menu];
240
241   for (NSMenuItem* item = [menu itemAtIndex:0];
242        ![item isSeparatorItem];
243        item = [menu itemAtIndex:0]) {
244     [menu removeItemAtIndex:0];
245   }
246
247   BOOL hasContent = [self insertItemsIntoMenu:menu atOffset:0 fromDock:NO];
248
249   [mainMenuItem_ setHidden:!hasContent];
250 }
251
252 - (NSMenuItem*)createItemWithTitle:(NSString*)title action:(SEL)sel {
253   base::scoped_nsobject<NSMenuItem> item(
254       [[NSMenuItem alloc] initWithTitle:title action:sel keyEquivalent:@""]);
255   [item setTarget:self];
256   return [item.release() autorelease];
257 }
258
259 @end