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
, 0,
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 set_border(text_button_border
.release());
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_
->set_border(NULL
);
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
);
245 if (!is_editing_allowed
)
248 SetIcon(*rb
->GetImageSkiaNamed(IDR_INFOBAR_AUTOFILL
));
249 set_icon_placement(views::TextButton::ICON_ON_RIGHT
);
251 // Textfield that overlaps the button.
252 profile_name_textfield_
= new views::Textfield();
253 profile_name_textfield_
->set_controller(controller
);
254 profile_name_textfield_
->SetFontList(medium_font_list
);
255 profile_name_textfield_
->SetVisible(false);
256 AddChildView(profile_name_textfield_
);
259 views::Textfield
* profile_name_textfield() {
260 return profile_name_textfield_
;
263 // Hide the editable textfield and show the button displaying the profile
265 void ShowReadOnlyView() {
266 if (profile_name_textfield_
)
267 profile_name_textfield_
->SetVisible(false);
271 // views::ButtonListener:
272 virtual void ButtonPressed(views::Button
* sender
,
273 const ui::Event
& event
) OVERRIDE
{
274 if (profile_name_textfield_
) {
275 profile_name_textfield_
->SetVisible(true);
276 profile_name_textfield_
->SetText(text());
277 profile_name_textfield_
->SelectAll(false);
278 profile_name_textfield_
->RequestFocus();
282 // views::CustomButton:
283 virtual bool OnKeyReleased(const ui::KeyEvent
& event
) OVERRIDE
{
284 // Override CustomButton's implementation, which presses the button when
285 // you press space and clicks it when you release space, as the space can be
286 // part of the new profile name typed in the textfield.
291 virtual void Layout() OVERRIDE
{
292 if (profile_name_textfield_
)
293 profile_name_textfield_
->SetBounds(0, 0, width(), height());
294 views::View::Layout();
297 // Button that is shown when hovering over the image view. Can be NULL if
298 // the profile name isn't allowed to be edited (e.g. for guest profiles).
299 views::Textfield
* profile_name_textfield_
;
301 DISALLOW_COPY_AND_ASSIGN(EditableProfileName
);
305 // ProfileChooserView ---------------------------------------------------------
308 ProfileChooserView
* ProfileChooserView::profile_bubble_
= NULL
;
309 bool ProfileChooserView::close_on_deactivate_for_testing_
= true;
312 void ProfileChooserView::ShowBubble(
313 views::View
* anchor_view
,
314 views::BubbleBorder::Arrow arrow
,
315 views::BubbleBorder::BubbleAlignment border_alignment
,
316 const gfx::Rect
& anchor_rect
,
319 // TODO(bcwhite): handle case where we should show on different window
322 profile_bubble_
= new ProfileChooserView(
323 anchor_view
, arrow
, anchor_rect
, browser
);
324 views::BubbleDelegateView::CreateBubble(profile_bubble_
);
325 profile_bubble_
->set_close_on_deactivate(close_on_deactivate_for_testing_
);
326 profile_bubble_
->SetAlignment(border_alignment
);
327 profile_bubble_
->GetWidget()->Show();
328 profile_bubble_
->SetArrowPaintType(views::BubbleBorder::PAINT_NONE
);
332 bool ProfileChooserView::IsShowing() {
333 return profile_bubble_
!= NULL
;
337 void ProfileChooserView::Hide() {
339 profile_bubble_
->GetWidget()->Close();
342 ProfileChooserView::ProfileChooserView(views::View
* anchor_view
,
343 views::BubbleBorder::Arrow arrow
,
344 const gfx::Rect
& anchor_rect
,
346 : BubbleDelegateView(anchor_view
, arrow
),
348 view_mode_(PROFILE_CHOOSER_VIEW
) {
349 // Reset the default margins inherited from the BubbleDelegateView.
350 set_margins(gfx::Insets());
354 avatar_menu_
.reset(new AvatarMenu(
355 &g_browser_process
->profile_manager()->GetProfileInfoCache(),
358 avatar_menu_
->RebuildMenu();
360 ProfileOAuth2TokenService
* oauth2_token_service
=
361 ProfileOAuth2TokenServiceFactory::GetForProfile(browser_
->profile());
362 if (oauth2_token_service
)
363 oauth2_token_service
->AddObserver(this);
366 ProfileChooserView::~ProfileChooserView() {
367 ProfileOAuth2TokenService
* oauth2_token_service
=
368 ProfileOAuth2TokenServiceFactory::GetForProfile(browser_
->profile());
369 if (oauth2_token_service
)
370 oauth2_token_service
->RemoveObserver(this);
373 void ProfileChooserView::ResetView() {
374 manage_accounts_link_
= NULL
;
375 signout_current_profile_link_
= NULL
;
376 signin_current_profile_link_
= NULL
;
377 guest_button_
= NULL
;
378 end_guest_button_
= NULL
;
379 users_button_
= NULL
;
380 add_user_button_
= NULL
;
381 add_account_button_
= NULL
;
382 current_profile_photo_
= NULL
;
383 current_profile_name_
= NULL
;
384 open_other_profile_indexes_map_
.clear();
385 current_profile_accounts_map_
.clear();
388 void ProfileChooserView::Init() {
389 ShowView(PROFILE_CHOOSER_VIEW
, avatar_menu_
.get());
392 void ProfileChooserView::OnAvatarMenuChanged(
393 AvatarMenu
* avatar_menu
) {
394 // Refresh the view with the new menu. We can't just update the local copy
395 // as this may have been triggered by a sign out action, in which case
396 // the view is being destroyed.
397 ShowView(PROFILE_CHOOSER_VIEW
, avatar_menu
);
400 void ProfileChooserView::OnRefreshTokenAvailable(
401 const std::string
& account_id
) {
402 // Refresh the account management view when a new account is added to the
404 if (view_mode_
== ACCOUNT_MANAGEMENT_VIEW
||
405 view_mode_
== GAIA_SIGNIN_VIEW
||
406 view_mode_
== GAIA_ADD_ACCOUNT_VIEW
) {
407 ShowView(ACCOUNT_MANAGEMENT_VIEW
, avatar_menu_
.get());
411 void ProfileChooserView::OnRefreshTokenRevoked(const std::string
& account_id
) {
412 // Refresh the account management view when an account is removed from the
414 if (view_mode_
== ACCOUNT_MANAGEMENT_VIEW
)
415 ShowView(ACCOUNT_MANAGEMENT_VIEW
, avatar_menu_
.get());
418 void ProfileChooserView::ShowView(BubbleViewMode view_to_display
,
419 AvatarMenu
* avatar_menu
) {
420 // The account management view should only be displayed if the active profile
422 if (view_to_display
== ACCOUNT_MANAGEMENT_VIEW
) {
423 const AvatarMenu::Item
& active_item
= avatar_menu
->GetItemAt(
424 avatar_menu
->GetActiveProfileIndex());
425 DCHECK(active_item
.signed_in
);
429 RemoveAllChildViews(true);
430 view_mode_
= view_to_display
;
432 views::GridLayout
* layout
= CreateSingleColumnLayout(this);
433 layout
->set_minimum_size(gfx::Size(kMinMenuWidth
, 0));
435 if (view_to_display
== GAIA_SIGNIN_VIEW
||
436 view_to_display
== GAIA_ADD_ACCOUNT_VIEW
) {
437 // Minimum size for embedded sign in pages as defined in Gaia.
438 const int kMinGaiaViewWidth
= 320;
439 const int kMinGaiaViewHeight
= 440;
440 Profile
* profile
= browser_
->profile();
441 views::WebView
* web_view
= new views::WebView(profile
);
442 signin::Source source
= (view_to_display
== GAIA_SIGNIN_VIEW
) ?
443 signin::SOURCE_AVATAR_BUBBLE_SIGN_IN
:
444 signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT
;
445 GURL
url(signin::GetPromoURL(
446 source
, false /* auto_close */, true /* is_constrained */));
447 web_view
->LoadInitialURL(url
);
448 layout
->StartRow(1, 0);
449 layout
->AddView(web_view
);
450 layout
->set_minimum_size(
451 gfx::Size(kMinGaiaViewWidth
, kMinGaiaViewHeight
));
453 if (GetBubbleFrameView())
458 // Separate items into active and alternatives.
459 Indexes other_profiles
;
460 bool is_guest_view
= true;
461 views::View
* current_profile_view
= NULL
;
462 views::View
* current_profile_accounts
= NULL
;
463 for (size_t i
= 0; i
< avatar_menu
->GetNumberOfItems(); ++i
) {
464 const AvatarMenu::Item
& item
= avatar_menu
->GetItemAt(i
);
466 if (view_to_display
== PROFILE_CHOOSER_VIEW
) {
467 current_profile_view
= CreateCurrentProfileView(item
, false);
469 current_profile_view
= CreateCurrentProfileEditableView(item
);
470 current_profile_accounts
= CreateCurrentProfileAccountsView(item
);
472 is_guest_view
= false;
474 other_profiles
.push_back(i
);
478 if (!current_profile_view
) // Guest windows don't have an active profile.
479 current_profile_view
= CreateGuestProfileView();
481 layout
->StartRow(1, 0);
482 layout
->AddView(current_profile_view
);
484 if (view_to_display
== PROFILE_CHOOSER_VIEW
) {
485 layout
->StartRow(1, 0);
486 layout
->AddView(CreateOtherProfilesView(other_profiles
));
488 DCHECK(current_profile_accounts
);
489 layout
->StartRow(0, 0);
490 layout
->AddView(new views::Separator(views::Separator::HORIZONTAL
));
491 layout
->StartRow(1, 0);
492 layout
->AddView(current_profile_accounts
);
495 layout
->StartRow(0, 0);
496 layout
->AddView(new views::Separator(views::Separator::HORIZONTAL
));
499 views::View
* option_buttons_view
= CreateOptionsView(is_guest_view
);
500 layout
->StartRow(0, 0);
501 layout
->AddView(option_buttons_view
);
504 if (GetBubbleFrameView())
508 void ProfileChooserView::WindowClosing() {
509 DCHECK_EQ(profile_bubble_
, this);
510 profile_bubble_
= NULL
;
513 void ProfileChooserView::ButtonPressed(views::Button
* sender
,
514 const ui::Event
& event
) {
515 // Disable button after clicking so that it doesn't get clicked twice and
516 // start a second action... which can crash Chrome. But don't disable if it
517 // has no parent (like in tests) because that will also crash.
518 if (sender
->parent())
519 sender
->SetEnabled(false);
521 if (sender
== guest_button_
) {
522 profiles::SwitchToGuestProfile(browser_
->host_desktop_type(),
523 profiles::ProfileSwitchingDoneCallback());
524 } else if (sender
== end_guest_button_
) {
525 profiles::CloseGuestProfileWindows();
526 } else if (sender
== users_button_
) {
527 // Only non-guest users appear in the User Manager.
528 base::FilePath profile_path
;
529 if (!end_guest_button_
) {
530 size_t active_index
= avatar_menu_
->GetActiveProfileIndex();
531 profile_path
= avatar_menu_
->GetItemAt(active_index
).profile_path
;
533 chrome::ShowUserManager(profile_path
);
534 } else if (sender
== add_user_button_
) {
535 profiles::CreateAndSwitchToNewProfile(
536 browser_
->host_desktop_type(),
537 profiles::ProfileSwitchingDoneCallback());
538 } else if (sender
== add_account_button_
) {
539 ShowView(GAIA_ADD_ACCOUNT_VIEW
, avatar_menu_
.get());
540 } else if (sender
== current_profile_photo_
->change_photo_button()) {
541 avatar_menu_
->EditProfile(avatar_menu_
->GetActiveProfileIndex());
543 // One of the "other profiles" buttons was pressed.
544 ButtonIndexes::const_iterator match
=
545 open_other_profile_indexes_map_
.find(sender
);
546 DCHECK(match
!= open_other_profile_indexes_map_
.end());
547 avatar_menu_
->SwitchToProfile(
549 ui::DispositionFromEventFlags(event
.flags()) == NEW_WINDOW
);
553 void ProfileChooserView::OnMenuButtonClicked(views::View
* source
,
554 const gfx::Point
& point
) {
555 AccountButtonIndexes::const_iterator match
=
556 current_profile_accounts_map_
.find(source
);
557 DCHECK(match
!= current_profile_accounts_map_
.end());
559 MutableProfileOAuth2TokenService
* oauth2_token_service
=
560 ProfileOAuth2TokenServiceFactory::GetPlatformSpecificForProfile(
561 browser_
->profile());
562 if (oauth2_token_service
)
563 oauth2_token_service
->RevokeCredentials(match
->second
);
566 void ProfileChooserView::LinkClicked(views::Link
* sender
, int event_flags
) {
567 if (sender
== manage_accounts_link_
) {
568 // ShowView() will DCHECK if this view is displayed for non signed-in users.
569 ShowView(ACCOUNT_MANAGEMENT_VIEW
, avatar_menu_
.get());
570 } else if (sender
== signout_current_profile_link_
) {
571 profiles::LockProfile(browser_
->profile());
573 DCHECK(sender
== signin_current_profile_link_
);
574 ShowView(GAIA_SIGNIN_VIEW
, avatar_menu_
.get());
578 bool ProfileChooserView::HandleKeyEvent(views::Textfield
* sender
,
579 const ui::KeyEvent
& key_event
) {
580 views::Textfield
* name_textfield
=
581 current_profile_name_
->profile_name_textfield();
582 DCHECK(sender
== name_textfield
);
584 if (key_event
.key_code() == ui::VKEY_RETURN
||
585 key_event
.key_code() == ui::VKEY_TAB
) {
586 // Pressing Tab/Enter commits the new profile name, unless it's empty.
587 base::string16 new_profile_name
= name_textfield
->text();
588 if (new_profile_name
.empty())
591 const AvatarMenu::Item
& active_item
= avatar_menu_
->GetItemAt(
592 avatar_menu_
->GetActiveProfileIndex());
593 Profile
* profile
= g_browser_process
->profile_manager()->GetProfile(
594 active_item
.profile_path
);
597 if (profile
->IsManaged())
600 profiles::UpdateProfileName(profile
, new_profile_name
);
601 current_profile_name_
->ShowReadOnlyView();
607 views::View
* ProfileChooserView::CreateCurrentProfileView(
608 const AvatarMenu::Item
& avatar_item
,
610 views::View
* view
= new views::View();
611 views::GridLayout
* layout
= CreateDoubleColumnLayout(view
);
612 layout
->SetInsets(views::kButtonVEdgeMarginNew
,
613 views::kButtonHEdgeMarginNew
,
614 views::kButtonVEdgeMarginNew
,
615 views::kButtonHEdgeMarginNew
);
617 current_profile_photo_
=
618 new EditableProfilePhoto(this, avatar_item
.icon
, !is_guest
);
619 view
->SetBoundsRect(current_profile_photo_
->bounds());
620 current_profile_name_
=
621 new EditableProfileName(this, avatar_item
.name
, !is_guest
);
623 layout
->StartRow(1, 0);
624 layout
->AddView(current_profile_photo_
, 1, 3);
625 layout
->AddView(current_profile_name_
);
628 layout
->StartRow(1, 0);
629 layout
->SkipColumns(1);
630 layout
->StartRow(1, 0);
631 layout
->SkipColumns(1);
632 } else if (avatar_item
.signed_in
) {
633 manage_accounts_link_
= CreateLink(
634 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_MANAGE_ACCOUNTS_BUTTON
),
636 signout_current_profile_link_
= CreateLink(
637 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_SIGNOUT_BUTTON
), this);
638 layout
->StartRow(1, 0);
639 layout
->SkipColumns(1);
640 layout
->AddView(signout_current_profile_link_
);
641 layout
->StartRow(1, 0);
642 layout
->SkipColumns(1);
643 layout
->AddView(manage_accounts_link_
);
645 signin_current_profile_link_
= CreateLink(
646 l10n_util::GetStringFUTF16(
647 IDS_SYNC_START_SYNC_BUTTON_LABEL
,
648 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME
)),
650 layout
->StartRow(1, 0);
651 layout
->SkipColumns(1);
652 layout
->AddView(signin_current_profile_link_
);
653 layout
->StartRow(1, 0);
654 layout
->SkipColumns(1);
660 views::View
* ProfileChooserView::CreateCurrentProfileEditableView(
661 const AvatarMenu::Item
& avatar_item
) {
662 DCHECK(avatar_item
.signed_in
);
663 views::View
* view
= new views::View();
664 views::GridLayout
* layout
= CreateDoubleColumnLayout(view
);
665 layout
->SetInsets(views::kButtonVEdgeMarginNew
,
666 views::kButtonHEdgeMarginNew
,
667 views::kButtonVEdgeMarginNew
,
668 views::kButtonHEdgeMarginNew
);
670 current_profile_photo_
=
671 new EditableProfilePhoto(this, avatar_item
.icon
, true);
672 view
->SetBoundsRect(current_profile_photo_
->bounds());
673 current_profile_name_
=
674 new EditableProfileName(this, avatar_item
.name
, true);
676 layout
->StartRow(1, 0);
677 layout
->AddView(current_profile_photo_
, 1, 3);
678 layout
->AddView(current_profile_name_
);
680 layout
->StartRow(1, 0);
681 layout
->SkipColumns(1);
683 layout
->StartRow(1, 0);
684 layout
->SkipColumns(1);
688 views::View
* ProfileChooserView::CreateGuestProfileView() {
689 gfx::Image guest_icon
=
690 ui::ResourceBundle::GetSharedInstance().GetImageNamed(IDR_LOGIN_GUEST
);
691 AvatarMenu::Item
guest_avatar_item(0, 0, guest_icon
);
692 guest_avatar_item
.active
= true;
693 guest_avatar_item
.name
= l10n_util::GetStringUTF16(
694 IDS_PROFILES_GUEST_PROFILE_NAME
);
695 guest_avatar_item
.signed_in
= false;
697 return CreateCurrentProfileView(guest_avatar_item
, true);
700 views::View
* ProfileChooserView::CreateOtherProfilesView(
701 const Indexes
& avatars_to_show
) {
702 views::View
* view
= new views::View();
703 views::GridLayout
* layout
= CreateSingleColumnLayout(view
);
704 layout
->SetInsets(0, views::kButtonHEdgeMarginNew
,
705 views::kButtonVEdgeMarginNew
, views::kButtonHEdgeMarginNew
);
706 int num_avatars_to_show
= avatars_to_show
.size();
707 for (int i
= 0; i
< num_avatars_to_show
; ++i
) {
708 const size_t index
= avatars_to_show
[i
];
709 const AvatarMenu::Item
& item
= avatar_menu_
->GetItemAt(index
);
710 const int kSmallImageSide
= 32;
712 gfx::Image image
= profiles::GetSizedAvatarIconWithBorder(
714 kSmallImageSide
+ profiles::kAvatarIconPadding
,
715 kSmallImageSide
+ profiles::kAvatarIconPadding
);
717 views::TextButton
* button
= new views::TextButton(this, item
.name
);
718 open_other_profile_indexes_map_
[button
] = index
;
719 button
->SetIcon(*image
.ToImageSkia());
720 button
->set_icon_text_spacing(views::kItemLabelSpacing
);
721 button
->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
722 ui::ResourceBundle::MediumFont
));
723 button
->set_border(NULL
);
725 layout
->StartRow(1, 0);
726 layout
->AddView(button
);
728 // The last avatar in the list does not need any bottom padding.
729 if (i
< num_avatars_to_show
- 1)
730 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
736 views::View
* ProfileChooserView::CreateOptionsView(bool is_guest_view
) {
737 views::View
* view
= new views::View();
738 views::GridLayout
* layout
= CreateSingleColumnLayout(view
);
739 // The horizontal padding will be set by each button individually, so that
740 // in the hovered state the button spans the entire parent view.
741 layout
->SetInsets(views::kRelatedControlVerticalSpacing
, 0,
742 views::kRelatedControlVerticalSpacing
, 0);
744 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
746 layout
->StartRow(1, 0);
748 end_guest_button_
= new BackgroundColorHoverButton(
750 l10n_util::GetStringUTF16(IDS_PROFILES_EXIT_GUEST_BUTTON
),
751 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_BROWSE_GUEST
),
752 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_BROWSE_GUEST_WHITE
));
753 layout
->AddView(end_guest_button_
);
755 guest_button_
= new BackgroundColorHoverButton(
757 l10n_util::GetStringUTF16(IDS_PROFILES_GUEST_BUTTON
),
758 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_BROWSE_GUEST
),
759 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_BROWSE_GUEST_WHITE
));
760 layout
->AddView(guest_button_
);
763 add_user_button_
= new BackgroundColorHoverButton(
765 l10n_util::GetStringUTF16(IDS_PROFILES_ADD_PERSON_BUTTON
),
766 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_ADD_USER
),
767 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_ADD_USER_WHITE
));
768 layout
->StartRow(1, 0);
769 layout
->AddView(add_user_button_
);
771 users_button_
= new BackgroundColorHoverButton(
773 l10n_util::GetStringUTF16(IDS_PROFILES_ALL_PEOPLE_BUTTON
),
774 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_ADD_USER
),
775 *rb
->GetImageSkiaNamed(IDR_ICON_PROFILES_ADD_USER_WHITE
));
776 layout
->StartRow(1, 0);
777 layout
->AddView(users_button_
);
782 views::View
* ProfileChooserView::CreateCurrentProfileAccountsView(
783 const AvatarMenu::Item
& avatar_item
) {
784 DCHECK(avatar_item
.signed_in
);
785 views::View
* view
= new views::View();
786 views::GridLayout
* layout
= CreateSingleColumnLayout(view
);
787 layout
->SetInsets(views::kButtonVEdgeMarginNew
,
788 views::kButtonHEdgeMarginNew
,
789 views::kButtonVEdgeMarginNew
,
790 views::kButtonHEdgeMarginNew
);
792 Profile
* profile
= browser_
->profile();
793 std::string primary_account
=
794 SigninManagerFactory::GetForProfile(profile
)->GetAuthenticatedUsername();
795 DCHECK(!primary_account
.empty());
796 std::vector
<std::string
>accounts
=
797 profiles::GetSecondaryAccountsForProfile(profile
, primary_account
);
799 // The primary account should always be listed first.
800 // TODO(rogerta): we still need to further differentiate the primary account
801 // from the others in the UI, so more work is likely required here:
803 CreateAccountButton(layout
, primary_account
, true);
804 for (size_t i
= 0; i
< accounts
.size(); ++i
)
805 CreateAccountButton(layout
, accounts
[i
], false);
807 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
809 add_account_button_
= new views::BlueButton(
811 l10n_util::GetStringFUTF16(IDS_PROFILES_PROFILE_ADD_ACCOUNT_BUTTON
,
813 layout
->StartRow(1, 0);
814 layout
->AddView(add_account_button_
);
818 void ProfileChooserView::CreateAccountButton(views::GridLayout
* layout
,
819 const std::string
& account
,
820 bool is_primary_account
) {
821 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
822 // Use a MenuButtonListener and not a regular ButtonListener to be
823 // able to distinguish between the unnamed "other profile" buttons and the
824 // unnamed "multiple accounts" buttons.
825 views::MenuButton
* email_button
= new views::MenuButton(
827 gfx::ElideEmail(base::UTF8ToUTF16(account
),
828 rb
->GetFontList(ui::ResourceBundle::BaseFont
),
830 is_primary_account
? NULL
: this, // Cannot delete the primary account.
831 !is_primary_account
);
832 email_button
->set_border(views::Border::CreateEmptyBorder(0, 0, 0, 0));
833 if (!is_primary_account
) {
834 email_button
->set_menu_marker(
835 rb
->GetImageNamed(IDR_CLOSE_1
).ToImageSkia());
836 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
838 layout
->StartRow(1, 0);
839 layout
->AddView(email_button
);
841 // Save the original email address, as the button text could be elided.
842 current_profile_accounts_map_
[email_button
] = account
;