Upstream version 7.36.149.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/nsview_additions.h"
17 #import "chrome/browser/ui/cocoa/profiles/avatar_label_button.h"
18 #include "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"
24
25 namespace {
26
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;
30
31 // Space between the bottom of the avatar icon and the bottom of the avatar
32 // label.
33 const CGFloat kAvatarLabelBottomSpacing = 3;
34
35 // Space between the right edge of the avatar label and the right edge of the
36 // avatar icon.
37 const CGFloat kAvatarLabelRightSpacing = 2;
38
39 }  // namespace
40
41 @interface AvatarIconController (Private)
42 - (void)setButtonEnabled:(BOOL)flag;
43 - (NSImage*)compositeImageWithShadow:(NSImage*)image;
44 - (void)updateAvatarButtonAndLayoutParent:(BOOL)layoutParent;
45 - (void)addOrRemoveButtonIfNecessary;
46 @end
47
48 // Declare a 10.7+ private API.
49 // NSThemeFrame < NSTitledFrame < NSFrameView < NSView.
50 @interface NSView (NSThemeFrame)
51 - (void)_tileTitlebarAndRedisplay:(BOOL)redisplay;
52 @end
53
54 @implementation AvatarIconController
55
56 - (id)initWithBrowser:(Browser*)browser {
57   if ((self = [super initWithBrowser:browser])) {
58     browser_ = browser;
59
60     base::scoped_nsobject<NSView> container(
61         [[NSView alloc] initWithFrame:NSMakeRect(
62             0, 0, profiles::kAvatarIconWidth, profiles::kAvatarIconHeight)]);
63     [container cr_setWantsLayer:YES];
64     [self setView:container];
65
66     button_.reset([[NSButton alloc] initWithFrame:NSMakeRect(
67         0, 0, profiles::kAvatarIconWidth, profiles::kAvatarIconHeight)]);
68     NSButtonCell* cell = [button_ cell];
69     [button_ setButtonType:NSMomentaryLightButton];
70
71     [button_ setImagePosition:NSImageOnly];
72     [cell setImageScaling:NSImageScaleProportionallyDown];
73     [cell setImagePosition:NSImageBelow];
74
75     // AppKit sets a title for some reason when using |-setImagePosition:|.
76     [button_ setTitle:nil];
77
78     [cell setImageDimsWhenDisabled:NO];
79     [cell setHighlightsBy:NSContentsCellMask];
80     [cell setShowsStateBy:NSContentsCellMask];
81
82     [button_ setBordered:NO];
83     [button_ setTarget:self];
84     [button_ setAction:@selector(buttonClicked:)];
85
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];
100
101     Profile* profile = browser_->profile();
102
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];
113
114       // Managed users cannot enter incognito mode, so we only need to check
115       // it in this code path.
116       if (profile->IsManaged()) {
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_];
126
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;
139 }
140
141 - (NSButton*)labelButtonView {
142   return labelButton_.get();
143 }
144
145 - (void)setImage:(NSImage*)image {
146   [button_ setImage:image];
147 }
148
149 - (void)setButtonEnabled:(BOOL)flag {
150   [button_ setEnabled:flag];
151 }
152
153 // This will take in an original image and redraw it with a shadow.
154 - (NSImage*)compositeImageWithShadow:(NSImage*)image {
155   gfx::ScopedNSGraphicsContextSaveGState scopedGState;
156
157   base::scoped_nsobject<NSImage> destination(
158       [[NSImage alloc] initWithSize:[image size]]);
159
160   NSRect destRect = NSZeroRect;
161   destRect.size = [destination size];
162
163   [destination lockFocus];
164
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];
171
172   [image drawInRect:destRect
173            fromRect:NSZeroRect
174           operation:NSCompositeSourceOver
175            fraction:1.0
176      respectFlipped:YES
177               hints:nil];
178
179   [destination unlockFocus];
180
181   return destination.autorelease();
182 }
183
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;
191
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;
198
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()];
206
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];
215 }
216
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;
222
223   NSWindowController* wc =
224       [browser_->window()->GetNativeWindow() windowController];
225   if (![wc isKindOfClass:[BrowserWindowController class]])
226     return;
227
228   size_t count = g_browser_process->profile_manager()->GetNumberOfProfiles();
229   [self.view setHidden:count < 2];
230
231   [static_cast<BrowserWindowController*>(wc) layoutSubviews];
232
233   // If the avatar is being added or removed, then the Lion fullscreen button
234   // needs to be adjusted. Since the fullscreen button is positioned by
235   // FramedBrowserWindow using private APIs, the easiest way to update the
236   // position of the button is through this private API. Resizing the window
237   // also works, but invoking |-display| does not.
238   NSView* themeFrame = [[[wc window] contentView] superview];
239   if ([themeFrame respondsToSelector:@selector(_tileTitlebarAndRedisplay:)])
240     [themeFrame _tileTitlebarAndRedisplay:YES];
241 }
242
243 @end