Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / profiles / 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/profiles/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_avatar_icon_util.h"
11 #include "chrome/browser/profiles/profile_info_cache.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_window_controller.h"
16 #import "chrome/browser/ui/cocoa/profiles/avatar_label_button.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     [container setWantsLayer:YES];
63     [self setView:container];
64
65     button_.reset([[NSButton alloc] initWithFrame:NSMakeRect(
66         0, 0, profiles::kAvatarIconWidth, profiles::kAvatarIconHeight)]);
67     NSButtonCell* cell = [button_ cell];
68     [button_ setButtonType:NSMomentaryLightButton];
69
70     [button_ setImagePosition:NSImageOnly];
71     [cell setImageScaling:NSImageScaleProportionallyDown];
72     [cell setImagePosition:NSImageBelow];
73
74     // AppKit sets a title for some reason when using |-setImagePosition:|.
75     [button_ setTitle:nil];
76
77     [cell setImageDimsWhenDisabled:NO];
78     [cell setHighlightsBy:NSContentsCellMask];
79     [cell setShowsStateBy:NSContentsCellMask];
80
81     [button_ setBordered:NO];
82     [button_ setTarget:self];
83     [button_ setAction:@selector(buttonClicked:)];
84
85     [cell accessibilitySetOverrideValue:NSAccessibilityButtonRole
86                            forAttribute:NSAccessibilityRoleAttribute];
87     [cell accessibilitySetOverrideValue:
88         NSAccessibilityRoleDescription(NSAccessibilityButtonRole, nil)
89           forAttribute:NSAccessibilityRoleDescriptionAttribute];
90     [cell accessibilitySetOverrideValue:
91         l10n_util::GetNSString(IDS_PROFILES_BUBBLE_ACCESSIBLE_NAME)
92                            forAttribute:NSAccessibilityTitleAttribute];
93     [cell accessibilitySetOverrideValue:
94         l10n_util::GetNSString(IDS_PROFILES_BUBBLE_ACCESSIBLE_DESCRIPTION)
95                            forAttribute:NSAccessibilityHelpAttribute];
96     [cell accessibilitySetOverrideValue:
97         l10n_util::GetNSString(IDS_PROFILES_BUBBLE_ACCESSIBLE_DESCRIPTION)
98                            forAttribute:NSAccessibilityDescriptionAttribute];
99
100     Profile* profile = browser_->profile();
101
102     if (profile->IsOffTheRecord() || profile->IsGuestSession()) {
103       const int icon_id = profile->IsGuestSession() ?
104           profiles::GetPlaceholderAvatarIconResourceID() : IDR_OTR_ICON;
105       NSImage* icon = ResourceBundle::GetSharedInstance().GetNativeImageNamed(
106           icon_id).ToNSImage();
107       [self setImage:[self compositeImageWithShadow:icon]];
108       [self setButtonEnabled:profile->IsGuestSession()];
109    } else {
110       [self setButtonEnabled:YES];
111       [self updateAvatarButtonAndLayoutParent:NO];
112
113       // Supervised users cannot enter incognito mode, so we only need to check
114       // it in this code path.
115       if (profile->IsSupervised()) {
116         // Initialize the avatar label button.
117         CGFloat extraWidth =
118             profiles::kAvatarIconWidth + kAvatarLabelRightSpacing;
119         NSRect frame = NSMakeRect(
120             kAvatarSpacing, kAvatarLabelBottomSpacing, extraWidth, 0);
121         labelButton_.reset([[AvatarLabelButton alloc] initWithFrame:frame]);
122         [labelButton_ setTarget:self];
123         [labelButton_ setAction:@selector(buttonClicked:)];
124         [[self view] addSubview:labelButton_];
125
126         // Resize the container and reposition the avatar button.
127         NSSize textSize = [[labelButton_ cell] labelTextSize];
128         [container setFrameSize:
129             NSMakeSize([labelButton_ frame].size.width + kAvatarSpacing,
130                        profiles::kAvatarIconHeight)];
131         [button_
132             setFrameOrigin:NSMakePoint(kAvatarSpacing + textSize.width, 0)];
133       }
134     }
135     [[self view] addSubview:button_];
136   }
137   return self;
138 }
139
140 - (NSButton*)labelButtonView {
141   return labelButton_.get();
142 }
143
144 - (void)setImage:(NSImage*)image {
145   [button_ setImage:image];
146 }
147
148 - (void)setButtonEnabled:(BOOL)flag {
149   [button_ setEnabled:flag];
150 }
151
152 // This will take in an original image and redraw it with a shadow.
153 - (NSImage*)compositeImageWithShadow:(NSImage*)image {
154   gfx::ScopedNSGraphicsContextSaveGState scopedGState;
155
156   base::scoped_nsobject<NSImage> destination(
157       [[NSImage alloc] initWithSize:[image size]]);
158
159   NSRect destRect = NSZeroRect;
160   destRect.size = [destination size];
161
162   [destination lockFocus];
163
164   base::scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]);
165   [shadow.get() setShadowColor:[NSColor colorWithCalibratedWhite:0.0
166                                                            alpha:0.75]];
167   [shadow.get() setShadowOffset:NSZeroSize];
168   [shadow.get() setShadowBlurRadius:3.0];
169   [shadow.get() set];
170
171   [image drawInRect:destRect
172            fromRect:NSZeroRect
173           operation:NSCompositeSourceOver
174            fraction:1.0
175      respectFlipped:YES
176               hints:nil];
177
178   [destination unlockFocus];
179
180   return destination.autorelease();
181 }
182
183 // Updates the avatar information from the profile cache.
184 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent {
185   // No updates are needed for an incognito or guest window, as the avatar
186   // is always fixed.
187   Profile* profile = browser_->profile();
188   if (profile->IsOffTheRecord() || profile->IsGuestSession())
189     return;
190
191   ProfileInfoCache& cache =
192       g_browser_process->profile_manager()->GetProfileInfoCache();
193   size_t index =
194       cache.GetIndexOfProfileWithPath(browser_->profile()->GetPath());
195   if (index == std::string::npos)
196     return;
197
198   BOOL is_gaia_picture =
199       cache.IsUsingGAIAPictureOfProfileAtIndex(index) &&
200       cache.GetGAIAPictureOfProfileAtIndex(index);
201   gfx::Image icon = profiles::GetAvatarIconForTitleBar(
202       cache.GetAvatarIconOfProfileAtIndex(index), is_gaia_picture,
203       profiles::kAvatarIconWidth, profiles::kAvatarIconHeight);
204   [self setImage:icon.ToNSImage()];
205
206   const base::string16& name = cache.GetNameOfProfileAtIndex(index);
207   NSString* nsName = base::SysUTF16ToNSString(name);
208   [button_ setToolTip:nsName];
209   [[button_ cell]
210       accessibilitySetOverrideValue:nsName
211                        forAttribute:NSAccessibilityValueAttribute];
212   if (layoutParent)
213     [self addOrRemoveButtonIfNecessary];
214 }
215
216 // If the second-to-last profile was removed or a second profile was added,
217 // show or hide the avatar button from the window frame.
218 - (void)addOrRemoveButtonIfNecessary {
219   if (browser_->profile()->IsOffTheRecord())
220     return;
221
222   NSWindowController* wc =
223       [browser_->window()->GetNativeWindow() windowController];
224   if (![wc isKindOfClass:[BrowserWindowController class]])
225     return;
226
227   [self.view setHidden:
228       ![static_cast<BrowserWindowController*>(wc) shouldShowAvatar]];
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