avatar_base_controller.mm revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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/avatar_base_controller.h"
6
7#include "chrome/app/chrome_command_ids.h"
8#include "chrome/browser/browser_process.h"
9#include "chrome/browser/profiles/profile_info_cache_observer.h"
10#include "chrome/browser/profiles/profile_avatar_icon_util.h"
11#include "chrome/browser/profiles/profile_manager.h"
12#include "chrome/browser/profiles/profile_metrics.h"
13#include "chrome/browser/ui/browser.h"
14#include "chrome/browser/ui/browser_commands.h"
15#include "chrome/browser/ui/browser_window.h"
16#import "chrome/browser/ui/cocoa/base_bubble_controller.h"
17#import "chrome/browser/ui/cocoa/browser_window_controller.h"
18#import "chrome/browser/ui/cocoa/profiles/avatar_menu_bubble_controller.h"
19#import "chrome/browser/ui/cocoa/profiles/profile_chooser_controller.h"
20#include "chrome/common/profile_management_switches.h"
21#include "ui/base/resource/resource_bundle.h"
22
23// Space between the avatar icon and the avatar menu bubble.
24const CGFloat kMenuYOffsetAdjust = 1.0;
25
26@interface AvatarBaseController (Private)
27// Shows the avatar bubble.
28- (IBAction)buttonClicked:(id)sender;
29- (void)bubbleWillClose:(NSNotification*)notif;
30// Updates the profile name displayed by the avatar button. If |layoutParent| is
31// yes, then the BrowserWindowController is notified to relayout the subviews,
32// as the button needs to be repositioned.
33- (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent;
34@end
35
36class ProfileInfoUpdateObserver : public ProfileInfoCacheObserver {
37 public:
38  ProfileInfoUpdateObserver(AvatarBaseController* avatarButton)
39      : avatarButton_(avatarButton) {
40    g_browser_process->profile_manager()->
41        GetProfileInfoCache().AddObserver(this);
42  }
43
44  virtual ~ProfileInfoUpdateObserver() {
45    g_browser_process->profile_manager()->
46        GetProfileInfoCache().RemoveObserver(this);
47  }
48
49  // ProfileInfoCacheObserver:
50  virtual void OnProfileAdded(const base::FilePath& profile_path) OVERRIDE {
51    [avatarButton_ updateAvatarButtonAndLayoutParent:YES];
52  }
53
54  virtual void OnProfileWasRemoved(
55      const base::FilePath& profile_path,
56      const base::string16& profile_name) OVERRIDE {
57    [avatarButton_ updateAvatarButtonAndLayoutParent:YES];
58  }
59
60  virtual void OnProfileNameChanged(
61      const base::FilePath& profile_path,
62      const base::string16& old_profile_name) OVERRIDE {
63    [avatarButton_ updateAvatarButtonAndLayoutParent:YES];
64  }
65
66  virtual void OnProfileAvatarChanged(
67      const base::FilePath& profile_path) OVERRIDE {
68    [avatarButton_ updateAvatarButtonAndLayoutParent:YES];
69  }
70
71 private:
72  AvatarBaseController* avatarButton_;  // Weak; owns this.
73
74  DISALLOW_COPY_AND_ASSIGN(ProfileInfoUpdateObserver);
75};
76
77@implementation AvatarBaseController
78
79- (id)initWithBrowser:(Browser*)browser {
80  if ((self = [super init])) {
81    browser_ = browser;
82    profileInfoObserver_.reset(new ProfileInfoUpdateObserver(self));
83  }
84  return self;
85}
86
87- (void)dealloc {
88  [[NSNotificationCenter defaultCenter]
89      removeObserver:self
90                name:NSWindowWillCloseNotification
91              object:[menuController_ window]];
92  [super dealloc];
93}
94
95- (NSButton*)buttonView {
96  CHECK(button_.get());  // Subclasses must set this.
97  return button_.get();
98}
99
100- (void)showAvatarBubble:(NSView*)anchor
101                withMode:(BrowserWindow::AvatarBubbleMode)mode {
102  if (menuController_)
103    return;
104
105  DCHECK(chrome::IsCommandEnabled(browser_, IDC_SHOW_AVATAR_MENU));
106
107  NSWindowController* wc =
108      [browser_->window()->GetNativeWindow() windowController];
109  if ([wc isKindOfClass:[BrowserWindowController class]]) {
110    [static_cast<BrowserWindowController*>(wc)
111        lockBarVisibilityForOwner:self withAnimation:NO delay:NO];
112  }
113
114  // The new avatar bubble does not have an arrow, and it should be anchored
115  // to the edge of the avatar button.
116  int anchorX = switches::IsNewProfileManagement() ? NSMaxX([anchor bounds]) :
117                                                     NSMidX([anchor bounds]);
118  NSPoint point = NSMakePoint(anchorX,
119                              NSMaxY([anchor bounds]) - kMenuYOffsetAdjust);
120  point = [anchor convertPoint:point toView:nil];
121  point = [[anchor window] convertBaseToScreen:point];
122
123  // |menuController_| will automatically release itself on close.
124  if (switches::IsNewProfileManagement()) {
125    BubbleViewMode viewMode =
126        mode == BrowserWindow::AVATAR_BUBBLE_MODE_DEFAULT ?
127        BUBBLE_VIEW_MODE_PROFILE_CHOOSER :
128        BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT;
129    menuController_ =
130        [[ProfileChooserController alloc] initWithBrowser:browser_
131                                               anchoredAt:point
132                                                 withMode:viewMode];
133  } else {
134    menuController_ =
135      [[AvatarMenuBubbleController alloc] initWithBrowser:browser_
136                                               anchoredAt:point];
137  }
138
139  [[NSNotificationCenter defaultCenter]
140      addObserver:self
141         selector:@selector(bubbleWillClose:)
142             name:NSWindowWillCloseNotification
143           object:[menuController_ window]];
144  [menuController_ showWindow:self];
145
146  ProfileMetrics::LogProfileOpenMethod(ProfileMetrics::ICON_AVATAR_BUBBLE);
147}
148
149- (IBAction)buttonClicked:(id)sender {
150  DCHECK_EQ(sender, button_.get());
151  [self showAvatarBubble:button_
152                withMode:BrowserWindow::AVATAR_BUBBLE_MODE_DEFAULT];
153}
154
155- (void)bubbleWillClose:(NSNotification*)notif {
156  NSWindowController* wc =
157      [browser_->window()->GetNativeWindow() windowController];
158  if ([wc isKindOfClass:[BrowserWindowController class]]) {
159    [static_cast<BrowserWindowController*>(wc)
160        releaseBarVisibilityForOwner:self withAnimation:YES delay:NO];
161  }
162  menuController_ = nil;
163}
164
165- (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent {
166  NOTREACHED();
167}
168
169- (BaseBubbleController*)menuController {
170  return menuController_;
171}
172
173@end
174