profile_chooser_view.cc revision 0de6073388f4e2780db8536178b129cd8f6ab386
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/profile_chooser_view.h" 6 7#include "base/prefs/pref_service.h" 8#include "base/strings/utf_string_conversions.h" 9#include "chrome/browser/browser_process.h" 10#include "chrome/browser/lifetime/application_lifetime.h" 11#include "chrome/browser/profiles/profile_avatar_icon_util.h" 12#include "chrome/browser/profiles/profile_info_cache.h" 13#include "chrome/browser/profiles/profile_manager.h" 14#include "chrome/browser/profiles/profile_metrics.h" 15#include "chrome/browser/profiles/profile_window.h" 16#include "chrome/browser/profiles/profiles_state.h" 17#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 18#include "chrome/browser/signin/signin_manager_factory.h" 19#include "chrome/browser/signin/signin_promo.h" 20#include "chrome/browser/ui/browser.h" 21#include "chrome/browser/ui/browser_commands.h" 22#include "chrome/browser/ui/browser_dialogs.h" 23#include "chrome/browser/ui/chrome_pages.h" 24#include "chrome/browser/ui/singleton_tabs.h" 25#include "chrome/browser/ui/views/profiles/user_manager_view.h" 26#include "chrome/common/pref_names.h" 27#include "chrome/common/url_constants.h" 28#include "components/signin/core/browser/mutable_profile_oauth2_token_service.h" 29#include "components/signin/core/browser/profile_oauth2_token_service.h" 30#include "components/signin/core/browser/signin_manager.h" 31#include "components/signin/core/common/profile_management_switches.h" 32#include "grit/chromium_strings.h" 33#include "grit/generated_resources.h" 34#include "grit/theme_resources.h" 35#include "third_party/skia/include/core/SkColor.h" 36#include "ui/base/l10n/l10n_util.h" 37#include "ui/base/resource/resource_bundle.h" 38#include "ui/gfx/canvas.h" 39#include "ui/gfx/image/image.h" 40#include "ui/gfx/image/image_skia.h" 41#include "ui/gfx/text_elider.h" 42#include "ui/native_theme/native_theme.h" 43#include "ui/views/controls/button/blue_button.h" 44#include "ui/views/controls/button/image_button.h" 45#include "ui/views/controls/button/label_button.h" 46#include "ui/views/controls/button/menu_button.h" 47#include "ui/views/controls/label.h" 48#include "ui/views/controls/link.h" 49#include "ui/views/controls/separator.h" 50#include "ui/views/controls/styled_label.h" 51#include "ui/views/controls/textfield/textfield.h" 52#include "ui/views/controls/webview/webview.h" 53#include "ui/views/layout/grid_layout.h" 54#include "ui/views/layout/layout_constants.h" 55#include "ui/views/widget/widget.h" 56 57namespace { 58 59// Helpers -------------------------------------------------------------------- 60 61const int kFixedMenuWidth = 250; 62const int kButtonHeight = 29; 63const int kProfileAvatarTutorialShowMax = 1; 64const int kFixedGaiaViewHeight = 400; 65const int kFixedGaiaViewWidth = 360; 66const int kFixedAccountRemovalViewWidth = 280; 67const int kFixedEndPreviewViewWidth = 280; 68const int kLargeImageSide = 88; 69 70// Creates a GridLayout with a single column. This ensures that all the child 71// views added get auto-expanded to fill the full width of the bubble. 72views::GridLayout* CreateSingleColumnLayout(views::View* view, int width) { 73 views::GridLayout* layout = new views::GridLayout(view); 74 view->SetLayoutManager(layout); 75 76 views::ColumnSet* columns = layout->AddColumnSet(0); 77 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, 78 views::GridLayout::FIXED, width, width); 79 return layout; 80} 81 82views::Link* CreateLink(const base::string16& link_text, 83 views::LinkListener* listener) { 84 views::Link* link_button = new views::Link(link_text); 85 link_button->SetHorizontalAlignment(gfx::ALIGN_LEFT); 86 link_button->SetUnderline(false); 87 link_button->set_listener(listener); 88 return link_button; 89} 90 91gfx::ImageSkia CreateSquarePlaceholderImage(int size) { 92 SkBitmap bitmap; 93 bitmap.setConfig(SkBitmap::kA8_Config, size, size); 94 bitmap.allocPixels(); 95 bitmap.eraseARGB(0, 0, 0, 0); 96 return gfx::ImageSkia::CreateFrom1xBitmap(bitmap); 97} 98 99// BackgroundColorHoverButton ------------------------------------------------- 100 101// A custom button that allows for setting a background color when hovered over. 102class BackgroundColorHoverButton : public views::LabelButton { 103 public: 104 BackgroundColorHoverButton(views::ButtonListener* listener, 105 const base::string16& text, 106 const gfx::ImageSkia& normal_icon, 107 const gfx::ImageSkia& hover_icon); 108 virtual ~BackgroundColorHoverButton(); 109 110 private: 111 // views::LabelButton: 112 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; 113 114 DISALLOW_COPY_AND_ASSIGN(BackgroundColorHoverButton); 115}; 116 117BackgroundColorHoverButton::BackgroundColorHoverButton( 118 views::ButtonListener* listener, 119 const base::string16& text, 120 const gfx::ImageSkia& normal_icon, 121 const gfx::ImageSkia& hover_icon) 122 : views::LabelButton(listener, text) { 123 SetBorder(views::Border::CreateEmptyBorder(0, views::kButtonHEdgeMarginNew, 124 0, views::kButtonHEdgeMarginNew)); 125 set_min_size(gfx::Size(0, kButtonHeight)); 126 SetImage(STATE_NORMAL, normal_icon); 127 SetImage(STATE_HOVERED, hover_icon); 128 SetImage(STATE_PRESSED, hover_icon); 129} 130 131BackgroundColorHoverButton::~BackgroundColorHoverButton() {} 132 133void BackgroundColorHoverButton::OnPaint(gfx::Canvas* canvas) { 134 if ((state() == STATE_PRESSED) || (state() == STATE_HOVERED) || HasFocus()) { 135 canvas->DrawColor(GetNativeTheme()->GetSystemColor( 136 ui::NativeTheme::kColorId_ButtonHoverBackgroundColor)); 137 } 138 LabelButton::OnPaint(canvas); 139} 140 141} // namespace 142 143 144// EditableProfilePhoto ------------------------------------------------- 145 146// A custom Image control that shows a "change" button when moused over. 147class EditableProfilePhoto : public views::ImageView { 148 public: 149 EditableProfilePhoto(views::ButtonListener* listener, 150 const gfx::Image& icon, 151 bool is_editing_allowed, 152 const gfx::Rect& bounds) 153 : views::ImageView(), 154 change_photo_button_(NULL) { 155 gfx::Image image = profiles::GetSizedAvatarIcon( 156 icon, true, kLargeImageSide, kLargeImageSide); 157 SetImage(image.ToImageSkia()); 158 SetBoundsRect(bounds); 159 160 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 161 views::ImageView* frame_overlay = new views::ImageView(); 162 frame_overlay->SetImage(rb->GetImageNamed( 163 IDR_ICON_PROFILES_AVATAR_PHOTO_FRAME).ToImageSkia()); 164 frame_overlay->SetVerticalAlignment(views::ImageView::CENTER); 165 frame_overlay->SetBoundsRect(bounds); 166 AddChildView(frame_overlay); 167 168 if (!is_editing_allowed) 169 return; 170 171 set_notify_enter_exit_on_child(true); 172 173 // Button overlay that appears when hovering over the image. 174 change_photo_button_ = new views::LabelButton(listener, base::string16()); 175 change_photo_button_->SetHorizontalAlignment(gfx::ALIGN_CENTER); 176 change_photo_button_->SetBorder(views::Border::NullBorder()); 177 178 const SkColor kBackgroundColor = SkColorSetARGB(65, 255, 255, 255); 179 change_photo_button_->set_background( 180 views::Background::CreateSolidBackground(kBackgroundColor)); 181 change_photo_button_->SetImage(views::LabelButton::STATE_NORMAL, 182 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_CAMERA)); 183 184 change_photo_button_->SetBoundsRect(bounds); 185 change_photo_button_->SetVisible(false); 186 AddChildView(change_photo_button_); 187 } 188 189 views::LabelButton* change_photo_button() { return change_photo_button_; } 190 191 private: 192 // views::View: 193 virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE { 194 if (change_photo_button_) 195 change_photo_button_->SetVisible(true); 196 } 197 198 virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE { 199 if (change_photo_button_) 200 change_photo_button_->SetVisible(false); 201 } 202 203 // Button that is shown when hovering over the image view. Can be NULL if 204 // the photo isn't allowed to be edited (e.g. for guest profiles). 205 views::LabelButton* change_photo_button_; 206 207 DISALLOW_COPY_AND_ASSIGN(EditableProfilePhoto); 208}; 209 210 211// EditableProfileName ------------------------------------------------- 212 213// A custom text control that turns into a textfield for editing when clicked. 214class EditableProfileName : public views::LabelButton, 215 public views::ButtonListener { 216 public: 217 EditableProfileName(views::TextfieldController* controller, 218 const base::string16& text, 219 bool is_editing_allowed) 220 : views::LabelButton(this, text), 221 profile_name_textfield_(NULL) { 222 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 223 const gfx::FontList& medium_font_list = 224 rb->GetFontList(ui::ResourceBundle::MediumFont); 225 SetFontList(medium_font_list); 226 SetHorizontalAlignment(gfx::ALIGN_CENTER); 227 228 if (!is_editing_allowed) 229 return; 230 231 // Show an "edit" pencil icon when hovering over. In the default state, 232 // we need to create an empty placeholder of the correct size, so that 233 // the text doesn't jump around when the hovered icon appears. 234 gfx::ImageSkia hover_image = 235 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_HOVER); 236 SetImage(STATE_NORMAL, CreateSquarePlaceholderImage(hover_image.width())); 237 SetImage(STATE_HOVERED, hover_image); 238 SetImage(STATE_PRESSED, 239 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_PRESSED)); 240 // To center the text, we need to offest it by the width of the icon we 241 // are adding. We need to also add a small top/bottom padding to account 242 // for the textfield's border. 243 SetBorder(views::Border::CreateEmptyBorder(2, hover_image.width(), 2, 0)); 244 245 // Textfield that overlaps the button. 246 profile_name_textfield_ = new views::Textfield(); 247 profile_name_textfield_->set_controller(controller); 248 profile_name_textfield_->SetFontList(medium_font_list); 249 profile_name_textfield_->SetHorizontalAlignment(gfx::ALIGN_CENTER); 250 251 profile_name_textfield_->SetVisible(false); 252 AddChildView(profile_name_textfield_); 253 } 254 255 views::Textfield* profile_name_textfield() { 256 return profile_name_textfield_; 257 } 258 259 // Hide the editable textfield to show the profile name button instead. 260 void ShowReadOnlyView() { 261 if (profile_name_textfield_) 262 profile_name_textfield_->SetVisible(false); 263 } 264 265 private: 266 // views::ButtonListener: 267 virtual void ButtonPressed(views::Button* sender, 268 const ui::Event& event) OVERRIDE { 269 if (profile_name_textfield_) { 270 profile_name_textfield_->SetVisible(true); 271 profile_name_textfield_->SetText(GetText()); 272 profile_name_textfield_->SelectAll(false); 273 profile_name_textfield_->RequestFocus(); 274 } 275 } 276 277 // views::LabelButton: 278 virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE { 279 // Override CustomButton's implementation, which presses the button when 280 // you press space and clicks it when you release space, as the space can be 281 // part of the new profile name typed in the textfield. 282 return false; 283 } 284 285 virtual void Layout() OVERRIDE { 286 if (profile_name_textfield_) 287 profile_name_textfield_->SetBounds(0, 0, width(), height()); 288 // This layout trick keeps the text left-aligned and the icon right-aligned. 289 SetHorizontalAlignment(gfx::ALIGN_RIGHT); 290 views::LabelButton::Layout(); 291 label()->SetHorizontalAlignment(gfx::ALIGN_CENTER); 292 } 293 294 // Textfield that is shown when editing the profile name. Can be NULL if 295 // the profile name isn't allowed to be edited (e.g. for guest profiles). 296 views::Textfield* profile_name_textfield_; 297 298 DISALLOW_COPY_AND_ASSIGN(EditableProfileName); 299}; 300 301// A title card with one back button right aligned and one label center aligned. 302class TitleCard : public views::View { 303 public: 304 TitleCard(int message_id, views::ButtonListener* listener, 305 views::ImageButton** back_button) { 306 back_button_ = new views::ImageButton(listener); 307 back_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT, 308 views::ImageButton::ALIGN_MIDDLE); 309 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 310 back_button_->SetImage(views::ImageButton::STATE_NORMAL, 311 rb->GetImageSkiaNamed(IDR_BACK)); 312 back_button_->SetImage(views::ImageButton::STATE_HOVERED, 313 rb->GetImageSkiaNamed(IDR_BACK_H)); 314 back_button_->SetImage(views::ImageButton::STATE_PRESSED, 315 rb->GetImageSkiaNamed(IDR_BACK_P)); 316 back_button_->SetImage(views::ImageButton::STATE_DISABLED, 317 rb->GetImageSkiaNamed(IDR_BACK_D)); 318 *back_button = back_button_; 319 320 title_label_ = new views::Label(l10n_util::GetStringUTF16(message_id)); 321 title_label_->SetHorizontalAlignment(gfx::ALIGN_CENTER); 322 const gfx::FontList& medium_font_list = 323 rb->GetFontList(ui::ResourceBundle::MediumFont); 324 title_label_->SetFontList(medium_font_list); 325 326 AddChildView(back_button_); 327 AddChildView(title_label_); 328 } 329 330 // Creates a new view that has the |title_card| with padding at the top, an 331 // edge-to-edge separator below, and the specified |view| at the bottom. 332 static views::View* AddPaddedTitleCard(views::View* view, 333 TitleCard* title_card, 334 int width) { 335 views::View* titled_view = new views::View(); 336 views::GridLayout* layout = new views::GridLayout(titled_view); 337 titled_view->SetLayoutManager(layout); 338 339 // Column set 0 is a single column layout with horizontal padding at left 340 // and right, and column set 1 is a single column layout with no padding. 341 views::ColumnSet* columns = layout->AddColumnSet(0); 342 columns->AddPaddingColumn(1, views::kButtonHEdgeMarginNew); 343 int available_width = width - 2 * views::kButtonHEdgeMarginNew; 344 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, 345 views::GridLayout::FIXED, available_width, available_width); 346 columns->AddPaddingColumn(1, views::kButtonHEdgeMarginNew); 347 layout->AddColumnSet(1)->AddColumn(views::GridLayout::FILL, 348 views::GridLayout::FILL, 0,views::GridLayout::FIXED, width, width); 349 350 layout->StartRowWithPadding(1, 0, 0, views::kButtonVEdgeMarginNew); 351 layout->AddView(title_card); 352 layout->StartRowWithPadding(1, 1, 0, views::kRelatedControlVerticalSpacing); 353 layout->AddView(new views::Separator(views::Separator::HORIZONTAL)); 354 355 layout->StartRow(1, 1); 356 layout->AddView(view); 357 358 return titled_view; 359 } 360 361 private: 362 virtual void Layout() OVERRIDE{ 363 back_button_->SetBounds( 364 0, 0, back_button_->GetPreferredSize().width(), height()); 365 title_label_->SetBoundsRect(GetContentsBounds()); 366 } 367 368 virtual gfx::Size GetPreferredSize() OVERRIDE{ 369 int height = std::max(title_label_->GetPreferredSize().height(), 370 back_button_->GetPreferredSize().height()); 371 return gfx::Size(width(), height); 372 } 373 374 views::ImageButton* back_button_; 375 views::Label* title_label_; 376 377 DISALLOW_COPY_AND_ASSIGN(TitleCard); 378}; 379 380// ProfileChooserView --------------------------------------------------------- 381 382// static 383ProfileChooserView* ProfileChooserView::profile_bubble_ = NULL; 384bool ProfileChooserView::close_on_deactivate_for_testing_ = true; 385 386// static 387void ProfileChooserView::ShowBubble( 388 BubbleViewMode view_mode, 389 views::View* anchor_view, 390 views::BubbleBorder::Arrow arrow, 391 views::BubbleBorder::BubbleAlignment border_alignment, 392 const gfx::Rect& anchor_rect, 393 Browser* browser) { 394 profile_bubble_ = new ProfileChooserView(anchor_view, arrow, anchor_rect, 395 browser, view_mode); 396 views::BubbleDelegateView::CreateBubble(profile_bubble_); 397 profile_bubble_->set_close_on_deactivate(close_on_deactivate_for_testing_); 398 profile_bubble_->SetAlignment(border_alignment); 399 profile_bubble_->GetWidget()->Show(); 400 profile_bubble_->SetArrowPaintType(views::BubbleBorder::PAINT_NONE); 401} 402 403// static 404bool ProfileChooserView::IsShowing() { 405 return profile_bubble_ != NULL; 406} 407 408// static 409void ProfileChooserView::Hide() { 410 if (IsShowing()) 411 profile_bubble_->GetWidget()->Close(); 412} 413 414ProfileChooserView::ProfileChooserView(views::View* anchor_view, 415 views::BubbleBorder::Arrow arrow, 416 const gfx::Rect& anchor_rect, 417 Browser* browser, 418 BubbleViewMode view_mode) 419 : BubbleDelegateView(anchor_view, arrow), 420 browser_(browser), 421 view_mode_(view_mode), 422 tutorial_mode_(TUTORIAL_MODE_NONE) { 423 // Reset the default margins inherited from the BubbleDelegateView. 424 set_margins(gfx::Insets()); 425 426 ResetView(); 427 428 avatar_menu_.reset(new AvatarMenu( 429 &g_browser_process->profile_manager()->GetProfileInfoCache(), 430 this, 431 browser_)); 432 avatar_menu_->RebuildMenu(); 433 434 ProfileOAuth2TokenService* oauth2_token_service = 435 ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile()); 436 if (oauth2_token_service) 437 oauth2_token_service->AddObserver(this); 438} 439 440ProfileChooserView::~ProfileChooserView() { 441 ProfileOAuth2TokenService* oauth2_token_service = 442 ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile()); 443 if (oauth2_token_service) 444 oauth2_token_service->RemoveObserver(this); 445} 446 447void ProfileChooserView::ResetView() { 448 question_mark_button_ = NULL; 449 manage_accounts_link_ = NULL; 450 signin_current_profile_link_ = NULL; 451 users_button_ = NULL; 452 lock_button_ = NULL; 453 add_account_link_ = NULL; 454 current_profile_photo_ = NULL; 455 current_profile_name_ = NULL; 456 tutorial_ok_button_ = NULL; 457 tutorial_learn_more_link_ = NULL; 458 tutorial_enable_new_profile_management_button_ = NULL; 459 tutorial_end_preview_link_ = NULL; 460 tutorial_send_feedback_button_ = NULL; 461 end_preview_and_relaunch_button_ = NULL; 462 end_preview_cancel_button_ = NULL; 463 remove_account_button_ = NULL; 464 account_removal_cancel_button_ = NULL; 465 gaia_signin_cancel_button_ = NULL; 466 open_other_profile_indexes_map_.clear(); 467 current_profile_accounts_map_.clear(); 468 tutorial_mode_ = TUTORIAL_MODE_NONE; 469} 470 471void ProfileChooserView::Init() { 472 ShowView(view_mode_, avatar_menu_.get()); 473} 474 475void ProfileChooserView::OnAvatarMenuChanged( 476 AvatarMenu* avatar_menu) { 477 // Refresh the view with the new menu. We can't just update the local copy 478 // as this may have been triggered by a sign out action, in which case 479 // the view is being destroyed. 480 ShowView(BUBBLE_VIEW_MODE_PROFILE_CHOOSER, avatar_menu); 481} 482 483void ProfileChooserView::OnRefreshTokenAvailable( 484 const std::string& account_id) { 485 // Refresh the account management view when a new account is added to the 486 // profile. 487 if (view_mode_ == BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT || 488 view_mode_ == BUBBLE_VIEW_MODE_GAIA_SIGNIN || 489 view_mode_ == BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT) { 490 ShowView(BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, avatar_menu_.get()); 491 } 492} 493 494void ProfileChooserView::OnRefreshTokenRevoked(const std::string& account_id) { 495 // Refresh the account management view when an account is removed from the 496 // profile. 497 if (view_mode_ == BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT) 498 ShowView(BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, avatar_menu_.get()); 499} 500 501void ProfileChooserView::ShowView(BubbleViewMode view_to_display, 502 AvatarMenu* avatar_menu) { 503 // The account management view should only be displayed if the active profile 504 // is signed in. 505 if (view_to_display == BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT) { 506 const AvatarMenu::Item& active_item = avatar_menu->GetItemAt( 507 avatar_menu->GetActiveProfileIndex()); 508 DCHECK(active_item.signed_in); 509 } 510 511 // Records the last tutorial mode. 512 TutorialMode last_tutorial_mode = tutorial_mode_; 513 ResetView(); 514 RemoveAllChildViews(true); 515 view_mode_ = view_to_display; 516 517 views::GridLayout* layout; 518 views::View* sub_view; 519 switch (view_mode_) { 520 case BUBBLE_VIEW_MODE_GAIA_SIGNIN: 521 case BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT: 522 layout = CreateSingleColumnLayout(this, kFixedGaiaViewWidth); 523 sub_view = CreateGaiaSigninView( 524 view_mode_ == BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT); 525 break; 526 case BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL: 527 layout = CreateSingleColumnLayout(this, kFixedAccountRemovalViewWidth); 528 sub_view = CreateAccountRemovalView(); 529 break; 530 case BUBBLE_VIEW_MODE_END_PREVIEW: 531 layout = CreateSingleColumnLayout(this, kFixedEndPreviewViewWidth); 532 sub_view = CreateEndPreviewView(); 533 break; 534 default: 535 layout = CreateSingleColumnLayout(this, kFixedMenuWidth); 536 sub_view = CreateProfileChooserView(avatar_menu, last_tutorial_mode); 537 } 538 sub_view->set_background(views::Background::CreateSolidBackground( 539 GetNativeTheme()->GetSystemColor( 540 ui::NativeTheme::kColorId_DialogBackground))); 541 542 layout->StartRow(1, 0); 543 layout->AddView(sub_view); 544 Layout(); 545 if (GetBubbleFrameView()) 546 SizeToContents(); 547} 548 549void ProfileChooserView::WindowClosing() { 550 DCHECK_EQ(profile_bubble_, this); 551 profile_bubble_ = NULL; 552} 553 554void ProfileChooserView::ButtonPressed(views::Button* sender, 555 const ui::Event& event) { 556 // Disable button after clicking so that it doesn't get clicked twice and 557 // start a second action... which can crash Chrome. But don't disable if it 558 // has no parent (like in tests) because that will also crash. 559 if (sender->parent()) 560 sender->SetEnabled(false); 561 562 if (sender == users_button_) { 563 profiles::ShowUserManagerMaybeWithTutorial(browser_->profile()); 564 // If this is a guest session, also close all the guest browser windows. 565 if (browser_->profile()->IsGuestSession()) 566 profiles::CloseGuestProfileWindows(); 567 } else if (sender == lock_button_) { 568 profiles::LockProfile(browser_->profile()); 569 } else if (sender == tutorial_ok_button_) { 570 // If the user manually dismissed the tutorial, never show it again by 571 // setting the number of times shown to the maximum plus 1, so that later we 572 // could distinguish between the dismiss case and the case when the tutorial 573 // is indeed shown for the maximum number of times. 574 browser_->profile()->GetPrefs()->SetInteger( 575 prefs::kProfileAvatarTutorialShown, kProfileAvatarTutorialShowMax + 1); 576 577 ProfileMetrics::LogProfileUpgradeEnrollment( 578 ProfileMetrics::PROFILE_ENROLLMENT_CLOSE_WELCOME_CARD); 579 ShowView(BUBBLE_VIEW_MODE_PROFILE_CHOOSER, avatar_menu_.get()); 580 } else if (sender == tutorial_enable_new_profile_management_button_) { 581 ProfileMetrics::LogProfileUpgradeEnrollment( 582 ProfileMetrics::PROFILE_ENROLLMENT_ACCEPT_NEW_PROFILE_MGMT); 583 profiles::EnableNewProfileManagementPreview(); 584 } else if (sender == remove_account_button_) { 585 RemoveAccount(); 586 } else if (sender == account_removal_cancel_button_) { 587 account_id_to_remove_.clear(); 588 ShowView(BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, avatar_menu_.get()); 589 } else if (sender == gaia_signin_cancel_button_) { 590 std::string primary_account = 591 SigninManagerFactory::GetForProfile(browser_->profile())-> 592 GetAuthenticatedUsername(); 593 ShowView(primary_account.empty() ? BUBBLE_VIEW_MODE_PROFILE_CHOOSER : 594 BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, 595 avatar_menu_.get()); 596 } else if (sender == question_mark_button_) { 597 tutorial_mode_ = TUTORIAL_MODE_SEND_FEEDBACK; 598 ShowView(BUBBLE_VIEW_MODE_PROFILE_CHOOSER, avatar_menu_.get()); 599 } else if (sender == tutorial_send_feedback_button_) { 600 chrome::OpenFeedbackDialog(browser_); 601 } else if (sender == end_preview_and_relaunch_button_) { 602 profiles::DisableNewProfileManagementPreview(); 603 } else if (sender == end_preview_cancel_button_) { 604 tutorial_mode_ = TUTORIAL_MODE_SEND_FEEDBACK; 605 ShowView(BUBBLE_VIEW_MODE_PROFILE_CHOOSER, avatar_menu_.get()); 606 } else if (current_profile_photo_ && 607 sender == current_profile_photo_->change_photo_button()) { 608 avatar_menu_->EditProfile(avatar_menu_->GetActiveProfileIndex()); 609 } else if (sender == signin_current_profile_link_) { 610 // Only show the inline signin if the new UI flag is flipped. Otherwise, 611 // use the tab signin page. 612 if (switches::IsNewProfileManagement()) 613 ShowView(BUBBLE_VIEW_MODE_GAIA_SIGNIN, avatar_menu_.get()); 614 else 615 chrome::ShowBrowserSignin(browser_, signin::SOURCE_MENU); 616 } else { 617 // Either one of the "other profiles", or one of the profile accounts 618 // buttons was pressed. 619 ButtonIndexes::const_iterator profile_match = 620 open_other_profile_indexes_map_.find(sender); 621 if (profile_match != open_other_profile_indexes_map_.end()) { 622 avatar_menu_->SwitchToProfile( 623 profile_match->second, 624 ui::DispositionFromEventFlags(event.flags()) == NEW_WINDOW, 625 ProfileMetrics::SWITCH_PROFILE_ICON); 626 } else { 627 // This was a profile accounts button. 628 AccountButtonIndexes::const_iterator account_match = 629 current_profile_accounts_map_.find(sender); 630 DCHECK(account_match != current_profile_accounts_map_.end()); 631 account_id_to_remove_ = account_match->second; 632 ShowView(BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL, avatar_menu_.get()); 633 } 634 } 635} 636 637void ProfileChooserView::RemoveAccount() { 638 DCHECK(!account_id_to_remove_.empty()); 639 MutableProfileOAuth2TokenService* oauth2_token_service = 640 ProfileOAuth2TokenServiceFactory::GetPlatformSpecificForProfile( 641 browser_->profile()); 642 if (oauth2_token_service) 643 oauth2_token_service->RevokeCredentials(account_id_to_remove_); 644 account_id_to_remove_.clear(); 645 646 ShowView(BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, avatar_menu_.get()); 647} 648 649void ProfileChooserView::LinkClicked(views::Link* sender, int event_flags) { 650 if (sender == manage_accounts_link_) { 651 // This link can either mean show/hide the account management view, 652 // depending on which view it is displayed. ShowView() will DCHECK if 653 // the account management view is displayed for non signed-in users. 654 ShowView( 655 view_mode_ == BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT ? 656 BUBBLE_VIEW_MODE_PROFILE_CHOOSER : 657 BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, 658 avatar_menu_.get()); 659 } else if (sender == add_account_link_) { 660 ShowView(BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT, avatar_menu_.get()); 661 } else if (sender == tutorial_learn_more_link_) { 662 ProfileMetrics::LogProfileUpgradeEnrollment( 663 ProfileMetrics::PROFILE_ENROLLMENT_LAUNCH_LEARN_MORE); 664 // TODO(guohui): update |learn_more_url| once it is decided. 665 const GURL lear_more_url("https://support.google.com/chrome/?hl=en#to"); 666 chrome::NavigateParams params( 667 browser_->profile(), 668 lear_more_url, 669 content::PAGE_TRANSITION_LINK); 670 params.disposition = NEW_FOREGROUND_TAB; 671 chrome::Navigate(¶ms); 672 } else { 673 DCHECK(sender == tutorial_end_preview_link_); 674 ShowView(BUBBLE_VIEW_MODE_END_PREVIEW, avatar_menu_.get()); 675 } 676} 677 678void ProfileChooserView::StyledLabelLinkClicked( 679 const gfx::Range& range, int event_flags) { 680 chrome::ShowSettings(browser_); 681} 682 683bool ProfileChooserView::HandleKeyEvent(views::Textfield* sender, 684 const ui::KeyEvent& key_event) { 685 views::Textfield* name_textfield = 686 current_profile_name_->profile_name_textfield(); 687 DCHECK(sender == name_textfield); 688 689 if (key_event.key_code() == ui::VKEY_RETURN || 690 key_event.key_code() == ui::VKEY_TAB) { 691 // Pressing Tab/Enter commits the new profile name, unless it's empty. 692 base::string16 new_profile_name = name_textfield->text(); 693 if (new_profile_name.empty()) 694 return true; 695 696 const AvatarMenu::Item& active_item = avatar_menu_->GetItemAt( 697 avatar_menu_->GetActiveProfileIndex()); 698 Profile* profile = g_browser_process->profile_manager()->GetProfile( 699 active_item.profile_path); 700 DCHECK(profile); 701 702 if (profile->IsManaged()) 703 return true; 704 705 profiles::UpdateProfileName(profile, new_profile_name); 706 current_profile_name_->ShowReadOnlyView(); 707 return true; 708 } 709 return false; 710} 711 712views::View* ProfileChooserView::CreateProfileChooserView( 713 AvatarMenu* avatar_menu, 714 TutorialMode last_tutorial_mode) { 715 // TODO(guohui, noms): the view should be customized based on whether new 716 // profile management preview is enabled or not. 717 718 views::View* view = new views::View(); 719 views::GridLayout* layout = CreateSingleColumnLayout(view, kFixedMenuWidth); 720 // Separate items into active and alternatives. 721 Indexes other_profiles; 722 views::View* tutorial_view = NULL; 723 views::View* current_profile_view = NULL; 724 views::View* current_profile_accounts = NULL; 725 views::View* option_buttons_view = NULL; 726 bool is_new_profile_management = switches::IsNewProfileManagement(); 727 for (size_t i = 0; i < avatar_menu->GetNumberOfItems(); ++i) { 728 const AvatarMenu::Item& item = avatar_menu->GetItemAt(i); 729 if (item.active) { 730 option_buttons_view = CreateOptionsView(item.signed_in); 731 current_profile_view = CreateCurrentProfileView(item, false); 732 if (view_mode_ == BUBBLE_VIEW_MODE_PROFILE_CHOOSER) { 733 if (is_new_profile_management) { 734 tutorial_view = last_tutorial_mode == TUTORIAL_MODE_SEND_FEEDBACK ? 735 CreateSendPreviewFeedbackView() : 736 CreatePreviewEnabledTutorialView( 737 item, last_tutorial_mode == TUTORIAL_MODE_PREVIEW_ENABLED); 738 } else { 739 tutorial_view = CreateNewProfileManagementPreviewView(); 740 } 741 } else { 742 current_profile_accounts = CreateCurrentProfileAccountsView(item); 743 } 744 } else { 745 other_profiles.push_back(i); 746 } 747 } 748 749 if (tutorial_view) { 750 // Be sure not to track the tutorial display on View refresh, and only count 751 // the preview-promo view, shown when New Profile Management is off. 752 if (tutorial_mode_ != last_tutorial_mode && !is_new_profile_management) { 753 ProfileMetrics::LogProfileUpgradeEnrollment( 754 ProfileMetrics::PROFILE_ENROLLMENT_SHOW_PREVIEW_PROMO); 755 } 756 layout->StartRow(1, 0); 757 layout->AddView(tutorial_view); 758 } 759 760 if (!current_profile_view) { 761 // Guest windows don't have an active profile. 762 current_profile_view = CreateGuestProfileView(); 763 option_buttons_view = CreateOptionsView(false); 764 } 765 766 layout->StartRow(1, 0); 767 layout->AddView(current_profile_view); 768 769 if (view_mode_ == BUBBLE_VIEW_MODE_PROFILE_CHOOSER) { 770 layout->StartRow(1, 0); 771 if (switches::IsFastUserSwitching()) 772 layout->AddView(CreateOtherProfilesView(other_profiles)); 773 } else { 774 DCHECK(current_profile_accounts); 775 layout->StartRow(0, 0); 776 layout->AddView(new views::Separator(views::Separator::HORIZONTAL)); 777 layout->StartRow(1, 0); 778 layout->AddView(current_profile_accounts); 779 } 780 781 layout->StartRow(0, 0); 782 layout->AddView(new views::Separator(views::Separator::HORIZONTAL)); 783 784 // Option buttons. Only available with the new profile management flag. 785 if (option_buttons_view) { 786 layout->StartRow(0, 0); 787 layout->AddView(option_buttons_view); 788 } 789 790 return view; 791} 792 793views::View* ProfileChooserView::CreatePreviewEnabledTutorialView( 794 const AvatarMenu::Item& current_avatar_item, 795 bool tutorial_shown) { 796 if (!switches::IsNewProfileManagementPreviewEnabled()) 797 return NULL; 798 799 Profile* profile = browser_->profile(); 800 const int show_count = profile->GetPrefs()->GetInteger( 801 prefs::kProfileAvatarTutorialShown); 802 // Do not show the tutorial if user has dismissed it. 803 if (show_count > kProfileAvatarTutorialShowMax) 804 return NULL; 805 806 if (!tutorial_shown) { 807 if (show_count == kProfileAvatarTutorialShowMax) 808 return NULL; 809 profile->GetPrefs()->SetInteger( 810 prefs::kProfileAvatarTutorialShown, show_count + 1); 811 } 812 813 return CreateTutorialView( 814 TUTORIAL_MODE_PREVIEW_ENABLED, 815 l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_ENABLED_TUTORIAL_TITLE), 816 l10n_util::GetStringUTF16( 817 IDS_PROFILES_PREVIEW_ENABLED_TUTORIAL_CONTENT_TEXT), 818 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_TUTORIAL_LEARN_MORE), 819 l10n_util::GetStringUTF16(IDS_PROFILES_TUTORIAL_OK_BUTTON), 820 &tutorial_learn_more_link_, 821 &tutorial_ok_button_); 822} 823 824views::View* ProfileChooserView::CreateSendPreviewFeedbackView() { 825 return CreateTutorialView( 826 TUTORIAL_MODE_SEND_FEEDBACK, 827 l10n_util::GetStringUTF16(IDS_PROFILES_FEEDBACK_TUTORIAL_TITLE), 828 l10n_util::GetStringUTF16( 829 IDS_PROFILES_FEEDBACK_TUTORIAL_CONTENT_TEXT), 830 l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW), 831 l10n_util::GetStringUTF16(IDS_PROFILES_SEND_FEEDBACK_BUTTON), 832 &tutorial_end_preview_link_, 833 &tutorial_send_feedback_button_); 834} 835 836views::View* ProfileChooserView::CreateTutorialView( 837 TutorialMode tutorial_mode, 838 const base::string16& title_text, 839 const base::string16& content_text, 840 const base::string16& link_text, 841 const base::string16& button_text, 842 views::Link** link, 843 views::LabelButton** button) { 844 tutorial_mode_ = tutorial_mode; 845 846 views::View* view = new views::View(); 847 view->set_background(views::Background::CreateSolidBackground( 848 profiles::kAvatarTutorialBackgroundColor)); 849 views::GridLayout* layout = CreateSingleColumnLayout(view, 850 kFixedMenuWidth - 2 * views::kButtonHEdgeMarginNew); 851 layout->SetInsets(views::kButtonVEdgeMarginNew, 852 views::kButtonHEdgeMarginNew, 853 views::kButtonVEdgeMarginNew, 854 views::kButtonHEdgeMarginNew); 855 856 // Adds title. 857 views::Label* title_label = new views::Label(title_text); 858 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 859 title_label->SetAutoColorReadabilityEnabled(false); 860 title_label->SetEnabledColor(SK_ColorWHITE); 861 title_label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList( 862 ui::ResourceBundle::MediumFont)); 863 layout->StartRow(1, 0); 864 layout->AddView(title_label); 865 866 // Adds body content. 867 views::Label* content_label = new views::Label(content_text); 868 content_label->SetMultiLine(true); 869 content_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 870 content_label->SetAutoColorReadabilityEnabled(false); 871 content_label->SetEnabledColor(profiles::kAvatarTutorialContentTextColor); 872 layout->StartRowWithPadding(1, 0, 0, views::kRelatedControlVerticalSpacing); 873 layout->AddView(content_label); 874 875 // Adds links and buttons. 876 views::View* button_row = new views::View(); 877 views::GridLayout* button_layout = new views::GridLayout(button_row); 878 views::ColumnSet* button_columns = button_layout->AddColumnSet(0); 879 button_columns->AddColumn(views::GridLayout::LEADING, 880 views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0); 881 button_columns->AddPaddingColumn( 882 1, views::kUnrelatedControlHorizontalSpacing); 883 button_columns->AddColumn(views::GridLayout::TRAILING, 884 views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0); 885 button_row->SetLayoutManager(button_layout); 886 887 *link = CreateLink(link_text, this); 888 (*link)->SetHorizontalAlignment(gfx::ALIGN_LEFT); 889 (*link)->SetAutoColorReadabilityEnabled(false); 890 (*link)->SetEnabledColor(SK_ColorWHITE); 891 button_layout->StartRow(1, 0); 892 button_layout->AddView(*link); 893 894 *button = new views::LabelButton(this, button_text); 895 (*button)->SetHorizontalAlignment(gfx::ALIGN_CENTER); 896 (*button)->SetStyle(views::Button::STYLE_BUTTON); 897 button_layout->AddView(*button); 898 899 layout->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing); 900 layout->AddView(button_row); 901 902 // Adds a padded caret image at the bottom. 903 views::View* padded_caret_view = new views::View(); 904 views::GridLayout* padded_caret_layout = 905 new views::GridLayout(padded_caret_view); 906 views::ColumnSet* padded_columns = padded_caret_layout->AddColumnSet(0); 907 padded_columns->AddPaddingColumn(0, views::kButtonHEdgeMarginNew); 908 padded_columns->AddColumn(views::GridLayout::LEADING, 909 views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0); 910 padded_caret_view->SetLayoutManager(padded_caret_layout); 911 912 views::ImageView* caret_image_view = new views::ImageView(); 913 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 914 caret_image_view->SetImage( 915 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_CARET)); 916 917 padded_caret_layout->StartRow(1, 0); 918 padded_caret_layout->AddView(caret_image_view); 919 920 views::View* view_with_caret = new views::View(); 921 views::GridLayout* layout_with_caret = 922 CreateSingleColumnLayout(view_with_caret, kFixedMenuWidth); 923 layout_with_caret->StartRow(1, 0); 924 layout_with_caret->AddView(view); 925 layout_with_caret->StartRow(1, 0); 926 layout_with_caret->AddView(padded_caret_view); 927 return view_with_caret; 928} 929 930views::View* ProfileChooserView::CreateCurrentProfileView( 931 const AvatarMenu::Item& avatar_item, 932 bool is_guest) { 933 views::View* view = new views::View(); 934 int column_width = kFixedMenuWidth - 2 * views::kButtonHEdgeMarginNew; 935 views::GridLayout* layout = CreateSingleColumnLayout(view, column_width); 936 layout->SetInsets(views::kButtonVEdgeMarginNew, 937 views::kButtonHEdgeMarginNew, 938 views::kUnrelatedControlVerticalSpacing, 939 views::kButtonHEdgeMarginNew); 940 941 // Profile icon, centered. 942 float x_offset = (column_width - kLargeImageSide) / 2; 943 current_profile_photo_ = new EditableProfilePhoto( 944 this, avatar_item.icon, !is_guest, 945 gfx::Rect(x_offset, 0, kLargeImageSide, kLargeImageSide)); 946 947 if (switches::IsNewProfileManagementPreviewEnabled()) { 948 question_mark_button_ = new views::ImageButton(this); 949 question_mark_button_->SetImageAlignment( 950 views::ImageButton::ALIGN_LEFT, views::ImageButton::ALIGN_MIDDLE); 951 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 952 question_mark_button_->SetImage(views::ImageButton::STATE_NORMAL, 953 rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_STABLE)); 954 question_mark_button_->SetImage(views::ImageButton::STATE_HOVERED, 955 rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_HOVER)); 956 question_mark_button_->SetImage(views::ImageButton::STATE_PRESSED, 957 rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_SELECT)); 958 gfx::Size preferred_size = question_mark_button_->GetPreferredSize(); 959 question_mark_button_->SetBounds( 960 0, 0, preferred_size.width(), preferred_size.height()); 961 current_profile_photo_->AddChildView(question_mark_button_); 962 } 963 964 layout->StartRow(1, 0); 965 layout->AddView(current_profile_photo_); 966 967 // Profile name, centered. 968 current_profile_name_ = new EditableProfileName( 969 this, profiles::GetAvatarNameForProfile(browser_->profile()), !is_guest); 970 layout->StartRow(1, 0); 971 layout->AddView(current_profile_name_); 972 973 if (is_guest) 974 return view; 975 976 // The available links depend on the type of profile that is active. 977 layout->StartRow(1, 0); 978 if (avatar_item.signed_in) { 979 if (switches::IsNewProfileManagement()) { 980 base::string16 link_title = l10n_util::GetStringUTF16( 981 view_mode_ == BUBBLE_VIEW_MODE_PROFILE_CHOOSER ? 982 IDS_PROFILES_PROFILE_MANAGE_ACCOUNTS_BUTTON : 983 IDS_PROFILES_PROFILE_HIDE_MANAGE_ACCOUNTS_BUTTON); 984 manage_accounts_link_ = CreateLink(link_title, this); 985 manage_accounts_link_->SetHorizontalAlignment(gfx::ALIGN_CENTER); 986 layout->AddView(manage_accounts_link_); 987 } else { 988 views::Label* email_label = new views::Label(avatar_item.sync_state); 989 email_label->SetHorizontalAlignment(gfx::ALIGN_CENTER); 990 layout->AddView(email_label); 991 } 992 } else { 993 signin_current_profile_link_ = new views::BlueButton( 994 this, l10n_util::GetStringFUTF16(IDS_SYNC_START_SYNC_BUTTON_LABEL, 995 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME))); 996 layout->AddView(signin_current_profile_link_); 997 } 998 999 return view; 1000} 1001 1002views::View* ProfileChooserView::CreateGuestProfileView() { 1003 gfx::Image guest_icon = 1004 ui::ResourceBundle::GetSharedInstance().GetImageNamed( 1005 profiles::GetPlaceholderAvatarIconResourceID()); 1006 AvatarMenu::Item guest_avatar_item(0, 0, guest_icon); 1007 guest_avatar_item.active = true; 1008 guest_avatar_item.name = l10n_util::GetStringUTF16( 1009 IDS_PROFILES_GUEST_PROFILE_NAME); 1010 guest_avatar_item.signed_in = false; 1011 1012 return CreateCurrentProfileView(guest_avatar_item, true); 1013} 1014 1015views::View* ProfileChooserView::CreateOtherProfilesView( 1016 const Indexes& avatars_to_show) { 1017 views::View* view = new views::View(); 1018 views::GridLayout* layout = CreateSingleColumnLayout(view, kFixedMenuWidth); 1019 1020 int num_avatars_to_show = avatars_to_show.size(); 1021 for (int i = 0; i < num_avatars_to_show; ++i) { 1022 const size_t index = avatars_to_show[i]; 1023 const AvatarMenu::Item& item = avatar_menu_->GetItemAt(index); 1024 const int kSmallImageSide = 32; 1025 1026 gfx::Image image = profiles::GetSizedAvatarIcon( 1027 item.icon, true, kSmallImageSide, kSmallImageSide); 1028 1029 views::LabelButton* button = new BackgroundColorHoverButton( 1030 this, 1031 item.name, 1032 *image.ToImageSkia(), 1033 *image.ToImageSkia()); 1034 button->set_min_size(gfx::Size( 1035 0, kButtonHeight + views::kRelatedControlVerticalSpacing)); 1036 1037 open_other_profile_indexes_map_[button] = index; 1038 1039 layout->StartRow(1, 0); 1040 layout->AddView(new views::Separator(views::Separator::HORIZONTAL)); 1041 layout->StartRow(1, 0); 1042 layout->AddView(button); 1043 } 1044 1045 return view; 1046} 1047 1048views::View* ProfileChooserView::CreateOptionsView(bool enable_lock) { 1049 if (!switches::IsNewProfileManagement()) 1050 return NULL; 1051 1052 views::View* view = new views::View(); 1053 views::GridLayout* layout; 1054 1055 // Only signed-in users have the ability to lock. 1056 if (enable_lock) { 1057 layout = new views::GridLayout(view); 1058 views::ColumnSet* columns = layout->AddColumnSet(0); 1059 int width_of_lock_button = 1060 2 * views::kUnrelatedControlLargeHorizontalSpacing + 12; 1061 int width_of_users_button = kFixedMenuWidth - width_of_lock_button; 1062 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, 1063 views::GridLayout::FIXED, width_of_users_button, 1064 width_of_users_button); 1065 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, 1066 views::GridLayout::FIXED, width_of_lock_button, 1067 width_of_lock_button); 1068 view->SetLayoutManager(layout); 1069 } else { 1070 layout = CreateSingleColumnLayout(view, kFixedMenuWidth); 1071 } 1072 1073 base::string16 text = browser_->profile()->IsGuestSession() ? 1074 l10n_util::GetStringUTF16(IDS_PROFILES_EXIT_GUEST) : 1075 l10n_util::GetStringFUTF16(IDS_PROFILES_NOT_YOU_BUTTON, 1076 profiles::GetAvatarNameForProfile(browser_->profile())); 1077 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 1078 users_button_ = new BackgroundColorHoverButton( 1079 this, 1080 text, 1081 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_AVATAR), 1082 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_AVATAR)); 1083 users_button_->set_min_size(gfx::Size( 1084 0, kButtonHeight + views::kRelatedControlVerticalSpacing)); 1085 1086 layout->StartRow(1, 0); 1087 layout->AddView(users_button_); 1088 1089 if (enable_lock) { 1090 lock_button_ = new BackgroundColorHoverButton( 1091 this, 1092 base::string16(), 1093 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_LOCK), 1094 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_LOCK)); 1095 lock_button_->set_min_size(gfx::Size( 1096 0, kButtonHeight + views::kRelatedControlVerticalSpacing)); 1097 layout->AddView(lock_button_); 1098 } 1099 return view; 1100} 1101 1102views::View* ProfileChooserView::CreateCurrentProfileAccountsView( 1103 const AvatarMenu::Item& avatar_item) { 1104 DCHECK(avatar_item.signed_in); 1105 views::View* view = new views::View(); 1106 view->set_background(views::Background::CreateSolidBackground( 1107 profiles::kAvatarBubbleAccountsBackgroundColor)); 1108 views::GridLayout* layout = CreateSingleColumnLayout(view, kFixedMenuWidth); 1109 1110 Profile* profile = browser_->profile(); 1111 std::string primary_account = 1112 SigninManagerFactory::GetForProfile(profile)->GetAuthenticatedUsername(); 1113 DCHECK(!primary_account.empty()); 1114 std::vector<std::string>accounts = 1115 profiles::GetSecondaryAccountsForProfile(profile, primary_account); 1116 1117 // The primary account should always be listed first. 1118 // TODO(rogerta): we still need to further differentiate the primary account 1119 // from the others in the UI, so more work is likely required here: 1120 // crbug.com/311124. 1121 CreateAccountButton(layout, primary_account, true, kFixedMenuWidth); 1122 for (size_t i = 0; i < accounts.size(); ++i) 1123 CreateAccountButton(layout, accounts[i], false, kFixedMenuWidth); 1124 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 1125 1126 add_account_link_ = CreateLink(l10n_util::GetStringFUTF16( 1127 IDS_PROFILES_PROFILE_ADD_ACCOUNT_BUTTON, avatar_item.name), this); 1128 add_account_link_->SetBorder(views::Border::CreateEmptyBorder( 1129 0, views::kButtonVEdgeMarginNew, 1130 views::kRelatedControlVerticalSpacing, 0)); 1131 layout->StartRow(1, 0); 1132 layout->AddView(add_account_link_); 1133 return view; 1134} 1135 1136void ProfileChooserView::CreateAccountButton(views::GridLayout* layout, 1137 const std::string& account, 1138 bool is_primary_account, 1139 int width) { 1140 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 1141 const gfx::ImageSkia* default_image = 1142 rb->GetImageNamed(IDR_CLOSE_1).ToImageSkia(); 1143 int kDeleteButtonWidth = default_image->width(); 1144 int available_width = width - 1145 kDeleteButtonWidth - views::kButtonHEdgeMarginNew; 1146 1147 views::LabelButton* email_button = new BackgroundColorHoverButton( 1148 NULL, 1149 gfx::ElideEmail(base::UTF8ToUTF16(account), 1150 rb->GetFontList(ui::ResourceBundle::BaseFont), 1151 available_width), 1152 gfx::ImageSkia(), 1153 gfx::ImageSkia()); 1154 layout->StartRow(1, 0); 1155 layout->AddView(email_button); 1156 1157 // Delete button. 1158 views::ImageButton* delete_button = new views::ImageButton(this); 1159 delete_button->SetImageAlignment(views::ImageButton::ALIGN_RIGHT, 1160 views::ImageButton::ALIGN_MIDDLE); 1161 delete_button->SetImage(views::ImageButton::STATE_NORMAL, 1162 default_image); 1163 delete_button->SetImage(views::ImageButton::STATE_HOVERED, 1164 rb->GetImageSkiaNamed(IDR_CLOSE_1_H)); 1165 delete_button->SetImage(views::ImageButton::STATE_PRESSED, 1166 rb->GetImageSkiaNamed(IDR_CLOSE_1_P)); 1167 delete_button->SetBounds( 1168 available_width, 0, kDeleteButtonWidth, kButtonHeight); 1169 1170 email_button->set_notify_enter_exit_on_child(true); 1171 email_button->AddChildView(delete_button); 1172 1173 // Save the original email address, as the button text could be elided. 1174 current_profile_accounts_map_[delete_button] = account; 1175} 1176 1177views::View* ProfileChooserView::CreateGaiaSigninView( 1178 bool add_secondary_account) { 1179 // Adds Gaia signin webview 1180 Profile* profile = browser_->profile(); 1181 views::WebView* web_view = new views::WebView(profile); 1182 signin::Source source = add_secondary_account ? 1183 signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT : 1184 signin::SOURCE_AVATAR_BUBBLE_SIGN_IN; 1185 GURL url(signin::GetPromoURL( 1186 source, false /* auto_close */, true /* is_constrained */)); 1187 web_view->LoadInitialURL(url); 1188 web_view->SetPreferredSize( 1189 gfx::Size(kFixedGaiaViewWidth, kFixedGaiaViewHeight)); 1190 1191 TitleCard* title_card = new TitleCard( 1192 add_secondary_account ? IDS_PROFILES_GAIA_ADD_ACCOUNT_TITLE : 1193 IDS_PROFILES_GAIA_SIGNIN_TITLE, 1194 this, &gaia_signin_cancel_button_); 1195 return TitleCard::AddPaddedTitleCard( 1196 web_view, title_card, kFixedGaiaViewWidth); 1197} 1198 1199views::View* ProfileChooserView::CreateAccountRemovalView() { 1200 views::View* view = new views::View(); 1201 views::GridLayout* layout = CreateSingleColumnLayout( 1202 view, kFixedAccountRemovalViewWidth - 2 * views::kButtonHEdgeMarginNew); 1203 layout->SetInsets(0, 1204 views::kButtonHEdgeMarginNew, 1205 views::kButtonVEdgeMarginNew, 1206 views::kButtonHEdgeMarginNew); 1207 1208 const std::string& primary_account = SigninManagerFactory::GetForProfile( 1209 browser_->profile())->GetAuthenticatedUsername(); 1210 bool is_primary_account = primary_account == account_id_to_remove_; 1211 1212 // Adds main text. 1213 layout->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing); 1214 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 1215 const gfx::FontList& small_font_list = 1216 rb->GetFontList(ui::ResourceBundle::SmallFont); 1217 1218 if (is_primary_account) { 1219 std::vector<size_t> offsets; 1220 const base::string16 settings_text = 1221 l10n_util::GetStringUTF16(IDS_PROFILES_SETTINGS_LINK); 1222 const base::string16 primary_account_removal_text = 1223 l10n_util::GetStringFUTF16(IDS_PROFILES_PRIMARY_ACCOUNT_REMOVAL_TEXT, 1224 base::UTF8ToUTF16(account_id_to_remove_), settings_text, &offsets); 1225 views::StyledLabel* primary_account_removal_label = 1226 new views::StyledLabel(primary_account_removal_text, this); 1227 primary_account_removal_label->AddStyleRange( 1228 gfx::Range(offsets[1], offsets[1] + settings_text.size()), 1229 views::StyledLabel::RangeStyleInfo::CreateForLink()); 1230 primary_account_removal_label->SetBaseFontList(small_font_list); 1231 layout->AddView(primary_account_removal_label); 1232 } else { 1233 views::Label* content_label = new views::Label( 1234 l10n_util::GetStringUTF16(IDS_PROFILES_ACCOUNT_REMOVAL_TEXT)); 1235 content_label->SetMultiLine(true); 1236 content_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 1237 content_label->SetFontList(small_font_list); 1238 layout->AddView(content_label); 1239 } 1240 1241 // Adds button. 1242 if (!is_primary_account) { 1243 remove_account_button_ = new views::BlueButton( 1244 this, l10n_util::GetStringUTF16(IDS_PROFILES_ACCOUNT_REMOVAL_BUTTON)); 1245 remove_account_button_->SetHorizontalAlignment( 1246 gfx::ALIGN_CENTER); 1247 layout->StartRowWithPadding( 1248 1, 0, 0, views::kUnrelatedControlVerticalSpacing); 1249 layout->AddView(remove_account_button_); 1250 } else { 1251 layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing); 1252 } 1253 1254 TitleCard* title_card = new TitleCard(IDS_PROFILES_ACCOUNT_REMOVAL_TITLE, 1255 this, &account_removal_cancel_button_); 1256 return TitleCard::AddPaddedTitleCard(view, title_card, 1257 kFixedAccountRemovalViewWidth); 1258} 1259 1260views::View* ProfileChooserView::CreateNewProfileManagementPreviewView() { 1261 return CreateTutorialView( 1262 TUTORIAL_MODE_ENABLE_PREVIEW, 1263 l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_TUTORIAL_TITLE), 1264 l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_TUTORIAL_CONTENT_TEXT), 1265 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_TUTORIAL_LEARN_MORE), 1266 l10n_util::GetStringUTF16(IDS_PROFILES_TUTORIAL_TRY_BUTTON), 1267 &tutorial_learn_more_link_, 1268 &tutorial_enable_new_profile_management_button_); 1269} 1270 1271views::View* ProfileChooserView::CreateEndPreviewView() { 1272 views::View* view = new views::View(); 1273 views::GridLayout* layout = CreateSingleColumnLayout( 1274 view, kFixedAccountRemovalViewWidth - 2 * views::kButtonHEdgeMarginNew); 1275 layout->SetInsets(0, 1276 views::kButtonHEdgeMarginNew, 1277 views::kButtonVEdgeMarginNew, 1278 views::kButtonHEdgeMarginNew); 1279 1280 // Adds main text. 1281 views::Label* content_label = new views::Label( 1282 l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW_TEXT)); 1283 content_label->SetMultiLine(true); 1284 content_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 1285 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 1286 const gfx::FontList& small_font_list = 1287 rb->GetFontList(ui::ResourceBundle::SmallFont); 1288 content_label->SetFontList(small_font_list); 1289 layout->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing); 1290 layout->AddView(content_label); 1291 1292 // Adds button. 1293 end_preview_and_relaunch_button_ = new views::BlueButton( 1294 this, l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW_AND_RELAUNCH)); 1295 end_preview_and_relaunch_button_->SetHorizontalAlignment( 1296 gfx::ALIGN_CENTER); 1297 layout->StartRowWithPadding( 1298 1, 0, 0, views::kUnrelatedControlVerticalSpacing); 1299 layout->AddView(end_preview_and_relaunch_button_); 1300 1301 TitleCard* title_card = new TitleCard( 1302 IDS_PROFILES_END_PREVIEW, this, &end_preview_cancel_button_); 1303 return TitleCard::AddPaddedTitleCard( 1304 view, title_card, kFixedAccountRemovalViewWidth); 1305} 1306 1307