OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "chrome/browser/ui/cocoa/browser/avatar_button_controller.h" | 5 #import "chrome/browser/ui/cocoa/browser/avatar_button_controller.h" |
6 | 6 |
7 #include "base/strings/sys_string_conversions.h" | 7 #include "base/strings/sys_string_conversions.h" |
8 #include "chrome/app/chrome_command_ids.h" | 8 #include "chrome/app/chrome_command_ids.h" |
9 #include "chrome/browser/browser_process.h" | 9 #include "chrome/browser/browser_process.h" |
10 #include "chrome/browser/chrome_notification_types.h" | 10 #include "chrome/browser/profiles/profile_info_cache_observer.h" |
11 #include "chrome/browser/command_updater.h" | |
12 #include "chrome/browser/profiles/profile.h" | |
13 #include "chrome/browser/profiles/profile_info_cache.h" | |
14 #include "chrome/browser/profiles/profile_info_util.h" | |
15 #include "chrome/browser/profiles/profile_manager.h" | 11 #include "chrome/browser/profiles/profile_manager.h" |
16 #include "chrome/browser/profiles/profile_metrics.h" | 12 #include "chrome/browser/profiles/profile_metrics.h" |
17 #include "chrome/browser/profiles/profiles_state.h" | 13 #include "chrome/browser/profiles/profiles_state.h" |
18 #include "chrome/browser/ui/browser.h" | 14 #include "chrome/browser/ui/browser.h" |
19 #include "chrome/browser/ui/browser_commands.h" | 15 #include "chrome/browser/ui/browser_commands.h" |
20 #include "chrome/browser/ui/browser_window.h" | 16 #include "chrome/browser/ui/browser_window.h" |
21 #import "chrome/browser/ui/cocoa/browser/avatar_label_button.h" | 17 #import "chrome/browser/ui/cocoa/base_bubble_controller.h" |
22 #import "chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller.h" | 18 #import "chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller.h" |
23 #import "chrome/browser/ui/cocoa/base_bubble_controller.h" | |
24 #import "chrome/browser/ui/cocoa/browser/profile_chooser_controller.h" | 19 #import "chrome/browser/ui/cocoa/browser/profile_chooser_controller.h" |
25 #import "chrome/browser/ui/cocoa/browser_window_controller.h" | 20 #import "chrome/browser/ui/cocoa/browser_window_controller.h" |
26 #include "chrome/common/profile_management_switches.h" | 21 #include "chrome/common/profile_management_switches.h" |
27 #include "content/public/browser/notification_service.h" | |
28 #include "grit/generated_resources.h" | 22 #include "grit/generated_resources.h" |
29 #include "grit/theme_resources.h" | 23 #include "grit/theme_resources.h" |
30 #include "ui/base/l10n/l10n_util_mac.h" | 24 #include "ui/base/l10n/l10n_util_mac.h" |
31 #include "ui/base/resource/resource_bundle.h" | 25 #include "ui/base/resource/resource_bundle.h" |
32 #include "ui/gfx/image/image.h" | 26 #include "ui/gfx/text_elider.h" |
33 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" | |
34 | 27 |
35 namespace { | 28 namespace { |
36 | |
37 // Space between the avatar icon and the avatar menu bubble. | 29 // Space between the avatar icon and the avatar menu bubble. |
38 const CGFloat kMenuYOffsetAdjust = 1.0; | 30 const CGFloat kMenuYOffsetAdjust = 1.0; |
39 | 31 |
40 // Space between the avatar label and the left edge of the container containing | 32 NSString* GetElidedProfileName(const base::string16& name) { |
41 // the label and the icon. | 33 // Maximum characters the button can be before the text will get elided. |
42 const CGFloat kAvatarSpacing = 4; | 34 const int kMaxCharactersToDisplay = 15; |
43 | 35 |
44 // Space between the bottom of the avatar icon and the bottom of the avatar | 36 gfx::FontList font_list = ui::ResourceBundle::GetSharedInstance().GetFontList( |
45 // label. | 37 ui::ResourceBundle::BaseFont); |
46 const CGFloat kAvatarLabelBottomSpacing = 3; | 38 return base::SysUTF16ToNSString(gfx::ElideText( |
47 | 39 name, |
48 // Space between the right edge of the avatar label and the right edge of the | 40 font_list, |
49 // avatar icon. | 41 font_list.GetExpectedTextWidth(kMaxCharactersToDisplay), |
50 const CGFloat kAvatarLabelRightSpacing = 2; | 42 gfx::ELIDE_AT_END)); |
| 43 } |
51 | 44 |
52 } // namespace | 45 } // namespace |
53 | 46 |
54 @interface AvatarButtonController (Private) | 47 @interface AvatarButtonController (Private) |
55 - (void)setButtonEnabled:(BOOL)flag; | 48 // Shows the ProfileMenuController. |
56 - (IBAction)buttonClicked:(id)sender; | 49 - (IBAction)buttonClicked:(id)sender; |
57 - (void)bubbleWillClose:(NSNotification*)notif; | 50 - (void)bubbleWillClose:(NSNotification*)notif; |
58 - (NSImage*)compositeImageWithShadow:(NSImage*)image; | 51 // Updates the profile name displayed by the avatar button. If |layoutParent| is |
59 - (void)updateAvatar; | 52 // yes, then the BrowserWindowController is notified to relayout the subviews, |
60 - (void)addOrRemoveButtonIfNecessary; | 53 // as the button needs to be repositioned. |
| 54 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent; |
61 @end | 55 @end |
62 | 56 |
63 // Declare a 10.7+ private API. | 57 class ProfileInfoUpdateObserver : public ProfileInfoCacheObserver { |
64 // NSThemeFrame < NSTitledFrame < NSFrameView < NSView. | |
65 @interface NSView (NSThemeFrame) | |
66 - (void)_tileTitlebarAndRedisplay:(BOOL)redisplay; | |
67 @end | |
68 | |
69 namespace AvatarButtonControllerInternal { | |
70 | |
71 class Observer : public content::NotificationObserver { | |
72 public: | 58 public: |
73 Observer(AvatarButtonController* button) : button_(button) { | 59 ProfileInfoUpdateObserver(AvatarButtonController* avatarButton) |
74 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, | 60 : avatarButton_(avatarButton) { |
75 content::NotificationService::AllSources()); | 61 g_browser_process->profile_manager()-> |
| 62 GetProfileInfoCache().AddObserver(this); |
76 } | 63 } |
77 | 64 |
78 // NotificationObserver: | 65 virtual ~ProfileInfoUpdateObserver() { |
79 virtual void Observe(int type, | 66 g_browser_process->profile_manager()-> |
80 const content::NotificationSource& source, | 67 GetProfileInfoCache().RemoveObserver(this); |
81 const content::NotificationDetails& details) OVERRIDE { | 68 } |
82 switch (type) { | 69 |
83 case chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED: | 70 // ProfileInfoCacheObserver: |
84 [button_ updateAvatar]; | 71 virtual void OnProfileAdded(const base::FilePath& profile_path) OVERRIDE { |
85 [button_ addOrRemoveButtonIfNecessary]; | 72 [avatarButton_ updateAvatarButtonAndLayoutParent:YES]; |
86 break; | 73 } |
87 default: | 74 |
88 NOTREACHED(); | 75 virtual void OnProfileWasRemoved( |
89 break; | 76 const base::FilePath& profile_path, |
90 } | 77 const base::string16& profile_name) OVERRIDE { |
| 78 [avatarButton_ updateAvatarButtonAndLayoutParent:YES]; |
| 79 } |
| 80 virtual void OnProfileNameChanged( |
| 81 const base::FilePath& profile_path, |
| 82 const base::string16& old_profile_name) OVERRIDE { |
| 83 [avatarButton_ updateAvatarButtonAndLayoutParent:YES]; |
| 84 } |
| 85 virtual void OnProfileAvatarChanged( |
| 86 const base::FilePath& profile_path) OVERRIDE { |
| 87 [avatarButton_ updateAvatarButtonAndLayoutParent:YES]; |
91 } | 88 } |
92 | 89 |
93 private: | 90 private: |
94 content::NotificationRegistrar registrar_; | 91 AvatarButtonController* avatarButton_; // Weak; owns this. |
95 | 92 |
96 AvatarButtonController* button_; // Weak; owns this. | 93 DISALLOW_COPY_AND_ASSIGN(ProfileInfoUpdateObserver); |
97 }; | 94 }; |
98 | 95 |
99 } // namespace AvatarButtonControllerInternal | 96 @implementation AvatarButtonController |
100 | 97 |
101 //////////////////////////////////////////////////////////////////////////////// | 98 - (id)init { |
102 | 99 if ((self = [super init])) |
103 @implementation AvatarButtonController | 100 profileInfoObserver_.reset(new ProfileInfoUpdateObserver(self)); |
| 101 return self; |
| 102 } |
104 | 103 |
105 - (id)initWithBrowser:(Browser*)browser { | 104 - (id)initWithBrowser:(Browser*)browser { |
106 if ((self = [super init])) { | 105 if ((self = [super init])) { |
107 browser_ = browser; | 106 browser_ = browser; |
| 107 profileInfoObserver_.reset(new ProfileInfoUpdateObserver(self)); |
108 | 108 |
109 base::scoped_nsobject<NSView> container( | 109 base::scoped_nsobject<NSView> container([[NSView alloc] |
110 [[NSView alloc] initWithFrame:NSMakeRect( | 110 initWithFrame:NSZeroRect]); |
111 0, 0, profiles::kAvatarIconWidth, profiles::kAvatarIconHeight)]); | |
112 [self setView:container]; | 111 [self setView:container]; |
113 button_.reset([[NSButton alloc] initWithFrame:NSMakeRect( | |
114 0, 0, profiles::kAvatarIconWidth, profiles::kAvatarIconHeight)]); | |
115 NSButtonCell* cell = [button_ cell]; | |
116 [button_ setButtonType:NSMomentaryLightButton]; | |
117 | 112 |
118 [button_ setImagePosition:NSImageOnly]; | 113 button_.reset([[NSButton alloc] initWithFrame:NSZeroRect]); |
119 [cell setImageScaling:NSImageScaleProportionallyDown]; | 114 [button_ setBezelStyle:NSTexturedRoundedBezelStyle]; |
120 [cell setImagePosition:NSImageBelow]; | 115 [button_ setImage:ui::ResourceBundle::GetSharedInstance(). |
121 | 116 GetNativeImageNamed(IDR_APP_DROPARROW).ToNSImage()]; |
122 // AppKit sets a title for some reason when using |-setImagePosition:|. | 117 [button_ setImagePosition:NSImageRight]; |
123 [button_ setTitle:nil]; | 118 [button_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin]; |
124 | |
125 [cell setImageDimsWhenDisabled:NO]; | |
126 [cell setHighlightsBy:NSContentsCellMask]; | |
127 [cell setShowsStateBy:NSContentsCellMask]; | |
128 | |
129 [button_ setBordered:NO]; | |
130 [button_ setTarget:self]; | 119 [button_ setTarget:self]; |
131 [button_ setAction:@selector(buttonClicked:)]; | 120 [button_ setAction:@selector(buttonClicked:)]; |
132 | 121 |
133 [cell accessibilitySetOverrideValue:NSAccessibilityButtonRole | 122 [self updateAvatarButtonAndLayoutParent:NO]; |
134 forAttribute:NSAccessibilityRoleAttribute]; | |
135 [cell accessibilitySetOverrideValue: | |
136 NSAccessibilityRoleDescription(NSAccessibilityButtonRole, nil) | |
137 forAttribute:NSAccessibilityRoleDescriptionAttribute]; | |
138 [cell accessibilitySetOverrideValue: | |
139 l10n_util::GetNSString(IDS_PROFILES_BUBBLE_ACCESSIBLE_NAME) | |
140 forAttribute:NSAccessibilityTitleAttribute]; | |
141 [cell accessibilitySetOverrideValue: | |
142 l10n_util::GetNSString(IDS_PROFILES_BUBBLE_ACCESSIBLE_DESCRIPTION) | |
143 forAttribute:NSAccessibilityHelpAttribute]; | |
144 [cell accessibilitySetOverrideValue: | |
145 l10n_util::GetNSString(IDS_PROFILES_BUBBLE_ACCESSIBLE_DESCRIPTION) | |
146 forAttribute:NSAccessibilityDescriptionAttribute]; | |
147 | 123 |
148 Profile* profile = browser_->profile(); | |
149 | |
150 if (profile->IsOffTheRecord() || profile->IsGuestSession()) { | |
151 const int icon_id = profile->IsGuestSession() ? IDR_LOGIN_GUEST : | |
152 IDR_OTR_ICON; | |
153 NSImage* icon = ResourceBundle::GetSharedInstance().GetNativeImageNamed( | |
154 icon_id).ToNSImage(); | |
155 [self setImage:[self compositeImageWithShadow:icon]]; | |
156 [self setButtonEnabled:profile->IsGuestSession()]; | |
157 } else { | |
158 [self setButtonEnabled:YES]; | |
159 observer_.reset(new AvatarButtonControllerInternal::Observer(self)); | |
160 [self updateAvatar]; | |
161 | |
162 // Managed users cannot enter incognito mode, so we only need to check | |
163 // it in this code path. | |
164 if (profile->IsManaged()) { | |
165 // Initialize the avatar label button. | |
166 CGFloat extraWidth = | |
167 profiles::kAvatarIconWidth + kAvatarLabelRightSpacing; | |
168 NSRect frame = NSMakeRect( | |
169 kAvatarSpacing, kAvatarLabelBottomSpacing, extraWidth, 0); | |
170 labelButton_.reset([[AvatarLabelButton alloc] initWithFrame:frame]); | |
171 [labelButton_ setTarget:self]; | |
172 [labelButton_ setAction:@selector(buttonClicked:)]; | |
173 [[self view] addSubview:labelButton_]; | |
174 | |
175 // Resize the container and reposition the avatar button. | |
176 NSSize textSize = [[labelButton_ cell] labelTextSize]; | |
177 [container setFrameSize: | |
178 NSMakeSize([labelButton_ frame].size.width + kAvatarSpacing, | |
179 profiles::kAvatarIconHeight)]; | |
180 [button_ | |
181 setFrameOrigin:NSMakePoint(kAvatarSpacing + textSize.width, 0)]; | |
182 } | |
183 } | |
184 [[self view] addSubview:button_]; | 124 [[self view] addSubview:button_]; |
185 } | 125 } |
186 return self; | 126 return self; |
187 } | 127 } |
188 | 128 |
189 - (void)dealloc { | 129 - (void)dealloc { |
190 [[NSNotificationCenter defaultCenter] | 130 [[NSNotificationCenter defaultCenter] |
191 removeObserver:self | 131 removeObserver:self |
192 name:NSWindowWillCloseNotification | 132 name:NSWindowWillCloseNotification |
193 object:[menuController_ window]]; | 133 object:[menuController_ window]]; |
194 [super dealloc]; | 134 [super dealloc]; |
195 } | 135 } |
196 | 136 |
197 - (NSButton*)buttonView { | 137 - (NSButton*)buttonView { |
198 return button_.get(); | 138 return button_.get(); |
199 } | 139 } |
200 | 140 |
201 - (NSButton*)labelButtonView { | |
202 return labelButton_.get(); | |
203 } | |
204 | |
205 - (void)setImage:(NSImage*)image { | |
206 [button_ setImage:image]; | |
207 } | |
208 | |
209 - (void)showAvatarBubble:(NSView*)anchor { | 141 - (void)showAvatarBubble:(NSView*)anchor { |
210 if (menuController_) | 142 if (menuController_) |
211 return; | 143 return; |
212 | 144 |
213 DCHECK(chrome::IsCommandEnabled(browser_, IDC_SHOW_AVATAR_MENU)); | 145 DCHECK(chrome::IsCommandEnabled(browser_, IDC_SHOW_AVATAR_MENU)); |
214 | 146 |
215 NSWindowController* wc = | 147 NSWindowController* wc = |
216 [browser_->window()->GetNativeWindow() windowController]; | 148 [browser_->window()->GetNativeWindow() windowController]; |
217 if ([wc isKindOfClass:[BrowserWindowController class]]) { | 149 if ([wc isKindOfClass:[BrowserWindowController class]]) { |
218 [static_cast<BrowserWindowController*>(wc) | 150 [static_cast<BrowserWindowController*>(wc) |
219 lockBarVisibilityForOwner:self withAnimation:NO delay:NO]; | 151 lockBarVisibilityForOwner:self withAnimation:NO delay:NO]; |
220 } | 152 } |
221 | 153 |
222 NSPoint point = NSMakePoint(NSMidX([anchor bounds]), | 154 NSPoint point = NSMakePoint(NSMidX([anchor bounds]), |
223 NSMaxY([anchor bounds]) - kMenuYOffsetAdjust); | 155 NSMaxY([anchor bounds]) - kMenuYOffsetAdjust); |
224 point = [anchor convertPoint:point toView:nil]; | 156 point = [anchor convertPoint:point toView:nil]; |
225 point = [[anchor window] convertBaseToScreen:point]; | 157 point = [[anchor window] convertBaseToScreen:point]; |
226 | 158 |
227 // |menuController_| will automatically release itself on close. | 159 // |menuController_| will automatically release itself on close. |
228 if (switches::IsNewProfileManagement()) { | 160 if (switches::IsNewProfileManagement()) { |
229 menuController_ = | 161 menuController_ = |
230 [[ProfileChooserController alloc] initWithBrowser:browser_ | 162 [[ProfileChooserController alloc] initWithBrowser:browser_ |
231 anchoredAt:point]; | 163 anchoredAt:point]; |
232 } else { | 164 } else { |
233 menuController_ = | 165 menuController_ = |
234 [[AvatarMenuBubbleController alloc] initWithBrowser:browser_ | 166 [[AvatarMenuBubbleController alloc] initWithBrowser:browser_ |
235 anchoredAt:point]; | 167 anchoredAt:point]; |
236 } | 168 } |
| 169 |
237 [[NSNotificationCenter defaultCenter] | 170 [[NSNotificationCenter defaultCenter] |
238 addObserver:self | 171 addObserver:self |
239 selector:@selector(bubbleWillClose:) | 172 selector:@selector(bubbleWillClose:) |
240 name:NSWindowWillCloseNotification | 173 name:NSWindowWillCloseNotification |
241 object:[menuController_ window]]; | 174 object:[menuController_ window]]; |
242 [menuController_ showWindow:self]; | 175 [menuController_ showWindow:self]; |
243 | 176 |
244 ProfileMetrics::LogProfileOpenMethod(ProfileMetrics::ICON_AVATAR_BUBBLE); | 177 ProfileMetrics::LogProfileOpenMethod(ProfileMetrics::ICON_AVATAR_BUBBLE); |
245 } | 178 } |
246 | 179 |
247 // Private ///////////////////////////////////////////////////////////////////// | |
248 | |
249 - (void)setButtonEnabled:(BOOL)flag { | |
250 [button_ setEnabled:flag]; | |
251 } | |
252 | |
253 - (IBAction)buttonClicked:(id)sender { | 180 - (IBAction)buttonClicked:(id)sender { |
254 DCHECK(sender == button_.get() || sender == labelButton_.get()); | 181 DCHECK(sender == button_.get()); |
255 [self showAvatarBubble:button_]; | 182 [self showAvatarBubble:button_]; |
256 } | 183 } |
257 | 184 |
258 - (void)bubbleWillClose:(NSNotification*)notif { | 185 - (void)bubbleWillClose:(NSNotification*)notif { |
259 NSWindowController* wc = | 186 NSWindowController* wc = |
260 [browser_->window()->GetNativeWindow() windowController]; | 187 [browser_->window()->GetNativeWindow() windowController]; |
261 if ([wc isKindOfClass:[BrowserWindowController class]]) { | 188 if ([wc isKindOfClass:[BrowserWindowController class]]) { |
262 [static_cast<BrowserWindowController*>(wc) | 189 [static_cast<BrowserWindowController*>(wc) |
263 releaseBarVisibilityForOwner:self withAnimation:YES delay:NO]; | 190 releaseBarVisibilityForOwner:self withAnimation:YES delay:NO]; |
264 } | 191 } |
265 menuController_ = nil; | 192 menuController_ = nil; |
266 } | 193 } |
267 | 194 |
268 // This will take in an original image and redraw it with a shadow. | 195 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent { |
269 - (NSImage*)compositeImageWithShadow:(NSImage*)image { | 196 [button_ setTitle:GetElidedProfileName( |
270 gfx::ScopedNSGraphicsContextSaveGState scopedGState; | 197 profiles::GetActiveProfileDisplayName(browser_))]; |
| 198 [button_ sizeToFit]; |
271 | 199 |
272 base::scoped_nsobject<NSImage> destination( | 200 // Resize the container. |
273 [[NSImage alloc] initWithSize:[image size]]); | 201 [[self view] setFrameSize:[button_ frame].size]; |
| 202 [button_ setFrameOrigin:NSMakePoint(0, 0)]; |
274 | 203 |
275 NSRect destRect = NSZeroRect; | 204 if (layoutParent) { |
276 destRect.size = [destination size]; | 205 // Because the width of the button might have changed, the parent browser |
277 | 206 // frame needs to recalculate the button bounds and redraw it. |
278 [destination lockFocus]; | 207 [[BrowserWindowController |
279 | 208 browserWindowControllerForWindow:browser_->window()->GetNativeWindow()] |
280 base::scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]); | 209 layoutSubviews]; |
281 [shadow.get() setShadowColor:[NSColor colorWithCalibratedWhite:0.0 | 210 } |
282 alpha:0.75]]; | |
283 [shadow.get() setShadowOffset:NSZeroSize]; | |
284 [shadow.get() setShadowBlurRadius:3.0]; | |
285 [shadow.get() set]; | |
286 | |
287 [image drawInRect:destRect | |
288 fromRect:NSZeroRect | |
289 operation:NSCompositeSourceOver | |
290 fraction:1.0 | |
291 respectFlipped:YES | |
292 hints:nil]; | |
293 | |
294 [destination unlockFocus]; | |
295 | |
296 return destination.autorelease(); | |
297 } | 211 } |
298 | 212 |
299 // Updates the avatar information from the profile cache. | |
300 - (void)updateAvatar { | |
301 ProfileInfoCache& cache = | |
302 g_browser_process->profile_manager()->GetProfileInfoCache(); | |
303 size_t index = | |
304 cache.GetIndexOfProfileWithPath(browser_->profile()->GetPath()); | |
305 if (index == std::string::npos) | |
306 return; | |
307 BOOL is_gaia_picture = | |
308 cache.IsUsingGAIAPictureOfProfileAtIndex(index) && | |
309 cache.GetGAIAPictureOfProfileAtIndex(index); | |
310 gfx::Image icon = profiles::GetAvatarIconForTitleBar( | |
311 cache.GetAvatarIconOfProfileAtIndex(index), is_gaia_picture, | |
312 profiles::kAvatarIconWidth, profiles::kAvatarIconHeight); | |
313 [self setImage:icon.ToNSImage()]; | |
314 | |
315 const base::string16& name = cache.GetNameOfProfileAtIndex(index); | |
316 NSString* nsName = base::SysUTF16ToNSString(name); | |
317 [button_ setToolTip:nsName]; | |
318 [[button_ cell] | |
319 accessibilitySetOverrideValue:nsName | |
320 forAttribute:NSAccessibilityValueAttribute]; | |
321 } | |
322 | |
323 // If the second-to-last profile was removed or a second profile was added, | |
324 // show or hide the avatar button from the window frame. | |
325 - (void)addOrRemoveButtonIfNecessary { | |
326 if (browser_->profile()->IsOffTheRecord()) | |
327 return; | |
328 | |
329 NSWindowController* wc = | |
330 [browser_->window()->GetNativeWindow() windowController]; | |
331 if (![wc isKindOfClass:[BrowserWindowController class]]) | |
332 return; | |
333 | |
334 size_t count = g_browser_process->profile_manager()->GetNumberOfProfiles(); | |
335 [self.view setHidden:count < 2]; | |
336 | |
337 [static_cast<BrowserWindowController*>(wc) layoutSubviews]; | |
338 | |
339 // If the avatar is being added or removed, then the Lion fullscreen button | |
340 // needs to be adjusted. Since the fullscreen button is positioned by | |
341 // FramedBrowserWindow using private APIs, the easiest way to update the | |
342 // position of the button is through this private API. Resizing the window | |
343 // also works, but invoking |-display| does not. | |
344 NSView* themeFrame = [[[wc window] contentView] superview]; | |
345 if ([themeFrame respondsToSelector:@selector(_tileTitlebarAndRedisplay:)]) | |
346 [themeFrame _tileTitlebarAndRedisplay:YES]; | |
347 } | |
348 | |
349 // Testing ///////////////////////////////////////////////////////////////////// | |
350 | |
351 - (BaseBubbleController*)menuController { | 213 - (BaseBubbleController*)menuController { |
352 return menuController_; | 214 return menuController_; |
353 } | 215 } |
354 | 216 |
355 @end | 217 @end |
OLD | NEW |