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/image/image.h"
31 #include "ui/gfx/image/image_skia.h"
32 #include "ui/gfx/text_elider.h"
33 #include "ui/views/controls/button/blue_button.h"
34 #include "ui/views/controls/button/menu_button.h"
35 #include "ui/views/controls/label.h"
36 #include "ui/views/controls/link.h"
37 #include "ui/views/controls/separator.h"
38 #include "ui/views/controls/textfield/textfield.h"
39 #include "ui/views/controls/webview/webview.h"
40 #include "ui/views/layout/grid_layout.h"
41 #include "ui/views/layout/layout_constants.h"
42 #include "ui/views/widget/widget.h"
45 #include "ui/native_theme/native_theme_aura.h"
50 // Helpers --------------------------------------------------------------------
52 const int kMinMenuWidth = 250;
53 const int kButtonHeight = 29;
55 // Creates a GridLayout with a single column. This ensures that all the child
56 // views added get auto-expanded to fill the full width of the bubble.
57 views::GridLayout* CreateSingleColumnLayout(views::View* view) {
58 views::GridLayout* layout = new views::GridLayout(view);
59 view->SetLayoutManager(layout);
61 views::ColumnSet* columns = layout->AddColumnSet(0);
62 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
63 views::GridLayout::USE_PREF, 0, 0);
67 // Creates a GridLayout with two columns.
68 views::GridLayout* CreateDoubleColumnLayout(views::View* view) {
69 views::GridLayout* layout = new views::GridLayout(view);
70 view->SetLayoutManager(layout);
72 views::ColumnSet* columns = layout->AddColumnSet(0);
73 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
74 views::GridLayout::USE_PREF, 0, 0);
75 columns->AddPaddingColumn(0, views::kUnrelatedControlLargeHorizontalSpacing);
76 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
77 views::GridLayout::USE_PREF, 0, 0);
81 views::Link* CreateLink(const base::string16& link_text,
82 views::LinkListener* listener) {
83 views::Link* link_button = new views::Link(link_text);
84 link_button->SetHorizontalAlignment(gfx::ALIGN_LEFT);
85 link_button->SetUnderline(false);
86 link_button->set_listener(listener);
91 // BackgroundColorHoverButton -------------------------------------------------
93 // A custom button that allows for setting a background color when hovered over.
94 class BackgroundColorHoverButton : public views::TextButton {
96 BackgroundColorHoverButton(views::ButtonListener* listener,
97 const base::string16& text,
98 const gfx::ImageSkia& normal_icon,
99 const gfx::ImageSkia& hover_icon);
100 virtual ~BackgroundColorHoverButton();
103 // views::TextButton:
104 virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE;
105 virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE;
107 void OnHighlightStateChanged();
109 DISALLOW_COPY_AND_ASSIGN(BackgroundColorHoverButton);
112 BackgroundColorHoverButton::BackgroundColorHoverButton(
113 views::ButtonListener* listener,
114 const base::string16& text,
115 const gfx::ImageSkia& normal_icon,
116 const gfx::ImageSkia& hover_icon)
117 : views::TextButton(listener, text) {
118 scoped_ptr<views::TextButtonBorder> text_button_border(
119 new views::TextButtonBorder());
120 text_button_border->SetInsets(gfx::Insets(0, views::kButtonHEdgeMarginNew,
121 0, views::kButtonHEdgeMarginNew));
122 SetBorder(text_button_border.PassAs<views::Border>());
123 set_min_height(kButtonHeight);
124 set_icon_text_spacing(views::kItemLabelSpacing);
125 SetIcon(normal_icon);
126 SetHoverIcon(hover_icon);
127 SetPushedIcon(hover_icon);
128 SetHoverColor(GetNativeTheme()->GetSystemColor(
129 ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor));
130 OnHighlightStateChanged();
133 BackgroundColorHoverButton::~BackgroundColorHoverButton() {
136 void BackgroundColorHoverButton::OnMouseEntered(const ui::MouseEvent& event) {
137 views::TextButton::OnMouseEntered(event);
138 OnHighlightStateChanged();
141 void BackgroundColorHoverButton::OnMouseExited(const ui::MouseEvent& event) {
142 views::TextButton::OnMouseExited(event);
143 OnHighlightStateChanged();
146 void BackgroundColorHoverButton::OnHighlightStateChanged() {
147 bool is_highlighted = (state() == views::TextButton::STATE_PRESSED) ||
148 (state() == views::TextButton::STATE_HOVERED) || HasFocus();
149 set_background(views::Background::CreateSolidBackground(
150 GetNativeTheme()->GetSystemColor(is_highlighted ?
151 ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor :
152 ui::NativeTheme::kColorId_MenuBackgroundColor)));
159 // EditableProfilePhoto -------------------------------------------------
161 // A custom Image control that shows a "change" button when moused over.
162 class EditableProfilePhoto : public views::ImageView {
164 EditableProfilePhoto(views::ButtonListener* listener,
165 const gfx::Image& icon,
166 bool is_editing_allowed)
167 : views::ImageView(),
168 change_photo_button_(NULL) {
169 const int kLargeImageSide = 64;
170 const SkColor kBackgroundColor = SkColorSetARGB(125, 0, 0, 0);
171 const int kOverlayHeight = 20;
173 gfx::Image image = profiles::GetSizedAvatarIconWithBorder(
175 kLargeImageSide + profiles::kAvatarIconPadding,
176 kLargeImageSide + profiles::kAvatarIconPadding);
177 SetImage(image.ToImageSkia());
179 if (!is_editing_allowed)
182 set_notify_enter_exit_on_child(true);
184 // Button overlay that appears when hovering over the image.
185 change_photo_button_ = new views::TextButton(listener,
186 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_CHANGE_PHOTO_BUTTON));
187 change_photo_button_->set_alignment(views::TextButton::ALIGN_CENTER);
188 change_photo_button_->SetBorder(views::Border::NullBorder());
189 change_photo_button_->SetEnabledColor(SK_ColorWHITE);
190 change_photo_button_->SetHoverColor(SK_ColorWHITE);
192 change_photo_button_->set_background(
193 views::Background::CreateSolidBackground(kBackgroundColor));
194 // Need to take in account the border padding on the avatar.
195 change_photo_button_->SetBounds(
196 profiles::kAvatarIconPadding,
197 kLargeImageSide - kOverlayHeight,
198 kLargeImageSide - profiles::kAvatarIconPadding,
200 change_photo_button_->SetVisible(false);
201 AddChildView(change_photo_button_);
204 views::TextButton* change_photo_button() {
205 return change_photo_button_;
210 virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE {
211 if (change_photo_button_)
212 change_photo_button_->SetVisible(true);
215 virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE {
216 if (change_photo_button_)
217 change_photo_button_->SetVisible(false);
220 // Button that is shown when hovering over the image view. Can be NULL if
221 // the photo isn't allowed to be edited (e.g. for guest profiles).
222 views::TextButton* change_photo_button_;
224 DISALLOW_COPY_AND_ASSIGN(EditableProfilePhoto);
228 // EditableProfileName -------------------------------------------------
230 // A custom text control that turns into a textfield for editing when clicked.
231 class EditableProfileName : public views::TextButton,
232 public views::ButtonListener {
234 EditableProfileName(views::TextfieldController* controller,
235 const base::string16& text,
236 bool is_editing_allowed)
237 : views::TextButton(this, text),
238 profile_name_textfield_(NULL) {
239 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
240 const gfx::FontList& medium_font_list =
241 rb->GetFontList(ui::ResourceBundle::MediumFont);
242 SetFontList(medium_font_list);
243 SetBorder(views::Border::NullBorder());
245 if (!is_editing_allowed)
248 SetHoverIcon(*rb->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_HOVER));
249 SetPushedIcon(*rb->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_PRESSED));
250 set_icon_placement(views::TextButton::ICON_ON_RIGHT);
251 set_full_justification(true);
253 // Textfield that overlaps the button.
254 profile_name_textfield_ = new views::Textfield();
255 profile_name_textfield_->set_controller(controller);
256 profile_name_textfield_->SetFontList(medium_font_list);
257 profile_name_textfield_->SetVisible(false);
258 AddChildView(profile_name_textfield_);
261 views::Textfield* profile_name_textfield() {
262 return profile_name_textfield_;
265 // Hide the editable textfield and show the button displaying the profile
267 void ShowReadOnlyView() {
268 if (profile_name_textfield_)
269 profile_name_textfield_->SetVisible(false);
273 // views::ButtonListener:
274 virtual void ButtonPressed(views::Button* sender,
275 const ui::Event& event) OVERRIDE {
276 if (profile_name_textfield_) {
277 profile_name_textfield_->SetVisible(true);
278 profile_name_textfield_->SetText(text());
279 profile_name_textfield_->SelectAll(false);
280 profile_name_textfield_->RequestFocus();
284 // views::CustomButton:
285 virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE {
286 // Override CustomButton's implementation, which presses the button when
287 // you press space and clicks it when you release space, as the space can be
288 // part of the new profile name typed in the textfield.
293 virtual void Layout() OVERRIDE {
294 if (profile_name_textfield_)
295 profile_name_textfield_->SetBounds(0, 0, width(), height());
296 views::View::Layout();
299 // Button that is shown when hovering over the image view. Can be NULL if
300 // the profile name isn't allowed to be edited (e.g. for guest profiles).
301 views::Textfield* profile_name_textfield_;
303 DISALLOW_COPY_AND_ASSIGN(EditableProfileName);
307 // ProfileChooserView ---------------------------------------------------------
310 ProfileChooserView* ProfileChooserView::profile_bubble_ = NULL;
311 bool ProfileChooserView::close_on_deactivate_for_testing_ = true;
314 void ProfileChooserView::ShowBubble(
315 views::View* anchor_view,
316 views::BubbleBorder::Arrow arrow,
317 views::BubbleBorder::BubbleAlignment border_alignment,
318 const gfx::Rect& anchor_rect,
321 // TODO(bcwhite): handle case where we should show on different window
324 profile_bubble_ = new ProfileChooserView(
325 anchor_view, arrow, anchor_rect, browser);
326 views::BubbleDelegateView::CreateBubble(profile_bubble_);
327 profile_bubble_->set_close_on_deactivate(close_on_deactivate_for_testing_);
328 profile_bubble_->SetAlignment(border_alignment);
329 profile_bubble_->GetWidget()->Show();
330 profile_bubble_->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
334 bool ProfileChooserView::IsShowing() {
335 return profile_bubble_ != NULL;
339 void ProfileChooserView::Hide() {
341 profile_bubble_->GetWidget()->Close();
344 ProfileChooserView::ProfileChooserView(views::View* anchor_view,
345 views::BubbleBorder::Arrow arrow,
346 const gfx::Rect& anchor_rect,
348 : BubbleDelegateView(anchor_view, arrow),
350 view_mode_(PROFILE_CHOOSER_VIEW) {
351 // Reset the default margins inherited from the BubbleDelegateView.
352 set_margins(gfx::Insets());
356 avatar_menu_.reset(new AvatarMenu(
357 &g_browser_process->profile_manager()->GetProfileInfoCache(),
360 avatar_menu_->RebuildMenu();
362 ProfileOAuth2TokenService* oauth2_token_service =
363 ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile());
364 if (oauth2_token_service)
365 oauth2_token_service->AddObserver(this);
368 ProfileChooserView::~ProfileChooserView() {
369 ProfileOAuth2TokenService* oauth2_token_service =
370 ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile());
371 if (oauth2_token_service)
372 oauth2_token_service->RemoveObserver(this);
375 void ProfileChooserView::ResetView() {
376 manage_accounts_link_ = NULL;
377 signout_current_profile_link_ = NULL;
378 signin_current_profile_link_ = NULL;
379 guest_button_ = NULL;
380 end_guest_button_ = NULL;
381 users_button_ = NULL;
382 add_user_button_ = NULL;
383 add_account_button_ = NULL;
384 current_profile_photo_ = NULL;
385 current_profile_name_ = NULL;
386 open_other_profile_indexes_map_.clear();
387 current_profile_accounts_map_.clear();
390 void ProfileChooserView::Init() {
391 ShowView(PROFILE_CHOOSER_VIEW, avatar_menu_.get());
394 void ProfileChooserView::OnAvatarMenuChanged(
395 AvatarMenu* avatar_menu) {
396 // Refresh the view with the new menu. We can't just update the local copy
397 // as this may have been triggered by a sign out action, in which case
398 // the view is being destroyed.
399 ShowView(PROFILE_CHOOSER_VIEW, avatar_menu);
402 void ProfileChooserView::OnRefreshTokenAvailable(
403 const std::string& account_id) {
404 // Refresh the account management view when a new account is added to the
406 if (view_mode_ == ACCOUNT_MANAGEMENT_VIEW ||
407 view_mode_ == GAIA_SIGNIN_VIEW ||
408 view_mode_ == GAIA_ADD_ACCOUNT_VIEW) {
409 ShowView(ACCOUNT_MANAGEMENT_VIEW, avatar_menu_.get());
413 void ProfileChooserView::OnRefreshTokenRevoked(const std::string& account_id) {
414 // Refresh the account management view when an account is removed from the
416 if (view_mode_ == ACCOUNT_MANAGEMENT_VIEW)
417 ShowView(ACCOUNT_MANAGEMENT_VIEW, avatar_menu_.get());
420 void ProfileChooserView::ShowView(BubbleViewMode view_to_display,
421 AvatarMenu* avatar_menu) {
422 // The account management view should only be displayed if the active profile
424 if (view_to_display == ACCOUNT_MANAGEMENT_VIEW) {
425 const AvatarMenu::Item& active_item = avatar_menu->GetItemAt(
426 avatar_menu->GetActiveProfileIndex());
427 DCHECK(active_item.signed_in);
431 RemoveAllChildViews(true);
432 view_mode_ = view_to_display;
434 views::GridLayout* layout = CreateSingleColumnLayout(this);
435 layout->set_minimum_size(gfx::Size(kMinMenuWidth, 0));
437 if (view_to_display == GAIA_SIGNIN_VIEW ||
438 view_to_display == GAIA_ADD_ACCOUNT_VIEW) {
439 // Minimum size for embedded sign in pages as defined in Gaia.
440 const int kMinGaiaViewWidth = 320;
441 const int kMinGaiaViewHeight = 440;
442 Profile* profile = browser_->profile();
443 views::WebView* web_view = new views::WebView(profile);
444 signin::Source source = (view_to_display == GAIA_SIGNIN_VIEW) ?
445 signin::SOURCE_AVATAR_BUBBLE_SIGN_IN :
446 signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT;
447 GURL url(signin::GetPromoURL(
448 source, false /* auto_close */, true /* is_constrained */));
449 web_view->LoadInitialURL(url);
450 layout->StartRow(1, 0);
451 layout->AddView(web_view);
452 layout->set_minimum_size(
453 gfx::Size(kMinGaiaViewWidth, kMinGaiaViewHeight));
455 if (GetBubbleFrameView())
460 // Separate items into active and alternatives.
461 Indexes other_profiles;
462 bool is_guest_view = true;
463 views::View* current_profile_view = NULL;
464 views::View* current_profile_accounts = NULL;
465 for (size_t i = 0; i < avatar_menu->GetNumberOfItems(); ++i) {
466 const AvatarMenu::Item& item = avatar_menu->GetItemAt(i);
468 if (view_to_display == PROFILE_CHOOSER_VIEW) {
469 current_profile_view = CreateCurrentProfileView(item, false);
471 current_profile_view = CreateCurrentProfileEditableView(item);
472 current_profile_accounts = CreateCurrentProfileAccountsView(item);
474 is_guest_view = false;
476 other_profiles.push_back(i);
480 if (!current_profile_view) // Guest windows don't have an active profile.
481 current_profile_view = CreateGuestProfileView();
483 layout->StartRow(1, 0);
484 layout->AddView(current_profile_view);
486 if (view_to_display == PROFILE_CHOOSER_VIEW) {
487 layout->StartRow(1, 0);
488 layout->AddView(CreateOtherProfilesView(other_profiles));
490 DCHECK(current_profile_accounts);
491 layout->StartRow(0, 0);
492 layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
493 layout->StartRow(1, 0);
494 layout->AddView(current_profile_accounts);
497 layout->StartRow(0, 0);
498 layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
501 views::View* option_buttons_view = CreateOptionsView(is_guest_view);
502 layout->StartRow(0, 0);
503 layout->AddView(option_buttons_view);
506 if (GetBubbleFrameView())
510 void ProfileChooserView::WindowClosing() {
511 DCHECK_EQ(profile_bubble_, this);
512 profile_bubble_ = NULL;
515 void ProfileChooserView::ButtonPressed(views::Button* sender,
516 const ui::Event& event) {
517 // Disable button after clicking so that it doesn't get clicked twice and
518 // start a second action... which can crash Chrome. But don't disable if it
519 // has no parent (like in tests) because that will also crash.
520 if (sender->parent())
521 sender->SetEnabled(false);
523 if (sender == guest_button_) {
524 profiles::SwitchToGuestProfile(browser_->host_desktop_type(),
525 profiles::ProfileSwitchingDoneCallback());
526 } else if (sender == end_guest_button_) {
527 profiles::CloseGuestProfileWindows();
528 } else if (sender == users_button_) {
529 // Only non-guest users appear in the User Manager.
530 base::FilePath profile_path;
531 if (!end_guest_button_) {
532 size_t active_index = avatar_menu_->GetActiveProfileIndex();
533 profile_path = avatar_menu_->GetItemAt(active_index).profile_path;
535 chrome::ShowUserManager(profile_path);
536 } else if (sender == add_user_button_) {
537 profiles::CreateAndSwitchToNewProfile(
538 browser_->host_desktop_type(),
539 profiles::ProfileSwitchingDoneCallback());
540 } else if (sender == add_account_button_) {
541 ShowView(GAIA_ADD_ACCOUNT_VIEW, avatar_menu_.get());
542 } else if (sender == current_profile_photo_->change_photo_button()) {
543 avatar_menu_->EditProfile(avatar_menu_->GetActiveProfileIndex());
545 // One of the "other profiles" buttons was pressed.
546 ButtonIndexes::const_iterator match =
547 open_other_profile_indexes_map_.find(sender);
548 DCHECK(match != open_other_profile_indexes_map_.end());
549 avatar_menu_->SwitchToProfile(
551 ui::DispositionFromEventFlags(event.flags()) == NEW_WINDOW);
555 void ProfileChooserView::OnMenuButtonClicked(views::View* source,
556 const gfx::Point& point) {
557 AccountButtonIndexes::const_iterator match =
558 current_profile_accounts_map_.find(source);
559 DCHECK(match != current_profile_accounts_map_.end());
561 MutableProfileOAuth2TokenService* oauth2_token_service =
562 ProfileOAuth2TokenServiceFactory::GetPlatformSpecificForProfile(
563 browser_->profile());
564 if (oauth2_token_service)
565 oauth2_token_service->RevokeCredentials(match->second);
568 void ProfileChooserView::LinkClicked(views::Link* sender, int event_flags) {
569 if (sender == manage_accounts_link_) {
570 // ShowView() will DCHECK if this view is displayed for non signed-in users.
571 ShowView(ACCOUNT_MANAGEMENT_VIEW, avatar_menu_.get());
572 } else if (sender == signout_current_profile_link_) {
573 profiles::LockProfile(browser_->profile());
575 DCHECK(sender == signin_current_profile_link_);
576 ShowView(GAIA_SIGNIN_VIEW, avatar_menu_.get());
580 bool ProfileChooserView::HandleKeyEvent(views::Textfield* sender,
581 const ui::KeyEvent& key_event) {
582 views::Textfield* name_textfield =
583 current_profile_name_->profile_name_textfield();
584 DCHECK(sender == name_textfield);
586 if (key_event.key_code() == ui::VKEY_RETURN ||
587 key_event.key_code() == ui::VKEY_TAB) {
588 // Pressing Tab/Enter commits the new profile name, unless it's empty.
589 base::string16 new_profile_name = name_textfield->text();
590 if (new_profile_name.empty())
593 const AvatarMenu::Item& active_item = avatar_menu_->GetItemAt(
594 avatar_menu_->GetActiveProfileIndex());
595 Profile* profile = g_browser_process->profile_manager()->GetProfile(
596 active_item.profile_path);
599 if (profile->IsManaged())
602 profiles::UpdateProfileName(profile, new_profile_name);
603 current_profile_name_->ShowReadOnlyView();
609 views::View* ProfileChooserView::CreateCurrentProfileView(
610 const AvatarMenu::Item& avatar_item,
612 views::View* view = new views::View();
613 views::GridLayout* layout = CreateDoubleColumnLayout(view);
614 layout->SetInsets(views::kButtonVEdgeMarginNew,
615 views::kButtonHEdgeMarginNew,
616 views::kButtonVEdgeMarginNew,
617 views::kButtonHEdgeMarginNew);
619 current_profile_photo_ =
620 new EditableProfilePhoto(this, avatar_item.icon, !is_guest);
621 view->SetBoundsRect(current_profile_photo_->bounds());
622 current_profile_name_ =
623 new EditableProfileName(this, avatar_item.name, !is_guest);
625 layout->StartRow(1, 0);
626 layout->AddView(current_profile_photo_, 1, 3);
627 layout->AddView(current_profile_name_);
630 layout->StartRow(1, 0);
631 layout->SkipColumns(1);
632 layout->StartRow(1, 0);
633 layout->SkipColumns(1);
634 } else if (avatar_item.signed_in) {
635 manage_accounts_link_ = CreateLink(
636 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_MANAGE_ACCOUNTS_BUTTON),
638 signout_current_profile_link_ = CreateLink(
639 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_SIGNOUT_BUTTON), this);
640 layout->StartRow(1, 0);
641 layout->SkipColumns(1);
642 layout->AddView(signout_current_profile_link_);
643 layout->StartRow(1, 0);
644 layout->SkipColumns(1);
645 layout->AddView(manage_accounts_link_);
647 signin_current_profile_link_ = CreateLink(
648 l10n_util::GetStringFUTF16(
649 IDS_SYNC_START_SYNC_BUTTON_LABEL,
650 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)),
652 layout->StartRow(1, 0);
653 layout->SkipColumns(1);
654 layout->AddView(signin_current_profile_link_);
655 layout->StartRow(1, 0);
656 layout->SkipColumns(1);
662 views::View* ProfileChooserView::CreateCurrentProfileEditableView(
663 const AvatarMenu::Item& avatar_item) {
664 DCHECK(avatar_item.signed_in);
665 views::View* view = new views::View();
666 views::GridLayout* layout = CreateDoubleColumnLayout(view);
667 layout->SetInsets(views::kButtonVEdgeMarginNew,
668 views::kButtonHEdgeMarginNew,
669 views::kButtonVEdgeMarginNew,
670 views::kButtonHEdgeMarginNew);
672 current_profile_photo_ =
673 new EditableProfilePhoto(this, avatar_item.icon, true);
674 view->SetBoundsRect(current_profile_photo_->bounds());
675 current_profile_name_ =
676 new EditableProfileName(this, avatar_item.name, true);
678 layout->StartRow(1, 0);
679 layout->AddView(current_profile_photo_, 1, 3);
680 layout->AddView(current_profile_name_);
682 layout->StartRow(1, 0);
683 layout->SkipColumns(1);
685 layout->StartRow(1, 0);
686 layout->SkipColumns(1);
690 views::View* ProfileChooserView::CreateGuestProfileView() {
691 gfx::Image guest_icon =
692 ui::ResourceBundle::GetSharedInstance().GetImageNamed(IDR_LOGIN_GUEST);
693 AvatarMenu::Item guest_avatar_item(0, 0, guest_icon);
694 guest_avatar_item.active = true;
695 guest_avatar_item.name = l10n_util::GetStringUTF16(
696 IDS_PROFILES_GUEST_PROFILE_NAME);
697 guest_avatar_item.signed_in = false;
699 return CreateCurrentProfileView(guest_avatar_item, true);
702 views::View* ProfileChooserView::CreateOtherProfilesView(
703 const Indexes& avatars_to_show) {
704 views::View* view = new views::View();
705 views::GridLayout* layout = CreateSingleColumnLayout(view);
706 layout->SetInsets(0, views::kButtonHEdgeMarginNew,
707 views::kButtonVEdgeMarginNew, views::kButtonHEdgeMarginNew);
708 int num_avatars_to_show = avatars_to_show.size();
709 for (int i = 0; i < num_avatars_to_show; ++i) {
710 const size_t index = avatars_to_show[i];
711 const AvatarMenu::Item& item = avatar_menu_->GetItemAt(index);
712 const int kSmallImageSide = 32;
714 gfx::Image image = profiles::GetSizedAvatarIconWithBorder(
716 kSmallImageSide + profiles::kAvatarIconPadding,
717 kSmallImageSide + profiles::kAvatarIconPadding);
719 views::TextButton* button = new views::TextButton(this, item.name);
720 open_other_profile_indexes_map_[button] = index;
721 button->SetIcon(*image.ToImageSkia());
722 button->set_icon_text_spacing(views::kItemLabelSpacing);
723 button->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
724 ui::ResourceBundle::MediumFont));
725 button->SetBorder(views::Border::NullBorder());
727 layout->StartRow(1, 0);
728 layout->AddView(button);
730 // The last avatar in the list does not need any bottom padding.
731 if (i < num_avatars_to_show - 1)
732 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
738 views::View* ProfileChooserView::CreateOptionsView(bool is_guest_view) {
739 views::View* view = new views::View();
740 views::GridLayout* layout = CreateSingleColumnLayout(view);
741 // The horizontal padding will be set by each button individually, so that
742 // in the hovered state the button spans the entire parent view.
743 layout->SetInsets(views::kRelatedControlVerticalSpacing, 0,
744 views::kRelatedControlVerticalSpacing, 0);
746 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
748 layout->StartRow(1, 0);
750 end_guest_button_ = new BackgroundColorHoverButton(
752 l10n_util::GetStringUTF16(IDS_PROFILES_EXIT_GUEST_BUTTON),
753 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_BROWSE_GUEST),
754 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_BROWSE_GUEST_WHITE));
755 layout->AddView(end_guest_button_);
757 guest_button_ = new BackgroundColorHoverButton(
759 l10n_util::GetStringUTF16(IDS_PROFILES_GUEST_BUTTON),
760 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_BROWSE_GUEST),
761 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_BROWSE_GUEST_WHITE));
762 layout->AddView(guest_button_);
765 add_user_button_ = new BackgroundColorHoverButton(
767 l10n_util::GetStringUTF16(IDS_PROFILES_ADD_PERSON_BUTTON),
768 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_ADD_USER),
769 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_ADD_USER_WHITE));
770 layout->StartRow(1, 0);
771 layout->AddView(add_user_button_);
773 users_button_ = new BackgroundColorHoverButton(
775 l10n_util::GetStringUTF16(IDS_PROFILES_ALL_PEOPLE_BUTTON),
776 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_ADD_USER),
777 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_ADD_USER_WHITE));
778 layout->StartRow(1, 0);
779 layout->AddView(users_button_);
784 views::View* ProfileChooserView::CreateCurrentProfileAccountsView(
785 const AvatarMenu::Item& avatar_item) {
786 DCHECK(avatar_item.signed_in);
787 views::View* view = new views::View();
788 views::GridLayout* layout = CreateSingleColumnLayout(view);
789 layout->SetInsets(views::kButtonVEdgeMarginNew,
790 views::kButtonHEdgeMarginNew,
791 views::kButtonVEdgeMarginNew,
792 views::kButtonHEdgeMarginNew);
794 Profile* profile = browser_->profile();
795 std::string primary_account =
796 SigninManagerFactory::GetForProfile(profile)->GetAuthenticatedUsername();
797 DCHECK(!primary_account.empty());
798 std::vector<std::string>accounts =
799 profiles::GetSecondaryAccountsForProfile(profile, primary_account);
801 // The primary account should always be listed first.
802 // TODO(rogerta): we still need to further differentiate the primary account
803 // from the others in the UI, so more work is likely required here:
805 CreateAccountButton(layout, primary_account, true);
806 for (size_t i = 0; i < accounts.size(); ++i)
807 CreateAccountButton(layout, accounts[i], false);
809 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
811 add_account_button_ = new views::BlueButton(
813 l10n_util::GetStringFUTF16(IDS_PROFILES_PROFILE_ADD_ACCOUNT_BUTTON,
815 layout->StartRow(1, 0);
816 layout->AddView(add_account_button_);
820 void ProfileChooserView::CreateAccountButton(views::GridLayout* layout,
821 const std::string& account,
822 bool is_primary_account) {
823 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
824 // Use a MenuButtonListener and not a regular ButtonListener to be
825 // able to distinguish between the unnamed "other profile" buttons and the
826 // unnamed "multiple accounts" buttons.
827 views::MenuButton* email_button = new views::MenuButton(
829 gfx::ElideEmail(base::UTF8ToUTF16(account),
830 rb->GetFontList(ui::ResourceBundle::BaseFont),
832 is_primary_account ? NULL : this, // Cannot delete the primary account.
833 !is_primary_account);
834 email_button->SetBorder(views::Border::CreateEmptyBorder(0, 0, 0, 0));
835 if (!is_primary_account) {
836 email_button->set_menu_marker(
837 rb->GetImageNamed(IDR_CLOSE_1).ToImageSkia());
838 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
840 layout->StartRow(1, 0);
841 layout->AddView(email_button);
843 // Save the original email address, as the button text could be elided.
844 current_profile_accounts_map_[email_button] = account;