1 // Copyright 2013 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 #include "chrome/browser/ui/views/profile_chooser_view.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/browser_process.h"
9 #include "chrome/browser/profiles/profile_info_util.h"
10 #include "chrome/browser/profiles/profile_manager.h"
11 #include "chrome/browser/profiles/profile_window.h"
12 #include "chrome/browser/profiles/profiles_state.h"
13 #include "chrome/browser/signin/mutable_profile_oauth2_token_service.h"
14 #include "chrome/browser/signin/profile_oauth2_token_service.h"
15 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
16 #include "chrome/browser/signin/signin_manager.h"
17 #include "chrome/browser/signin/signin_manager_factory.h"
18 #include "chrome/browser/signin/signin_promo.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_dialogs.h"
21 #include "chrome/browser/ui/singleton_tabs.h"
22 #include "chrome/browser/ui/views/user_manager_view.h"
23 #include "chrome/common/url_constants.h"
24 #include "grit/chromium_strings.h"
25 #include "grit/generated_resources.h"
26 #include "grit/theme_resources.h"
27 #include "third_party/skia/include/core/SkColor.h"
28 #include "ui/base/l10n/l10n_util.h"
29 #include "ui/base/resource/resource_bundle.h"
30 #include "ui/gfx/canvas.h"
31 #include "ui/gfx/image/image.h"
32 #include "ui/gfx/image/image_skia.h"
33 #include "ui/gfx/text_elider.h"
34 #include "ui/native_theme/native_theme.h"
35 #include "ui/views/controls/button/blue_button.h"
36 #include "ui/views/controls/button/label_button.h"
37 #include "ui/views/controls/button/menu_button.h"
38 #include "ui/views/controls/label.h"
39 #include "ui/views/controls/link.h"
40 #include "ui/views/controls/separator.h"
41 #include "ui/views/controls/textfield/textfield.h"
42 #include "ui/views/controls/webview/webview.h"
43 #include "ui/views/layout/grid_layout.h"
44 #include "ui/views/layout/layout_constants.h"
45 #include "ui/views/widget/widget.h"
49 // Helpers --------------------------------------------------------------------
51 const int kFixedMenuWidth = 250;
52 const int kButtonHeight = 29;
54 // Creates a GridLayout with a single column. This ensures that all the child
55 // views added get auto-expanded to fill the full width of the bubble.
56 views::GridLayout* CreateSingleColumnLayout(views::View* view, int width) {
57 views::GridLayout* layout = new views::GridLayout(view);
58 view->SetLayoutManager(layout);
60 views::ColumnSet* columns = layout->AddColumnSet(0);
61 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
62 views::GridLayout::FIXED, width, width);
66 // Creates a GridLayout with two columns.
67 views::GridLayout* CreateDoubleColumnLayout(views::View* view) {
68 views::GridLayout* layout = new views::GridLayout(view);
69 view->SetLayoutManager(layout);
71 views::ColumnSet* columns = layout->AddColumnSet(0);
72 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
73 views::GridLayout::USE_PREF, 0, 0);
74 columns->AddPaddingColumn(0, views::kUnrelatedControlLargeHorizontalSpacing);
75 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
76 views::GridLayout::USE_PREF, 0, 0);
80 views::Link* CreateLink(const base::string16& link_text,
81 views::LinkListener* listener) {
82 views::Link* link_button = new views::Link(link_text);
83 link_button->SetHorizontalAlignment(gfx::ALIGN_LEFT);
84 link_button->SetUnderline(false);
85 link_button->set_listener(listener);
90 // BackgroundColorHoverButton -------------------------------------------------
92 // A custom button that allows for setting a background color when hovered over.
93 class BackgroundColorHoverButton : public views::LabelButton {
95 BackgroundColorHoverButton(views::ButtonListener* listener,
96 const base::string16& text,
97 const gfx::ImageSkia& normal_icon,
98 const gfx::ImageSkia& hover_icon);
99 virtual ~BackgroundColorHoverButton();
102 // views::LabelButton:
103 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
105 DISALLOW_COPY_AND_ASSIGN(BackgroundColorHoverButton);
108 BackgroundColorHoverButton::BackgroundColorHoverButton(
109 views::ButtonListener* listener,
110 const base::string16& text,
111 const gfx::ImageSkia& normal_icon,
112 const gfx::ImageSkia& hover_icon)
113 : views::LabelButton(listener, text) {
114 SetBorder(views::Border::CreateEmptyBorder(0, views::kButtonHEdgeMarginNew,
115 0, views::kButtonHEdgeMarginNew));
116 set_min_size(gfx::Size(0, kButtonHeight));
117 SetImage(STATE_NORMAL, normal_icon);
118 SetImage(STATE_HOVERED, hover_icon);
119 SetImage(STATE_PRESSED, hover_icon);
120 SetTextColor(STATE_HOVERED, GetNativeTheme()->GetSystemColor(
121 ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor));
124 BackgroundColorHoverButton::~BackgroundColorHoverButton() {}
126 void BackgroundColorHoverButton::OnPaint(gfx::Canvas* canvas) {
127 if ((state() == STATE_PRESSED) || (state() == STATE_HOVERED) || HasFocus()) {
128 canvas->DrawColor(GetNativeTheme()->GetSystemColor(
129 ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor));
131 LabelButton::OnPaint(canvas);
137 // EditableProfilePhoto -------------------------------------------------
139 // A custom Image control that shows a "change" button when moused over.
140 class EditableProfilePhoto : public views::ImageView {
142 EditableProfilePhoto(views::ButtonListener* listener,
143 const gfx::Image& icon,
144 bool is_editing_allowed)
145 : views::ImageView(),
146 change_photo_button_(NULL) {
147 const int kLargeImageSide = 64;
148 gfx::Image image = profiles::GetSizedAvatarIconWithBorder(
150 kLargeImageSide + profiles::kAvatarIconPadding,
151 kLargeImageSide + profiles::kAvatarIconPadding);
152 SetImage(image.ToImageSkia());
154 if (!is_editing_allowed)
157 set_notify_enter_exit_on_child(true);
159 // Button overlay that appears when hovering over the image.
160 change_photo_button_ = new views::LabelButton(listener,
161 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_CHANGE_PHOTO_BUTTON));
162 change_photo_button_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
163 change_photo_button_->SetBorder(views::Border::NullBorder());
164 const SkColor color = SK_ColorWHITE;
165 change_photo_button_->SetTextColor(views::Button::STATE_NORMAL, color);
166 change_photo_button_->SetTextColor(views::Button::STATE_HOVERED, color);
168 const SkColor kBackgroundColor = SkColorSetARGB(125, 0, 0, 0);
169 change_photo_button_->set_background(
170 views::Background::CreateSolidBackground(kBackgroundColor));
171 // Need to take into account the border padding on the avatar.
172 const int kOverlayHeight = 20;
173 change_photo_button_->SetBounds(
174 profiles::kAvatarIconPadding,
175 kLargeImageSide - kOverlayHeight,
176 kLargeImageSide - profiles::kAvatarIconPadding,
178 change_photo_button_->SetVisible(false);
179 AddChildView(change_photo_button_);
182 views::LabelButton* change_photo_button() { return change_photo_button_; }
186 virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE {
187 if (change_photo_button_)
188 change_photo_button_->SetVisible(true);
191 virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE {
192 if (change_photo_button_)
193 change_photo_button_->SetVisible(false);
196 // Button that is shown when hovering over the image view. Can be NULL if
197 // the photo isn't allowed to be edited (e.g. for guest profiles).
198 views::LabelButton* change_photo_button_;
200 DISALLOW_COPY_AND_ASSIGN(EditableProfilePhoto);
204 // EditableProfileName -------------------------------------------------
206 // A custom text control that turns into a textfield for editing when clicked.
207 class EditableProfileName : public views::LabelButton,
208 public views::ButtonListener {
210 EditableProfileName(views::TextfieldController* controller,
211 const base::string16& text,
212 bool is_editing_allowed)
213 : views::LabelButton(this, text),
214 profile_name_textfield_(NULL) {
215 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
216 const gfx::FontList& medium_font_list =
217 rb->GetFontList(ui::ResourceBundle::MediumFont);
218 SetFontList(medium_font_list);
219 SetBorder(views::Border::NullBorder());
221 if (!is_editing_allowed)
224 SetImage(STATE_HOVERED,
225 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_HOVER));
226 SetImage(STATE_PRESSED,
227 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_PRESSED));
229 // Textfield that overlaps the button.
230 profile_name_textfield_ = new views::Textfield();
231 profile_name_textfield_->set_controller(controller);
232 profile_name_textfield_->SetFontList(medium_font_list);
233 profile_name_textfield_->SetVisible(false);
234 AddChildView(profile_name_textfield_);
237 views::Textfield* profile_name_textfield() {
238 return profile_name_textfield_;
241 // Hide the editable textfield to show the profile name button instead.
242 void ShowReadOnlyView() {
243 if (profile_name_textfield_)
244 profile_name_textfield_->SetVisible(false);
248 // views::ButtonListener:
249 virtual void ButtonPressed(views::Button* sender,
250 const ui::Event& event) OVERRIDE {
251 if (profile_name_textfield_) {
252 profile_name_textfield_->SetVisible(true);
253 profile_name_textfield_->SetText(GetText());
254 profile_name_textfield_->SelectAll(false);
255 profile_name_textfield_->RequestFocus();
259 // views::LabelButton:
260 virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE {
261 // Override CustomButton's implementation, which presses the button when
262 // you press space and clicks it when you release space, as the space can be
263 // part of the new profile name typed in the textfield.
267 virtual void Layout() OVERRIDE {
268 if (profile_name_textfield_)
269 profile_name_textfield_->SetBounds(0, 0, width(), height());
270 // This layout trick keeps the text left-aligned and the icon right-aligned.
271 SetHorizontalAlignment(gfx::ALIGN_RIGHT);
272 views::LabelButton::Layout();
273 label()->SetHorizontalAlignment(gfx::ALIGN_LEFT);
276 // Button that is shown when hovering over the image view. Can be NULL if
277 // the profile name isn't allowed to be edited (e.g. for guest profiles).
278 views::Textfield* profile_name_textfield_;
280 DISALLOW_COPY_AND_ASSIGN(EditableProfileName);
284 // ProfileChooserView ---------------------------------------------------------
287 ProfileChooserView* ProfileChooserView::profile_bubble_ = NULL;
288 bool ProfileChooserView::close_on_deactivate_for_testing_ = true;
291 void ProfileChooserView::ShowBubble(
292 views::View* anchor_view,
293 views::BubbleBorder::Arrow arrow,
294 views::BubbleBorder::BubbleAlignment border_alignment,
295 const gfx::Rect& anchor_rect,
298 // TODO(bcwhite): handle case where we should show on different window
301 profile_bubble_ = new ProfileChooserView(
302 anchor_view, arrow, anchor_rect, browser);
303 views::BubbleDelegateView::CreateBubble(profile_bubble_);
304 profile_bubble_->set_close_on_deactivate(close_on_deactivate_for_testing_);
305 profile_bubble_->SetAlignment(border_alignment);
306 profile_bubble_->GetWidget()->Show();
307 profile_bubble_->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
311 bool ProfileChooserView::IsShowing() {
312 return profile_bubble_ != NULL;
316 void ProfileChooserView::Hide() {
318 profile_bubble_->GetWidget()->Close();
321 ProfileChooserView::ProfileChooserView(views::View* anchor_view,
322 views::BubbleBorder::Arrow arrow,
323 const gfx::Rect& anchor_rect,
325 : BubbleDelegateView(anchor_view, arrow),
327 view_mode_(PROFILE_CHOOSER_VIEW) {
328 // Reset the default margins inherited from the BubbleDelegateView.
329 set_margins(gfx::Insets());
333 avatar_menu_.reset(new AvatarMenu(
334 &g_browser_process->profile_manager()->GetProfileInfoCache(),
337 avatar_menu_->RebuildMenu();
339 ProfileOAuth2TokenService* oauth2_token_service =
340 ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile());
341 if (oauth2_token_service)
342 oauth2_token_service->AddObserver(this);
345 ProfileChooserView::~ProfileChooserView() {
346 ProfileOAuth2TokenService* oauth2_token_service =
347 ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile());
348 if (oauth2_token_service)
349 oauth2_token_service->RemoveObserver(this);
352 void ProfileChooserView::ResetView() {
353 manage_accounts_link_ = NULL;
354 signout_current_profile_link_ = NULL;
355 signin_current_profile_link_ = NULL;
356 guest_button_ = NULL;
357 end_guest_button_ = NULL;
358 users_button_ = NULL;
359 add_user_button_ = NULL;
360 add_account_button_ = NULL;
361 current_profile_photo_ = NULL;
362 current_profile_name_ = NULL;
363 open_other_profile_indexes_map_.clear();
364 current_profile_accounts_map_.clear();
367 void ProfileChooserView::Init() {
368 ShowView(PROFILE_CHOOSER_VIEW, avatar_menu_.get());
371 void ProfileChooserView::OnAvatarMenuChanged(
372 AvatarMenu* avatar_menu) {
373 // Refresh the view with the new menu. We can't just update the local copy
374 // as this may have been triggered by a sign out action, in which case
375 // the view is being destroyed.
376 ShowView(PROFILE_CHOOSER_VIEW, avatar_menu);
379 void ProfileChooserView::OnRefreshTokenAvailable(
380 const std::string& account_id) {
381 // Refresh the account management view when a new account is added to the
383 if (view_mode_ == ACCOUNT_MANAGEMENT_VIEW ||
384 view_mode_ == GAIA_SIGNIN_VIEW ||
385 view_mode_ == GAIA_ADD_ACCOUNT_VIEW) {
386 ShowView(ACCOUNT_MANAGEMENT_VIEW, avatar_menu_.get());
390 void ProfileChooserView::OnRefreshTokenRevoked(const std::string& account_id) {
391 // Refresh the account management view when an account is removed from the
393 if (view_mode_ == ACCOUNT_MANAGEMENT_VIEW)
394 ShowView(ACCOUNT_MANAGEMENT_VIEW, avatar_menu_.get());
397 void ProfileChooserView::ShowView(BubbleViewMode view_to_display,
398 AvatarMenu* avatar_menu) {
399 // The account management view should only be displayed if the active profile
401 if (view_to_display == ACCOUNT_MANAGEMENT_VIEW) {
402 const AvatarMenu::Item& active_item = avatar_menu->GetItemAt(
403 avatar_menu->GetActiveProfileIndex());
404 DCHECK(active_item.signed_in);
408 RemoveAllChildViews(true);
409 view_mode_ = view_to_display;
411 if (view_to_display == GAIA_SIGNIN_VIEW ||
412 view_to_display == GAIA_ADD_ACCOUNT_VIEW) {
413 // Minimum size for embedded sign in pages as defined in Gaia.
414 const int kMinGaiaViewWidth = 320;
415 const int kMinGaiaViewHeight = 440;
416 Profile* profile = browser_->profile();
417 views::WebView* web_view = new views::WebView(profile);
418 signin::Source source = (view_to_display == GAIA_SIGNIN_VIEW) ?
419 signin::SOURCE_AVATAR_BUBBLE_SIGN_IN :
420 signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT;
421 GURL url(signin::GetPromoURL(
422 source, false /* auto_close */, true /* is_constrained */));
423 web_view->LoadInitialURL(url);
424 views::GridLayout* layout =
425 CreateSingleColumnLayout(this, kMinGaiaViewWidth);
426 layout->StartRow(1, 0);
427 layout->AddView(web_view);
428 layout->set_minimum_size(
429 gfx::Size(kMinGaiaViewWidth, kMinGaiaViewHeight));
431 if (GetBubbleFrameView())
436 views::GridLayout* layout = CreateSingleColumnLayout(this, kFixedMenuWidth);
437 // Separate items into active and alternatives.
438 Indexes other_profiles;
439 bool is_guest_view = true;
440 views::View* current_profile_view = NULL;
441 views::View* current_profile_accounts = NULL;
442 for (size_t i = 0; i < avatar_menu->GetNumberOfItems(); ++i) {
443 const AvatarMenu::Item& item = avatar_menu->GetItemAt(i);
445 if (view_to_display == PROFILE_CHOOSER_VIEW) {
446 current_profile_view = CreateCurrentProfileView(item, false);
448 current_profile_view = CreateCurrentProfileEditableView(item);
449 current_profile_accounts = CreateCurrentProfileAccountsView(item);
451 is_guest_view = false;
453 other_profiles.push_back(i);
457 if (!current_profile_view) // Guest windows don't have an active profile.
458 current_profile_view = CreateGuestProfileView();
460 layout->StartRow(1, 0);
461 layout->AddView(current_profile_view);
463 if (view_to_display == PROFILE_CHOOSER_VIEW) {
464 layout->StartRow(1, 0);
465 layout->AddView(CreateOtherProfilesView(other_profiles));
467 DCHECK(current_profile_accounts);
468 layout->StartRow(0, 0);
469 layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
470 layout->StartRow(1, 0);
471 layout->AddView(current_profile_accounts);
474 layout->StartRow(0, 0);
475 layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
478 views::View* option_buttons_view = CreateOptionsView(is_guest_view);
479 layout->StartRow(0, 0);
480 layout->AddView(option_buttons_view);
483 if (GetBubbleFrameView())
487 void ProfileChooserView::WindowClosing() {
488 DCHECK_EQ(profile_bubble_, this);
489 profile_bubble_ = NULL;
492 void ProfileChooserView::ButtonPressed(views::Button* sender,
493 const ui::Event& event) {
494 // Disable button after clicking so that it doesn't get clicked twice and
495 // start a second action... which can crash Chrome. But don't disable if it
496 // has no parent (like in tests) because that will also crash.
497 if (sender->parent())
498 sender->SetEnabled(false);
500 if (sender == guest_button_) {
501 profiles::SwitchToGuestProfile(browser_->host_desktop_type(),
502 profiles::ProfileSwitchingDoneCallback());
503 } else if (sender == end_guest_button_) {
504 profiles::CloseGuestProfileWindows();
505 } else if (sender == users_button_) {
506 // Only non-guest users appear in the User Manager.
507 base::FilePath profile_path;
508 if (!end_guest_button_) {
509 size_t active_index = avatar_menu_->GetActiveProfileIndex();
510 profile_path = avatar_menu_->GetItemAt(active_index).profile_path;
512 chrome::ShowUserManager(profile_path);
513 } else if (sender == add_user_button_) {
514 profiles::CreateAndSwitchToNewProfile(
515 browser_->host_desktop_type(),
516 profiles::ProfileSwitchingDoneCallback(),
517 ProfileMetrics::ADD_NEW_USER_ICON);
518 } else if (sender == add_account_button_) {
519 ShowView(GAIA_ADD_ACCOUNT_VIEW, avatar_menu_.get());
520 } else if (sender == current_profile_photo_->change_photo_button()) {
521 avatar_menu_->EditProfile(avatar_menu_->GetActiveProfileIndex());
523 // One of the "other profiles" buttons was pressed.
524 ButtonIndexes::const_iterator match =
525 open_other_profile_indexes_map_.find(sender);
526 DCHECK(match != open_other_profile_indexes_map_.end());
527 avatar_menu_->SwitchToProfile(
529 ui::DispositionFromEventFlags(event.flags()) == NEW_WINDOW,
530 ProfileMetrics::SWITCH_PROFILE_ICON);
534 void ProfileChooserView::OnMenuButtonClicked(views::View* source,
535 const gfx::Point& point) {
536 AccountButtonIndexes::const_iterator match =
537 current_profile_accounts_map_.find(source);
538 DCHECK(match != current_profile_accounts_map_.end());
540 MutableProfileOAuth2TokenService* oauth2_token_service =
541 ProfileOAuth2TokenServiceFactory::GetPlatformSpecificForProfile(
542 browser_->profile());
543 if (oauth2_token_service)
544 oauth2_token_service->RevokeCredentials(match->second);
547 void ProfileChooserView::LinkClicked(views::Link* sender, int event_flags) {
548 if (sender == manage_accounts_link_) {
549 // ShowView() will DCHECK if this view is displayed for non signed-in users.
550 ShowView(ACCOUNT_MANAGEMENT_VIEW, avatar_menu_.get());
551 } else if (sender == signout_current_profile_link_) {
552 profiles::LockProfile(browser_->profile());
554 DCHECK(sender == signin_current_profile_link_);
555 ShowView(GAIA_SIGNIN_VIEW, avatar_menu_.get());
559 bool ProfileChooserView::HandleKeyEvent(views::Textfield* sender,
560 const ui::KeyEvent& key_event) {
561 views::Textfield* name_textfield =
562 current_profile_name_->profile_name_textfield();
563 DCHECK(sender == name_textfield);
565 if (key_event.key_code() == ui::VKEY_RETURN ||
566 key_event.key_code() == ui::VKEY_TAB) {
567 // Pressing Tab/Enter commits the new profile name, unless it's empty.
568 base::string16 new_profile_name = name_textfield->text();
569 if (new_profile_name.empty())
572 const AvatarMenu::Item& active_item = avatar_menu_->GetItemAt(
573 avatar_menu_->GetActiveProfileIndex());
574 Profile* profile = g_browser_process->profile_manager()->GetProfile(
575 active_item.profile_path);
578 if (profile->IsManaged())
581 profiles::UpdateProfileName(profile, new_profile_name);
582 current_profile_name_->ShowReadOnlyView();
588 views::View* ProfileChooserView::CreateCurrentProfileView(
589 const AvatarMenu::Item& avatar_item,
591 views::View* view = new views::View();
592 views::GridLayout* layout = CreateDoubleColumnLayout(view);
593 layout->SetInsets(views::kButtonVEdgeMarginNew,
594 views::kButtonHEdgeMarginNew,
595 views::kButtonVEdgeMarginNew,
596 views::kButtonHEdgeMarginNew);
598 current_profile_photo_ =
599 new EditableProfilePhoto(this, avatar_item.icon, !is_guest);
600 view->SetBoundsRect(current_profile_photo_->bounds());
601 current_profile_name_ =
602 new EditableProfileName(this, avatar_item.name, !is_guest);
604 layout->StartRow(1, 0);
605 layout->AddView(current_profile_photo_, 1, 3);
606 layout->AddView(current_profile_name_);
609 layout->StartRow(1, 0);
610 layout->SkipColumns(1);
611 layout->StartRow(1, 0);
612 layout->SkipColumns(1);
613 } else if (avatar_item.signed_in) {
614 manage_accounts_link_ = CreateLink(
615 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_MANAGE_ACCOUNTS_BUTTON),
617 signout_current_profile_link_ = CreateLink(
618 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_SIGNOUT_BUTTON), this);
619 layout->StartRow(1, 0);
620 layout->SkipColumns(1);
621 layout->AddView(signout_current_profile_link_);
622 layout->StartRow(1, 0);
623 layout->SkipColumns(1);
624 layout->AddView(manage_accounts_link_);
626 signin_current_profile_link_ = CreateLink(
627 l10n_util::GetStringFUTF16(
628 IDS_SYNC_START_SYNC_BUTTON_LABEL,
629 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)),
631 layout->StartRow(1, 0);
632 layout->SkipColumns(1);
633 layout->AddView(signin_current_profile_link_);
634 layout->StartRow(1, 0);
635 layout->SkipColumns(1);
641 views::View* ProfileChooserView::CreateCurrentProfileEditableView(
642 const AvatarMenu::Item& avatar_item) {
643 DCHECK(avatar_item.signed_in);
644 views::View* view = new views::View();
645 views::GridLayout* layout = CreateDoubleColumnLayout(view);
646 layout->SetInsets(views::kButtonVEdgeMarginNew,
647 views::kButtonHEdgeMarginNew,
648 views::kButtonVEdgeMarginNew,
649 views::kButtonHEdgeMarginNew);
651 current_profile_photo_ =
652 new EditableProfilePhoto(this, avatar_item.icon, true);
653 view->SetBoundsRect(current_profile_photo_->bounds());
654 current_profile_name_ =
655 new EditableProfileName(this, avatar_item.name, true);
657 layout->StartRow(1, 0);
658 layout->AddView(current_profile_photo_, 1, 3);
659 layout->AddView(current_profile_name_);
661 layout->StartRow(1, 0);
662 layout->SkipColumns(1);
664 layout->StartRow(1, 0);
665 layout->SkipColumns(1);
669 views::View* ProfileChooserView::CreateGuestProfileView() {
670 gfx::Image guest_icon =
671 ui::ResourceBundle::GetSharedInstance().GetImageNamed(IDR_LOGIN_GUEST);
672 AvatarMenu::Item guest_avatar_item(0, 0, guest_icon);
673 guest_avatar_item.active = true;
674 guest_avatar_item.name = l10n_util::GetStringUTF16(
675 IDS_PROFILES_GUEST_PROFILE_NAME);
676 guest_avatar_item.signed_in = false;
678 return CreateCurrentProfileView(guest_avatar_item, true);
681 views::View* ProfileChooserView::CreateOtherProfilesView(
682 const Indexes& avatars_to_show) {
683 views::View* view = new views::View();
684 views::GridLayout* layout = CreateSingleColumnLayout(
685 view, kFixedMenuWidth - 2 * views::kButtonHEdgeMarginNew);
686 layout->SetInsets(0, views::kButtonHEdgeMarginNew,
687 views::kButtonVEdgeMarginNew, views::kButtonHEdgeMarginNew);
688 int num_avatars_to_show = avatars_to_show.size();
689 for (int i = 0; i < num_avatars_to_show; ++i) {
690 const size_t index = avatars_to_show[i];
691 const AvatarMenu::Item& item = avatar_menu_->GetItemAt(index);
692 const int kSmallImageSide = 32;
694 gfx::Image image = profiles::GetSizedAvatarIconWithBorder(
696 kSmallImageSide + profiles::kAvatarIconPadding,
697 kSmallImageSide + profiles::kAvatarIconPadding);
699 views::LabelButton* button = new views::LabelButton(this, item.name);
700 open_other_profile_indexes_map_[button] = index;
701 button->SetImage(views::Button::STATE_NORMAL, *image.ToImageSkia());
702 button->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
703 ui::ResourceBundle::MediumFont));
704 button->SetBorder(views::Border::NullBorder());
706 layout->StartRow(1, 0);
707 layout->AddView(button);
709 // The last avatar in the list does not need any bottom padding.
710 if (i < num_avatars_to_show - 1)
711 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
717 views::View* ProfileChooserView::CreateOptionsView(bool is_guest_view) {
718 views::View* view = new views::View();
719 views::GridLayout* layout = CreateSingleColumnLayout(view, kFixedMenuWidth);
720 // The horizontal padding will be set by each button individually, so that
721 // in the hovered state the button spans the entire parent view.
722 layout->SetInsets(views::kRelatedControlVerticalSpacing, 0,
723 views::kRelatedControlVerticalSpacing, 0);
725 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
727 layout->StartRow(1, 0);
729 end_guest_button_ = new BackgroundColorHoverButton(
731 l10n_util::GetStringUTF16(IDS_PROFILES_EXIT_GUEST_BUTTON),
732 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_BROWSE_GUEST),
733 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_BROWSE_GUEST_WHITE));
734 layout->AddView(end_guest_button_);
736 guest_button_ = new BackgroundColorHoverButton(
738 l10n_util::GetStringUTF16(IDS_PROFILES_GUEST_BUTTON),
739 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_BROWSE_GUEST),
740 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_BROWSE_GUEST_WHITE));
741 layout->AddView(guest_button_);
744 add_user_button_ = new BackgroundColorHoverButton(
746 l10n_util::GetStringUTF16(IDS_PROFILES_ADD_PERSON_BUTTON),
747 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_ADD_USER),
748 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_ADD_USER_WHITE));
749 layout->StartRow(1, 0);
750 layout->AddView(add_user_button_);
752 users_button_ = new BackgroundColorHoverButton(
754 l10n_util::GetStringUTF16(IDS_PROFILES_ALL_PEOPLE_BUTTON),
755 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_ADD_USER),
756 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_ADD_USER_WHITE));
757 layout->StartRow(1, 0);
758 layout->AddView(users_button_);
763 views::View* ProfileChooserView::CreateCurrentProfileAccountsView(
764 const AvatarMenu::Item& avatar_item) {
765 DCHECK(avatar_item.signed_in);
766 views::View* view = new views::View();
767 int column_width = kFixedMenuWidth - 2 * views::kButtonHEdgeMarginNew;
768 views::GridLayout* layout = CreateSingleColumnLayout(view, column_width);
769 layout->SetInsets(views::kButtonVEdgeMarginNew,
770 views::kButtonHEdgeMarginNew,
771 views::kButtonVEdgeMarginNew,
772 views::kButtonHEdgeMarginNew);
774 Profile* profile = browser_->profile();
775 std::string primary_account =
776 SigninManagerFactory::GetForProfile(profile)->GetAuthenticatedUsername();
777 DCHECK(!primary_account.empty());
778 std::vector<std::string>accounts =
779 profiles::GetSecondaryAccountsForProfile(profile, primary_account);
781 // The primary account should always be listed first.
782 // TODO(rogerta): we still need to further differentiate the primary account
783 // from the others in the UI, so more work is likely required here:
785 CreateAccountButton(layout, primary_account, true, column_width);
786 for (size_t i = 0; i < accounts.size(); ++i)
787 CreateAccountButton(layout, accounts[i], false, column_width);
788 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
790 add_account_button_ = new views::BlueButton(
792 l10n_util::GetStringFUTF16(IDS_PROFILES_PROFILE_ADD_ACCOUNT_BUTTON,
794 layout->StartRow(1, 0);
795 layout->AddView(add_account_button_);
799 void ProfileChooserView::CreateAccountButton(views::GridLayout* layout,
800 const std::string& account,
801 bool is_primary_account,
803 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
804 const gfx::ImageSkia* menu_marker =
805 rb->GetImageNamed(IDR_CLOSE_1).ToImageSkia();
806 // Use a MenuButtonListener and not a regular ButtonListener to be
807 // able to distinguish between the unnamed "other profile" buttons and the
808 // unnamed "multiple accounts" buttons.
809 views::MenuButton* email_button = new views::MenuButton(
811 gfx::ElideEmail(base::UTF8ToUTF16(account),
812 rb->GetFontList(ui::ResourceBundle::BaseFont),
813 width - menu_marker->width()),
814 is_primary_account ? NULL : this, // Cannot delete the primary account.
815 !is_primary_account);
816 email_button->SetBorder(views::Border::CreateEmptyBorder(0, 0, 0, 0));
817 if (!is_primary_account) {
818 email_button->set_menu_marker(menu_marker);
819 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
821 layout->StartRow(1, 0);
822 layout->AddView(email_button);
824 // Save the original email address, as the button text could be elided.
825 current_profile_accounts_map_[email_button] = account;