Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / profiles / avatar_icon_controller.mm
blob6b8dbb8fbb98bcb261857081dd46d2080fe284eb
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_icon_controller.h"
7 #include "base/mac/foundation_util.h"
8 #include "base/strings/sys_string_conversions.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
12 #include "chrome/browser/profiles/profile_info_cache.h"
13 #include "chrome/browser/profiles/profile_manager.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_window.h"
16 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
17 #import "chrome/browser/ui/cocoa/profiles/avatar_label_button.h"
18 #include "chrome/grit/generated_resources.h"
19 #include "grit/theme_resources.h"
20 #include "ui/base/l10n/l10n_util_mac.h"
21 #include "ui/base/resource/resource_bundle.h"
22 #include "ui/gfx/image/image.h"
23 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
25 namespace {
27 // Space between the avatar label and the left edge of the container containing
28 // the label and the icon.
29 const CGFloat kAvatarSpacing = 4;
31 // Space between the bottom of the avatar icon and the bottom of the avatar
32 // label.
33 const CGFloat kAvatarLabelBottomSpacing = 3;
35 // Space between the right edge of the avatar label and the right edge of the
36 // avatar icon.
37 const CGFloat kAvatarLabelRightSpacing = 2;
39 }  // namespace
41 @interface AvatarIconController (Private)
42 - (void)setButtonEnabled:(BOOL)flag;
43 - (NSImage*)compositeImageWithShadow:(NSImage*)image;
44 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent;
45 - (void)addOrRemoveButtonIfNecessary;
46 @end
48 // Declare a 10.7+ private API.
49 // NSThemeFrame < NSTitledFrame < NSFrameView < NSView.
50 @interface NSView (NSThemeFrame)
51 - (void)_tileTitlebarAndRedisplay:(BOOL)redisplay;
52 @end
54 @implementation AvatarIconController
56 - (id)initWithBrowser:(Browser*)browser {
57   if ((self = [super initWithBrowser:browser])) {
58     browser_ = browser;
60     base::scoped_nsobject<NSView> container(
61         [[NSView alloc] initWithFrame:NSMakeRect(
62             0, 0, profiles::kAvatarIconWidth, profiles::kAvatarIconHeight)]);
63     [container setWantsLayer:YES];
64     [self setView:container];
66     button_.reset([[NSButton alloc] initWithFrame:NSMakeRect(
67         0, 0, profiles::kAvatarIconWidth, profiles::kAvatarIconHeight)]);
68     NSButtonCell* cell = [button_ cell];
69     [button_ setButtonType:NSMomentaryLightButton];
71     [button_ setImagePosition:NSImageOnly];
72     [cell setImageScaling:NSImageScaleProportionallyDown];
73     [cell setImagePosition:NSImageBelow];
75     // AppKit sets a title for some reason when using |-setImagePosition:|.
76     [button_ setTitle:nil];
78     [cell setImageDimsWhenDisabled:NO];
79     [cell setHighlightsBy:NSContentsCellMask];
80     [cell setShowsStateBy:NSContentsCellMask];
82     [button_ setBordered:NO];
83     [button_ setTarget:self];
84     [button_ setAction:@selector(buttonClicked:)];
86     [cell accessibilitySetOverrideValue:NSAccessibilityButtonRole
87                            forAttribute:NSAccessibilityRoleAttribute];
88     [cell accessibilitySetOverrideValue:
89         NSAccessibilityRoleDescription(NSAccessibilityButtonRole, nil)
90           forAttribute:NSAccessibilityRoleDescriptionAttribute];
91     [cell accessibilitySetOverrideValue:
92         l10n_util::GetNSString(IDS_PROFILES_BUBBLE_ACCESSIBLE_NAME)
93                            forAttribute:NSAccessibilityTitleAttribute];
94     [cell accessibilitySetOverrideValue:
95         l10n_util::GetNSString(IDS_PROFILES_BUBBLE_ACCESSIBLE_DESCRIPTION)
96                            forAttribute:NSAccessibilityHelpAttribute];
97     [cell accessibilitySetOverrideValue:
98         l10n_util::GetNSString(IDS_PROFILES_BUBBLE_ACCESSIBLE_DESCRIPTION)
99                            forAttribute:NSAccessibilityDescriptionAttribute];
101     Profile* profile = browser_->profile();
103     if (profile->IsOffTheRecord() || profile->IsGuestSession()) {
104       const int icon_id = profile->IsGuestSession() ?
105           profiles::GetPlaceholderAvatarIconResourceID() : IDR_OTR_ICON;
106       NSImage* icon = ResourceBundle::GetSharedInstance().GetNativeImageNamed(
107           icon_id).ToNSImage();
108       [self setImage:[self compositeImageWithShadow:icon]];
109       [self setButtonEnabled:profile->IsGuestSession()];
110    } else {
111       [self setButtonEnabled:YES];
112       [self updateAvatarButtonAndLayoutParent:NO];
114       // Supervised users cannot enter incognito mode, so we only need to check
115       // it in this code path.
116       if (profile->IsSupervised()) {
117         // Initialize the avatar label button.
118         CGFloat extraWidth =
119             profiles::kAvatarIconWidth + kAvatarLabelRightSpacing;
120         NSRect frame = NSMakeRect(
121             kAvatarSpacing, kAvatarLabelBottomSpacing, extraWidth, 0);
122         labelButton_.reset([[AvatarLabelButton alloc] initWithFrame:frame]);
123         [labelButton_ setTarget:self];
124         [labelButton_ setAction:@selector(buttonClicked:)];
125         [[self view] addSubview:labelButton_];
127         // Resize the container and reposition the avatar button.
128         NSSize textSize = [[labelButton_ cell] labelTextSize];
129         [container setFrameSize:
130             NSMakeSize([labelButton_ frame].size.width + kAvatarSpacing,
131                        profiles::kAvatarIconHeight)];
132         [button_
133             setFrameOrigin:NSMakePoint(kAvatarSpacing + textSize.width, 0)];
134       }
135     }
136     [[self view] addSubview:button_];
137   }
138   return self;
141 - (NSButton*)labelButtonView {
142   return labelButton_.get();
145 - (void)setImage:(NSImage*)image {
146   [button_ setImage:image];
149 - (void)setButtonEnabled:(BOOL)flag {
150   [button_ setEnabled:flag];
153 // This will take in an original image and redraw it with a shadow.
154 - (NSImage*)compositeImageWithShadow:(NSImage*)image {
155   gfx::ScopedNSGraphicsContextSaveGState scopedGState;
157   base::scoped_nsobject<NSImage> destination(
158       [[NSImage alloc] initWithSize:[image size]]);
160   NSRect destRect = NSZeroRect;
161   destRect.size = [destination size];
163   [destination lockFocus];
165   base::scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]);
166   [shadow.get() setShadowColor:[NSColor colorWithCalibratedWhite:0.0
167                                                            alpha:0.75]];
168   [shadow.get() setShadowOffset:NSZeroSize];
169   [shadow.get() setShadowBlurRadius:3.0];
170   [shadow.get() set];
172   [image drawInRect:destRect
173            fromRect:NSZeroRect
174           operation:NSCompositeSourceOver
175            fraction:1.0
176      respectFlipped:YES
177               hints:nil];
179   [destination unlockFocus];
181   return destination.autorelease();
184 // Updates the avatar information from the profile cache.
185 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent {
186   // No updates are needed for an incognito or guest window, as the avatar
187   // is always fixed.
188   Profile* profile = browser_->profile();
189   if (profile->IsOffTheRecord() || profile->IsGuestSession())
190     return;
192   ProfileInfoCache& cache =
193       g_browser_process->profile_manager()->GetProfileInfoCache();
194   size_t index =
195       cache.GetIndexOfProfileWithPath(browser_->profile()->GetPath());
196   if (index == std::string::npos)
197     return;
199   BOOL is_gaia_picture =
200       cache.IsUsingGAIAPictureOfProfileAtIndex(index) &&
201       cache.GetGAIAPictureOfProfileAtIndex(index);
202   gfx::Image icon = profiles::GetAvatarIconForTitleBar(
203       cache.GetAvatarIconOfProfileAtIndex(index), is_gaia_picture,
204       profiles::kAvatarIconWidth, profiles::kAvatarIconHeight);
205   [self setImage:icon.ToNSImage()];
207   const base::string16& name = cache.GetNameOfProfileAtIndex(index);
208   NSString* nsName = base::SysUTF16ToNSString(name);
209   [button_ setToolTip:nsName];
210   [[button_ cell]
211       accessibilitySetOverrideValue:nsName
212                        forAttribute:NSAccessibilityValueAttribute];
213   if (layoutParent)
214     [self addOrRemoveButtonIfNecessary];
217 // If the second-to-last profile was removed or a second profile was added,
218 // show or hide the avatar button from the window frame.
219 - (void)addOrRemoveButtonIfNecessary {
220   if (browser_->profile()->IsOffTheRecord())
221     return;
223   BrowserWindowController* wc = base::mac::ObjCCast<BrowserWindowController>(
224       [browser_->window()->GetNativeWindow() windowController]);
225   if (!wc)
226     return;
228   [self.view setHidden:![wc shouldShowAvatar]];
230   // If the avatar is being added or removed, then the Lion fullscreen button
231   // needs to be adjusted. Since the fullscreen button is positioned by
232   // FramedBrowserWindow using private APIs, the easiest way to update the
233   // position of the button is through this private API. Resizing the window
234   // also works, but invoking |-display| does not.
235   NSView* themeFrame = [[[wc window] contentView] superview];
236   if ([themeFrame respondsToSelector:@selector(_tileTitlebarAndRedisplay:)])
237     [themeFrame _tileTitlebarAndRedisplay:YES];
239   // This needs to be called after the fullscreen button is positioned, because
240   // TabStripController's rightIndentForControls depends on it.
241   [wc layoutSubviews];
244 @end