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