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 #include "chrome/browser/ui/views/profiles/new_avatar_button.h"
6
7 #include "base/strings/utf_string_conversions.h"
8 #include "base/win/windows_version.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/profiles/profile_manager.h"
11 #include "chrome/browser/profiles/profiles_state.h"
12 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
13 #include "chrome/browser/ui/browser.h"
14 #include "components/signin/core/browser/profile_oauth2_token_service.h"
15 #include "grit/generated_resources.h"
16 #include "grit/theme_resources.h"
17 #include "ui/base/l10n/l10n_util.h"
18 #include "ui/base/resource/resource_bundle.h"
19 #include "ui/gfx/canvas.h"
20 #include "ui/gfx/color_utils.h"
21 #include "ui/gfx/font_list.h"
22 #include "ui/gfx/text_constants.h"
23 #include "ui/gfx/text_elider.h"
24 #include "ui/views/border.h"
25 #include "ui/views/controls/button/label_button_border.h"
26 #include "ui/views/painter.h"
27
28 namespace {
29
CreateBorder(const int normal_image_set[],const int hot_image_set[],const int pushed_image_set[])30 scoped_ptr<views::Border> CreateBorder(const int normal_image_set[],
31 const int hot_image_set[],
32 const int pushed_image_set[]) {
33 scoped_ptr<views::LabelButtonBorder> border(
34 new views::LabelButtonBorder(views::Button::STYLE_TEXTBUTTON));
35 border->SetPainter(false, views::Button::STATE_NORMAL,
36 views::Painter::CreateImageGridPainter(normal_image_set));
37 border->SetPainter(false, views::Button::STATE_HOVERED,
38 views::Painter::CreateImageGridPainter(hot_image_set));
39 border->SetPainter(false, views::Button::STATE_PRESSED,
40 views::Painter::CreateImageGridPainter(pushed_image_set));
41
42 const int kLeftRightInset = 10;
43 const int kTopInset = 0;
44 const int kBottomInset = 4;
45 border->set_insets(gfx::Insets(kTopInset, kLeftRightInset,
46 kBottomInset, kLeftRightInset));
47
48 return border.PassAs<views::Border>();
49 }
50
GetElidedText(const base::string16 & original_text)51 base::string16 GetElidedText(const base::string16& original_text) {
52 // Maximum characters the button can be before the text will get elided.
53 const int kMaxCharactersToDisplay = 15;
54 const gfx::FontList font_list;
55 return gfx::ElideText(original_text, font_list,
56 font_list.GetExpectedTextWidth(kMaxCharactersToDisplay),
57 gfx::ELIDE_TAIL);
58 }
59
GetButtonText(Profile * profile)60 base::string16 GetButtonText(Profile* profile) {
61 base::string16 name = GetElidedText(
62 profiles::GetAvatarNameForProfile(profile));
63 if (profile->IsSupervised())
64 name = l10n_util::GetStringFUTF16(IDS_MANAGED_USER_NEW_AVATAR_LABEL, name);
65 return name;
66 }
67
68 } // namespace
69
NewAvatarButton(views::ButtonListener * listener,const base::string16 & profile_name,AvatarButtonStyle button_style,Browser * browser)70 NewAvatarButton::NewAvatarButton(
71 views::ButtonListener* listener,
72 const base::string16& profile_name,
73 AvatarButtonStyle button_style,
74 Browser* browser)
75 : MenuButton(listener, GetButtonText(browser->profile()), NULL, true),
76 browser_(browser) {
77 set_animate_on_state_change(false);
78 SetTextColor(views::Button::STATE_NORMAL, SK_ColorWHITE);
79 SetTextColor(views::Button::STATE_HOVERED, SK_ColorWHITE);
80 SetTextColor(views::Button::STATE_PRESSED, SK_ColorWHITE);
81 SetTextShadows(gfx::ShadowValues(10,
82 gfx::ShadowValue(gfx::Point(), 1.0f, SK_ColorDKGRAY)));
83 SetTextSubpixelRenderingEnabled(false);
84
85 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
86 if (button_style == THEMED_BUTTON) {
87 const int kNormalImageSet[] = IMAGE_GRID(IDR_AVATAR_THEMED_BUTTON_NORMAL);
88 const int kHotImageSet[] = IMAGE_GRID(IDR_AVATAR_THEMED_BUTTON_HOVER);
89 const int kPushedImageSet[] = IMAGE_GRID(IDR_AVATAR_THEMED_BUTTON_PRESSED);
90
91 SetBorder(CreateBorder(kNormalImageSet, kHotImageSet, kPushedImageSet));
92 set_menu_marker(
93 rb->GetImageNamed(IDR_AVATAR_THEMED_BUTTON_DROPARROW).ToImageSkia());
94 #if defined(OS_WIN)
95 } else if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
96 const int kNormalImageSet[] = IMAGE_GRID(IDR_AVATAR_METRO_BUTTON_NORMAL);
97 const int kHotImageSet[] = IMAGE_GRID(IDR_AVATAR_METRO_BUTTON_HOVER);
98 const int kPushedImageSet[] = IMAGE_GRID(IDR_AVATAR_METRO_BUTTON_PRESSED);
99
100 SetBorder(CreateBorder(kNormalImageSet, kHotImageSet, kPushedImageSet));
101 set_menu_marker(
102 rb->GetImageNamed(IDR_AVATAR_METRO_BUTTON_DROPARROW).ToImageSkia());
103 #endif
104 } else {
105 const int kNormalImageSet[] = IMAGE_GRID(IDR_AVATAR_GLASS_BUTTON_NORMAL);
106 const int kHotImageSet[] = IMAGE_GRID(IDR_AVATAR_GLASS_BUTTON_HOVER);
107 const int kPushedImageSet[] = IMAGE_GRID(IDR_AVATAR_GLASS_BUTTON_PRESSED);
108
109 SetBorder(CreateBorder(kNormalImageSet, kHotImageSet, kPushedImageSet));
110 set_menu_marker(
111 rb->GetImageNamed(IDR_AVATAR_GLASS_BUTTON_DROPARROW).ToImageSkia());
112 }
113
114 g_browser_process->profile_manager()->GetProfileInfoCache().AddObserver(this);
115
116 // Subscribe to authentication error changes so that the avatar button can
117 // update itself. Note that guest mode profiles won't have a token service.
118 SigninErrorController* error =
119 profiles::GetSigninErrorController(browser_->profile());
120 if (error) {
121 error->AddObserver(this);
122 OnErrorChanged();
123 }
124
125 SchedulePaint();
126 }
127
~NewAvatarButton()128 NewAvatarButton::~NewAvatarButton() {
129 g_browser_process->profile_manager()->
130 GetProfileInfoCache().RemoveObserver(this);
131 SigninErrorController* error =
132 profiles::GetSigninErrorController(browser_->profile());
133 if (error)
134 error->RemoveObserver(this);
135 }
136
OnProfileAdded(const base::FilePath & profile_path)137 void NewAvatarButton::OnProfileAdded(const base::FilePath& profile_path) {
138 UpdateAvatarButtonAndRelayoutParent();
139 }
140
OnProfileWasRemoved(const base::FilePath & profile_path,const base::string16 & profile_name)141 void NewAvatarButton::OnProfileWasRemoved(
142 const base::FilePath& profile_path,
143 const base::string16& profile_name) {
144 UpdateAvatarButtonAndRelayoutParent();
145 }
146
OnProfileNameChanged(const base::FilePath & profile_path,const base::string16 & old_profile_name)147 void NewAvatarButton::OnProfileNameChanged(
148 const base::FilePath& profile_path,
149 const base::string16& old_profile_name) {
150 UpdateAvatarButtonAndRelayoutParent();
151 }
152
OnProfileSupervisedUserIdChanged(const base::FilePath & profile_path)153 void NewAvatarButton::OnProfileSupervisedUserIdChanged(
154 const base::FilePath& profile_path) {
155 UpdateAvatarButtonAndRelayoutParent();
156 }
157
OnErrorChanged()158 void NewAvatarButton::OnErrorChanged() {
159 gfx::ImageSkia icon;
160
161 // If there is an error, show an warning icon.
162 const SigninErrorController* error =
163 profiles::GetSigninErrorController(browser_->profile());
164 if (error && error->HasError()) {
165 icon = *ui::ResourceBundle::GetSharedInstance().GetImageNamed(
166 IDR_ICON_PROFILES_AVATAR_BUTTON_ERROR).ToImageSkia();
167 }
168
169 SetImage(views::Button::STATE_NORMAL, icon);
170 UpdateAvatarButtonAndRelayoutParent();
171 }
172
UpdateAvatarButtonAndRelayoutParent()173 void NewAvatarButton::UpdateAvatarButtonAndRelayoutParent() {
174 // We want the button to resize if the new text is shorter.
175 SetText(GetButtonText(browser_->profile()));
176 set_min_size(gfx::Size());
177 InvalidateLayout();
178
179 // Because the width of the button might have changed, the parent browser
180 // frame needs to recalculate the button bounds and redraw it.
181 if (parent())
182 parent()->Layout();
183 }
184