Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / profiles / avatar_base_controller.mm
blobe634fdfb3d61b6e4d30a228b54f7cb95c9591e88
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/avatar_base_controller.h"
7 #include "base/mac/foundation_util.h"
8 #include "chrome/app/chrome_command_ids.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
11 #include "chrome/browser/profiles/profile_info_cache_observer.h"
12 #include "chrome/browser/profiles/profile_manager.h"
13 #include "chrome/browser/profiles/profile_metrics.h"
14 #include "chrome/browser/profiles/profile_window.h"
15 #include "chrome/browser/profiles/profiles_state.h"
16 #include "chrome/browser/signin/chrome_signin_helper.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/browser_commands.h"
19 #include "chrome/browser/ui/browser_window.h"
20 #import "chrome/browser/ui/cocoa/base_bubble_controller.h"
21 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
22 #import "chrome/browser/ui/cocoa/profiles/avatar_menu_bubble_controller.h"
23 #import "chrome/browser/ui/cocoa/profiles/profile_chooser_controller.h"
24 #include "components/signin/core/browser/signin_error_controller.h"
25 #include "components/signin/core/common/profile_management_switches.h"
27 // Space between the avatar icon and the avatar menu bubble.
28 const CGFloat kMenuYOffsetAdjust = 1.0;
29 // Offset needed to align the edge of the avatar bubble with the edge of the
30 // avatar button.
31 const CGFloat kMenuXOffsetAdjust = 2.0;
33 @interface AvatarBaseController (Private)
34 // Shows the avatar bubble.
35 - (IBAction)buttonClicked:(id)sender;
36 - (IBAction)buttonRightClicked:(id)sender;
38 - (void)bubbleWillClose:(NSNotification*)notif;
40 // Updates the profile name displayed by the avatar button. If |layoutParent| is
41 // yes, then the BrowserWindowController is notified to relayout the subviews,
42 // as the button needs to be repositioned.
43 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent;
45 // Displays an error icon if any accounts associated with this profile have an
46 // auth error.
47 - (void)updateErrorStatus:(BOOL)hasError;
48 @end
50 class ProfileInfoUpdateObserver : public ProfileInfoCacheObserver,
51                                   public SigninErrorController::Observer {
52  public:
53   ProfileInfoUpdateObserver(Profile* profile,
54                             AvatarBaseController* avatarController)
55       : profile_(profile),
56         avatarController_(avatarController) {
57     g_browser_process->profile_manager()->
58         GetProfileInfoCache().AddObserver(this);
60     // Subscribe to authentication error changes so that the avatar button
61     // can update itself.
62     SigninErrorController* errorController =
63         profiles::GetSigninErrorController(profile_);
64     if (errorController)
65       errorController->AddObserver(this);
66   }
68   ~ProfileInfoUpdateObserver() override {
69     g_browser_process->profile_manager()->
70         GetProfileInfoCache().RemoveObserver(this);
71     SigninErrorController* errorController =
72         profiles::GetSigninErrorController(profile_);
73     if (errorController)
74       errorController->RemoveObserver(this);
75   }
77   // ProfileInfoCacheObserver:
78   void OnProfileAdded(const base::FilePath& profile_path) override {
79     [avatarController_ updateAvatarButtonAndLayoutParent:YES];
80   }
82   void OnProfileWasRemoved(const base::FilePath& profile_path,
83                            const base::string16& profile_name) override {
84     // If deleting the active profile, don't bother updating the avatar
85     // button, as the browser window is being closed anyway.
86     if (profile_->GetPath() != profile_path)
87       [avatarController_ updateAvatarButtonAndLayoutParent:YES];
88   }
90   void OnProfileNameChanged(const base::FilePath& profile_path,
91                             const base::string16& old_profile_name) override {
92     if (profile_->GetPath() == profile_path)
93       [avatarController_ updateAvatarButtonAndLayoutParent:YES];
94   }
96   void OnProfileAvatarChanged(const base::FilePath& profile_path) override {
97     if (!switches::IsNewAvatarMenu() && profile_->GetPath() == profile_path)
98       [avatarController_ updateAvatarButtonAndLayoutParent:YES];
99   }
101   void OnProfileSupervisedUserIdChanged(
102       const base::FilePath& profile_path) override {
103     if (profile_->GetPath() == profile_path)
104       [avatarController_ updateAvatarButtonAndLayoutParent:YES];
105   }
107   // SigninErrorController::Observer:
108   void OnErrorChanged() override {
109     SigninErrorController* errorController =
110         profiles::GetSigninErrorController(profile_);
111     if (errorController)
112       [avatarController_ updateErrorStatus:errorController->HasError()];
113   }
115  private:
116   Profile* profile_;
117   AvatarBaseController* avatarController_;  // Weak; owns this.
119   DISALLOW_COPY_AND_ASSIGN(ProfileInfoUpdateObserver);
122 @implementation AvatarBaseController
124 - (id)initWithBrowser:(Browser*)browser {
125   if ((self = [super init])) {
126     browser_ = browser;
127     profileInfoObserver_.reset(
128         new ProfileInfoUpdateObserver(browser_->profile(), self));
129   }
130   return self;
133 - (void)dealloc {
134   [self browserWillBeDestroyed];
135   [super dealloc];
138 - (void)browserWillBeDestroyed {
139   [[NSNotificationCenter defaultCenter]
140       removeObserver:self
141                 name:NSWindowWillCloseNotification
142               object:[menuController_ window]];
143   browser_ = nullptr;
146 - (NSButton*)buttonView {
147   CHECK(button_.get());  // Subclasses must set this.
148   return button_.get();
151 - (void)showAvatarBubbleAnchoredAt:(NSView*)anchor
152                           withMode:(BrowserWindow::AvatarBubbleMode)mode
153                    withServiceType:(signin::GAIAServiceType)serviceType {
154   if (menuController_) {
155     if (switches::IsNewAvatarMenu()) {
156       profiles::BubbleViewMode viewMode;
157       profiles::TutorialMode tutorialMode;
158       profiles::BubbleViewModeFromAvatarBubbleMode(
159           mode, &viewMode, &tutorialMode);
160       if (tutorialMode != profiles::TUTORIAL_MODE_NONE) {
161         ProfileChooserController* profileChooserController =
162             base::mac::ObjCCastStrict<ProfileChooserController>(
163                 menuController_);
164         [profileChooserController setTutorialMode:tutorialMode];
165         [profileChooserController initMenuContentsWithView:viewMode];
166       }
167     }
168     return;
169   }
171   DCHECK(chrome::IsCommandEnabled(browser_, IDC_SHOW_AVATAR_MENU));
173   NSWindowController* wc =
174       [browser_->window()->GetNativeWindow() windowController];
175   if ([wc isKindOfClass:[BrowserWindowController class]]) {
176     [static_cast<BrowserWindowController*>(wc)
177         lockBarVisibilityForOwner:self withAnimation:NO delay:NO];
178   }
180   // The new avatar bubble does not have an arrow, and it should be anchored
181   // to the edge of the avatar button.
182   int anchorX = switches::IsNewAvatarMenu() ?
183       NSMaxX([anchor bounds]) - kMenuXOffsetAdjust :
184       NSMidX([anchor bounds]);
185   NSPoint point = NSMakePoint(anchorX,
186                               NSMaxY([anchor bounds]) + kMenuYOffsetAdjust);
187   point = [anchor convertPoint:point toView:nil];
188   point = [[anchor window] convertBaseToScreen:point];
190   // |menuController_| will automatically release itself on close.
191   if (switches::IsNewAvatarMenu()) {
192     profiles::BubbleViewMode viewMode;
193     profiles::TutorialMode tutorialMode;
194     profiles::BubbleViewModeFromAvatarBubbleMode(
195         mode, &viewMode, &tutorialMode);
196     // Don't start creating the view if it would be an empty fast user switcher.
197     // It has to happen here to prevent the view system from creating an empty
198     // container.
199     if (viewMode == profiles::BUBBLE_VIEW_MODE_FAST_PROFILE_CHOOSER &&
200         !profiles::HasProfileSwitchTargets(browser_->profile())) {
201       return;
202     }
204     menuController_ =
205         [[ProfileChooserController alloc] initWithBrowser:browser_
206                                                anchoredAt:point
207                                                  viewMode:viewMode
208                                              tutorialMode:tutorialMode
209                                               serviceType:serviceType];
210   } else {
211     menuController_ =
212       [[AvatarMenuBubbleController alloc] initWithBrowser:browser_
213                                                anchoredAt:point];
214   }
216   [[NSNotificationCenter defaultCenter]
217       addObserver:self
218          selector:@selector(bubbleWillClose:)
219              name:NSWindowWillCloseNotification
220            object:[menuController_ window]];
221   [menuController_ showWindow:self];
223   ProfileMetrics::LogProfileOpenMethod(ProfileMetrics::ICON_AVATAR_BUBBLE);
226 - (BOOL)isCtrlPressed {
227   return [NSEvent modifierFlags] & NSControlKeyMask ? YES : NO;
230 - (IBAction)buttonClicked:(id)sender {
231   BrowserWindow::AvatarBubbleMode mode =
232       BrowserWindow::AVATAR_BUBBLE_MODE_DEFAULT;
233   if ([self isCtrlPressed])
234     mode = BrowserWindow::AVATAR_BUBBLE_MODE_FAST_USER_SWITCH;
236   [self showAvatarBubbleAnchoredAt:button_
237                           withMode:mode
238                    withServiceType:signin::GAIA_SERVICE_TYPE_NONE];
241 - (IBAction)buttonRightClicked:(id)sender {
242   BrowserWindow::AvatarBubbleMode mode =
243       BrowserWindow::AVATAR_BUBBLE_MODE_FAST_USER_SWITCH;
245   [self showAvatarBubbleAnchoredAt:button_
246                           withMode:mode
247                    withServiceType:signin::GAIA_SERVICE_TYPE_NONE];
250 - (void)bubbleWillClose:(NSNotification*)notif {
251   NSWindowController* wc =
252       [browser_->window()->GetNativeWindow() windowController];
253   if ([wc isKindOfClass:[BrowserWindowController class]]) {
254     [static_cast<BrowserWindowController*>(wc)
255         releaseBarVisibilityForOwner:self withAnimation:YES delay:NO];
256   }
257   menuController_ = nil;
260 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent {
261   NOTREACHED();
264 - (void)updateErrorStatus:(BOOL)hasError {
267 - (BaseBubbleController*)menuController {
268   return menuController_;
271 @end