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_info_cache_observer.h"
11 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
12 #include "chrome/browser/profiles/profile_manager.h"
13 #include "chrome/browser/profiles/profile_metrics.h"
14 #include "chrome/browser/signin/signin_header_helper.h"
15 #include "chrome/browser/profiles/profiles_state.h"
16 #include "chrome/browser/profiles/profile_window.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
31 const CGFloat kMenuXOffsetAdjust = 2.0;
33 @interface AvatarBaseController (Private)
34 // Shows the avatar bubble.
35 - (IBAction)buttonClicked:(id)sender;
37 - (void)bubbleWillClose:(NSNotification*)notif;
39 // Updates the profile name displayed by the avatar button. If |layoutParent| is
40 // yes, then the BrowserWindowController is notified to relayout the subviews,
41 // as the button needs to be repositioned.
42 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent;
44 // Displays an error icon if any accounts associated with this profile have an
46 - (void)updateErrorStatus:(BOOL)hasError;
49 class ProfileInfoUpdateObserver : public ProfileInfoCacheObserver,
50 public SigninErrorController::Observer {
52 ProfileInfoUpdateObserver(Profile* profile,
53 AvatarBaseController* avatarController)
55 avatarController_(avatarController) {
56 g_browser_process->profile_manager()->
57 GetProfileInfoCache().AddObserver(this);
59 // Subscribe to authentication error changes so that the avatar button
61 SigninErrorController* errorController =
62 profiles::GetSigninErrorController(profile_);
64 errorController->AddObserver(this);
67 virtual ~ProfileInfoUpdateObserver() {
68 g_browser_process->profile_manager()->
69 GetProfileInfoCache().RemoveObserver(this);
70 SigninErrorController* errorController =
71 profiles::GetSigninErrorController(profile_);
73 errorController->RemoveObserver(this);
76 // ProfileInfoCacheObserver:
77 virtual void OnProfileAdded(const base::FilePath& profile_path) OVERRIDE {
78 [avatarController_ updateAvatarButtonAndLayoutParent:YES];
81 virtual void OnProfileWasRemoved(
82 const base::FilePath& profile_path,
83 const base::string16& profile_name) OVERRIDE {
84 [avatarController_ updateAvatarButtonAndLayoutParent:YES];
87 virtual void OnProfileNameChanged(
88 const base::FilePath& profile_path,
89 const base::string16& old_profile_name) OVERRIDE {
90 [avatarController_ updateAvatarButtonAndLayoutParent:YES];
93 virtual void OnProfileAvatarChanged(
94 const base::FilePath& profile_path) OVERRIDE {
95 [avatarController_ updateAvatarButtonAndLayoutParent:YES];
98 virtual void OnProfileSupervisedUserIdChanged(
99 const base::FilePath& profile_path) OVERRIDE {
100 [avatarController_ updateAvatarButtonAndLayoutParent:YES];
103 // SigninErrorController::Observer:
104 virtual void OnErrorChanged() OVERRIDE {
105 SigninErrorController* errorController =
106 profiles::GetSigninErrorController(profile_);
108 [avatarController_ updateErrorStatus:errorController->HasError()];
113 AvatarBaseController* avatarController_; // Weak; owns this.
115 DISALLOW_COPY_AND_ASSIGN(ProfileInfoUpdateObserver);
118 @implementation AvatarBaseController
120 - (id)initWithBrowser:(Browser*)browser {
121 if ((self = [super init])) {
123 profileInfoObserver_.reset(
124 new ProfileInfoUpdateObserver(browser_->profile(), self));
130 [[NSNotificationCenter defaultCenter]
132 name:NSWindowWillCloseNotification
133 object:[menuController_ window]];
137 - (NSButton*)buttonView {
138 CHECK(button_.get()); // Subclasses must set this.
139 return button_.get();
142 - (void)showAvatarBubble:(NSView*)anchor
143 withMode:(BrowserWindow::AvatarBubbleMode)mode
144 withServiceType:(signin::GAIAServiceType)serviceType {
145 if (menuController_) {
146 if (switches::IsNewAvatarMenu()) {
147 profiles::BubbleViewMode viewMode;
148 profiles::TutorialMode tutorialMode;
149 profiles::BubbleViewModeFromAvatarBubbleMode(
150 mode, &viewMode, &tutorialMode);
151 if (tutorialMode != profiles::TUTORIAL_MODE_NONE) {
152 ProfileChooserController* profileChooserController =
153 base::mac::ObjCCastStrict<ProfileChooserController>(
155 [profileChooserController setTutorialMode:tutorialMode];
156 [profileChooserController initMenuContentsWithView:viewMode];
162 DCHECK(chrome::IsCommandEnabled(browser_, IDC_SHOW_AVATAR_MENU));
164 NSWindowController* wc =
165 [browser_->window()->GetNativeWindow() windowController];
166 if ([wc isKindOfClass:[BrowserWindowController class]]) {
167 [static_cast<BrowserWindowController*>(wc)
168 lockBarVisibilityForOwner:self withAnimation:NO delay:NO];
171 // The new avatar bubble does not have an arrow, and it should be anchored
172 // to the edge of the avatar button.
173 int anchorX = switches::IsNewAvatarMenu() ?
174 NSMaxX([anchor bounds]) - kMenuXOffsetAdjust :
175 NSMidX([anchor bounds]);
176 NSPoint point = NSMakePoint(anchorX,
177 NSMaxY([anchor bounds]) - kMenuYOffsetAdjust);
178 point = [anchor convertPoint:point toView:nil];
179 point = [[anchor window] convertBaseToScreen:point];
181 // |menuController_| will automatically release itself on close.
182 if (switches::IsNewAvatarMenu()) {
183 profiles::BubbleViewMode viewMode;
184 profiles::TutorialMode tutorialMode;
185 profiles::BubbleViewModeFromAvatarBubbleMode(
186 mode, &viewMode, &tutorialMode);
188 [[ProfileChooserController alloc] initWithBrowser:browser_
191 tutorialMode:tutorialMode
192 serviceType:serviceType];
195 [[AvatarMenuBubbleController alloc] initWithBrowser:browser_
199 [[NSNotificationCenter defaultCenter]
201 selector:@selector(bubbleWillClose:)
202 name:NSWindowWillCloseNotification
203 object:[menuController_ window]];
204 [menuController_ showWindow:self];
206 ProfileMetrics::LogProfileOpenMethod(ProfileMetrics::ICON_AVATAR_BUBBLE);
209 - (IBAction)buttonClicked:(id)sender {
210 DCHECK_EQ(sender, button_.get());
211 [self showAvatarBubble:button_
212 withMode:BrowserWindow::AVATAR_BUBBLE_MODE_DEFAULT
213 withServiceType:signin::GAIA_SERVICE_TYPE_NONE];
216 - (void)bubbleWillClose:(NSNotification*)notif {
217 NSWindowController* wc =
218 [browser_->window()->GetNativeWindow() windowController];
219 if ([wc isKindOfClass:[BrowserWindowController class]]) {
220 [static_cast<BrowserWindowController*>(wc)
221 releaseBarVisibilityForOwner:self withAnimation:YES delay:NO];
223 menuController_ = nil;
226 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent {
230 - (void)updateErrorStatus:(BOOL)hasError {
233 - (BaseBubbleController*)menuController {
234 return menuController_;