[Win,Linux] New avatar menu: Fix invisible "question mark" button.
[chromium-blink-merge.git] / chrome / browser / ui / views / profiles / profile_chooser_view.cc
blob67ac56a1d892baf206cfc550a2084d09e18c9eac
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.
5 #include "chrome/browser/ui/views/profiles/profile_chooser_view.h"
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_header_helper.h"
19 #include "chrome/browser/signin/signin_manager_factory.h"
20 #include "chrome/browser/signin/signin_promo.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_commands.h"
23 #include "chrome/browser/ui/browser_dialogs.h"
24 #include "chrome/browser/ui/chrome_pages.h"
25 #include "chrome/browser/ui/singleton_tabs.h"
26 #include "chrome/browser/ui/views/profiles/user_manager_view.h"
27 #include "chrome/common/pref_names.h"
28 #include "chrome/common/url_constants.h"
29 #include "components/signin/core/browser/mutable_profile_oauth2_token_service.h"
30 #include "components/signin/core/browser/profile_oauth2_token_service.h"
31 #include "components/signin/core/browser/signin_error_controller.h"
32 #include "components/signin/core/browser/signin_manager.h"
33 #include "components/signin/core/common/profile_management_switches.h"
34 #include "grit/chromium_strings.h"
35 #include "grit/generated_resources.h"
36 #include "grit/theme_resources.h"
37 #include "third_party/skia/include/core/SkColor.h"
38 #include "ui/base/l10n/l10n_util.h"
39 #include "ui/base/resource/resource_bundle.h"
40 #include "ui/gfx/canvas.h"
41 #include "ui/gfx/image/image.h"
42 #include "ui/gfx/image/image_skia.h"
43 #include "ui/gfx/path.h"
44 #include "ui/gfx/skia_util.h"
45 #include "ui/gfx/text_elider.h"
46 #include "ui/native_theme/native_theme.h"
47 #include "ui/views/controls/button/blue_button.h"
48 #include "ui/views/controls/button/image_button.h"
49 #include "ui/views/controls/button/label_button.h"
50 #include "ui/views/controls/button/menu_button.h"
51 #include "ui/views/controls/label.h"
52 #include "ui/views/controls/link.h"
53 #include "ui/views/controls/separator.h"
54 #include "ui/views/controls/styled_label.h"
55 #include "ui/views/controls/textfield/textfield.h"
56 #include "ui/views/controls/webview/webview.h"
57 #include "ui/views/layout/grid_layout.h"
58 #include "ui/views/layout/layout_constants.h"
59 #include "ui/views/widget/widget.h"
61 namespace {
63 // Helpers --------------------------------------------------------------------
65 const int kFixedMenuWidth = 250;
66 const int kButtonHeight = 29;
67 const int kProfileAvatarTutorialShowMax = 1;
68 const int kFixedGaiaViewHeight = 400;
69 const int kFixedGaiaViewWidth = 360;
70 const int kFixedAccountRemovalViewWidth = 280;
71 const int kFixedEndPreviewViewWidth = 280;
72 const int kLargeImageSide = 88;
74 // Creates a GridLayout with a single column. This ensures that all the child
75 // views added get auto-expanded to fill the full width of the bubble.
76 views::GridLayout* CreateSingleColumnLayout(views::View* view, int width) {
77 views::GridLayout* layout = new views::GridLayout(view);
78 view->SetLayoutManager(layout);
80 views::ColumnSet* columns = layout->AddColumnSet(0);
81 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
82 views::GridLayout::FIXED, width, width);
83 return layout;
86 views::Link* CreateLink(const base::string16& link_text,
87 views::LinkListener* listener) {
88 views::Link* link_button = new views::Link(link_text);
89 link_button->SetHorizontalAlignment(gfx::ALIGN_LEFT);
90 link_button->SetUnderline(false);
91 link_button->set_listener(listener);
92 return link_button;
95 gfx::ImageSkia CreateSquarePlaceholderImage(int size) {
96 SkBitmap bitmap;
97 bitmap.setConfig(SkBitmap::kA8_Config, size, size);
98 bitmap.allocPixels();
99 bitmap.eraseARGB(0, 0, 0, 0);
100 return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
103 bool HasAuthError(Profile* profile) {
104 const SigninErrorController* error =
105 profiles::GetSigninErrorController(profile);
106 return error && error->HasError();
109 std::string GetAuthErrorAccountId(Profile* profile) {
110 const SigninErrorController* error =
111 profiles::GetSigninErrorController(profile);
112 if (!error)
113 return std::string();
115 return error->error_account_id();
118 std::string GetAuthErrorUsername(Profile* profile) {
119 const SigninErrorController* error =
120 profiles::GetSigninErrorController(profile);
121 if (!error)
122 return std::string();
124 return error->error_username();
127 // BackgroundColorHoverButton -------------------------------------------------
129 // A custom button that allows for setting a background color when hovered over.
130 class BackgroundColorHoverButton : public views::LabelButton {
131 public:
132 BackgroundColorHoverButton(views::ButtonListener* listener,
133 const base::string16& text,
134 const gfx::ImageSkia& normal_icon,
135 const gfx::ImageSkia& hover_icon);
136 virtual ~BackgroundColorHoverButton();
138 private:
139 // views::LabelButton:
140 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
142 DISALLOW_COPY_AND_ASSIGN(BackgroundColorHoverButton);
145 BackgroundColorHoverButton::BackgroundColorHoverButton(
146 views::ButtonListener* listener,
147 const base::string16& text,
148 const gfx::ImageSkia& normal_icon,
149 const gfx::ImageSkia& hover_icon)
150 : views::LabelButton(listener, text) {
151 SetBorder(views::Border::CreateEmptyBorder(0, views::kButtonHEdgeMarginNew,
152 0, views::kButtonHEdgeMarginNew));
153 set_min_size(gfx::Size(0, kButtonHeight));
154 SetImage(STATE_NORMAL, normal_icon);
155 SetImage(STATE_HOVERED, hover_icon);
156 SetImage(STATE_PRESSED, hover_icon);
159 BackgroundColorHoverButton::~BackgroundColorHoverButton() {}
161 void BackgroundColorHoverButton::OnPaint(gfx::Canvas* canvas) {
162 if ((state() == STATE_PRESSED) || (state() == STATE_HOVERED) || HasFocus()) {
163 canvas->DrawColor(GetNativeTheme()->GetSystemColor(
164 ui::NativeTheme::kColorId_ButtonHoverBackgroundColor));
166 LabelButton::OnPaint(canvas);
169 // SizedContainer -------------------------------------------------
171 // A simple container view that takes an explicit preferred size.
172 class SizedContainer : public views::View {
173 public:
174 explicit SizedContainer(const gfx::Size& preferred_size)
175 : preferred_size_(preferred_size) {}
177 virtual gfx::Size GetPreferredSize() const OVERRIDE {
178 return preferred_size_;
181 private:
182 gfx::Size preferred_size_;
185 } // namespace
188 // EditableProfilePhoto -------------------------------------------------
190 // A custom Image control that shows a "change" button when moused over.
191 class EditableProfilePhoto : public views::ImageView {
192 public:
193 EditableProfilePhoto(views::ButtonListener* listener,
194 const gfx::Image& icon,
195 bool is_editing_allowed,
196 const gfx::Rect& bounds)
197 : views::ImageView(),
198 change_photo_button_(NULL) {
199 gfx::Image image = profiles::GetSizedAvatarIcon(
200 icon, true, kLargeImageSide, kLargeImageSide);
201 SetImage(image.ToImageSkia());
202 SetBoundsRect(bounds);
204 // Calculate the circular mask that will be used to display the photo.
205 circular_mask_.addCircle(SkIntToScalar(bounds.width() / 2),
206 SkIntToScalar(bounds.height() / 2),
207 SkIntToScalar(bounds.width() / 2));
209 if (!is_editing_allowed)
210 return;
212 set_notify_enter_exit_on_child(true);
214 // Button overlay that appears when hovering over the image.
215 change_photo_button_ = new views::LabelButton(listener, base::string16());
216 change_photo_button_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
217 change_photo_button_->SetBorder(views::Border::NullBorder());
219 const SkColor kBackgroundColor = SkColorSetARGB(65, 255, 255, 255);
220 change_photo_button_->set_background(
221 views::Background::CreateSolidBackground(kBackgroundColor));
222 change_photo_button_->SetImage(views::LabelButton::STATE_NORMAL,
223 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
224 IDR_ICON_PROFILES_EDIT_CAMERA));
226 change_photo_button_->SetSize(bounds.size());
227 change_photo_button_->SetVisible(false);
228 AddChildView(change_photo_button_);
231 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
232 // Display the profile picture as a circle.
233 canvas->ClipPath(circular_mask_, true);
234 views::ImageView::OnPaint(canvas);
237 virtual void PaintChildren(gfx::Canvas* canvas,
238 const views::CullSet& cull_set) OVERRIDE {
239 // Display any children (the "change photo" overlay) as a circle.
240 canvas->ClipPath(circular_mask_, true);
241 View::PaintChildren(canvas, cull_set);
244 views::LabelButton* change_photo_button() { return change_photo_button_; }
246 private:
247 // views::View:
248 virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE {
249 if (change_photo_button_)
250 change_photo_button_->SetVisible(true);
253 virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE {
254 if (change_photo_button_)
255 change_photo_button_->SetVisible(false);
258 gfx::Path circular_mask_;
260 // Button that is shown when hovering over the image view. Can be NULL if
261 // the photo isn't allowed to be edited (e.g. for guest profiles).
262 views::LabelButton* change_photo_button_;
264 DISALLOW_COPY_AND_ASSIGN(EditableProfilePhoto);
268 // EditableProfileName -------------------------------------------------
270 // A custom text control that turns into a textfield for editing when clicked.
271 class EditableProfileName : public views::LabelButton,
272 public views::ButtonListener {
273 public:
274 EditableProfileName(views::TextfieldController* controller,
275 const base::string16& text,
276 bool is_editing_allowed)
277 : views::LabelButton(this, text),
278 profile_name_textfield_(NULL) {
279 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
280 const gfx::FontList& medium_font_list =
281 rb->GetFontList(ui::ResourceBundle::MediumFont);
282 SetFontList(medium_font_list);
283 SetHorizontalAlignment(gfx::ALIGN_CENTER);
285 if (!is_editing_allowed) {
286 SetBorder(views::Border::CreateEmptyBorder(2, 0, 2, 0));
287 return;
290 // Show an "edit" pencil icon when hovering over. In the default state,
291 // we need to create an empty placeholder of the correct size, so that
292 // the text doesn't jump around when the hovered icon appears.
293 gfx::ImageSkia hover_image =
294 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_HOVER);
295 SetImage(STATE_NORMAL, CreateSquarePlaceholderImage(hover_image.width()));
296 SetImage(STATE_HOVERED, hover_image);
297 SetImage(STATE_PRESSED,
298 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_EDIT_PRESSED));
299 // To center the text, we need to offest it by the width of the icon we
300 // are adding. We need to also add a small top/bottom padding to account
301 // for the textfield's border.
302 SetBorder(views::Border::CreateEmptyBorder(2, hover_image.width(), 2, 0));
304 // Textfield that overlaps the button.
305 profile_name_textfield_ = new views::Textfield();
306 profile_name_textfield_->set_controller(controller);
307 profile_name_textfield_->SetFontList(medium_font_list);
308 profile_name_textfield_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
310 profile_name_textfield_->SetVisible(false);
311 AddChildView(profile_name_textfield_);
314 views::Textfield* profile_name_textfield() {
315 return profile_name_textfield_;
318 // Hide the editable textfield to show the profile name button instead.
319 void ShowReadOnlyView() {
320 if (profile_name_textfield_)
321 profile_name_textfield_->SetVisible(false);
324 private:
325 // views::ButtonListener:
326 virtual void ButtonPressed(views::Button* sender,
327 const ui::Event& event) OVERRIDE {
328 if (profile_name_textfield_) {
329 profile_name_textfield_->SetVisible(true);
330 profile_name_textfield_->SetText(GetText());
331 profile_name_textfield_->SelectAll(false);
332 profile_name_textfield_->RequestFocus();
336 // views::LabelButton:
337 virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE {
338 // Override CustomButton's implementation, which presses the button when
339 // you press space and clicks it when you release space, as the space can be
340 // part of the new profile name typed in the textfield.
341 return false;
344 virtual void Layout() OVERRIDE {
345 if (profile_name_textfield_)
346 profile_name_textfield_->SetBounds(0, 0, width(), height());
347 // This layout trick keeps the text left-aligned and the icon right-aligned.
348 SetHorizontalAlignment(gfx::ALIGN_RIGHT);
349 views::LabelButton::Layout();
350 label()->SetHorizontalAlignment(gfx::ALIGN_CENTER);
353 // Textfield that is shown when editing the profile name. Can be NULL if
354 // the profile name isn't allowed to be edited (e.g. for guest profiles).
355 views::Textfield* profile_name_textfield_;
357 DISALLOW_COPY_AND_ASSIGN(EditableProfileName);
360 // A title card with one back button right aligned and one label center aligned.
361 class TitleCard : public views::View {
362 public:
363 TitleCard(int message_id, views::ButtonListener* listener,
364 views::ImageButton** back_button) {
365 back_button_ = new views::ImageButton(listener);
366 back_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
367 views::ImageButton::ALIGN_MIDDLE);
368 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
369 back_button_->SetImage(views::ImageButton::STATE_NORMAL,
370 rb->GetImageSkiaNamed(IDR_BACK));
371 back_button_->SetImage(views::ImageButton::STATE_HOVERED,
372 rb->GetImageSkiaNamed(IDR_BACK_H));
373 back_button_->SetImage(views::ImageButton::STATE_PRESSED,
374 rb->GetImageSkiaNamed(IDR_BACK_P));
375 back_button_->SetImage(views::ImageButton::STATE_DISABLED,
376 rb->GetImageSkiaNamed(IDR_BACK_D));
377 *back_button = back_button_;
379 title_label_ = new views::Label(l10n_util::GetStringUTF16(message_id));
380 title_label_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
381 const gfx::FontList& medium_font_list =
382 rb->GetFontList(ui::ResourceBundle::MediumFont);
383 title_label_->SetFontList(medium_font_list);
385 AddChildView(back_button_);
386 AddChildView(title_label_);
389 // Creates a new view that has the |title_card| with padding at the top, an
390 // edge-to-edge separator below, and the specified |view| at the bottom.
391 static views::View* AddPaddedTitleCard(views::View* view,
392 TitleCard* title_card,
393 int width) {
394 views::View* titled_view = new views::View();
395 views::GridLayout* layout = new views::GridLayout(titled_view);
396 titled_view->SetLayoutManager(layout);
398 // Column set 0 is a single column layout with horizontal padding at left
399 // and right, and column set 1 is a single column layout with no padding.
400 views::ColumnSet* columns = layout->AddColumnSet(0);
401 columns->AddPaddingColumn(1, views::kButtonHEdgeMarginNew);
402 int available_width = width - 2 * views::kButtonHEdgeMarginNew;
403 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
404 views::GridLayout::FIXED, available_width, available_width);
405 columns->AddPaddingColumn(1, views::kButtonHEdgeMarginNew);
406 layout->AddColumnSet(1)->AddColumn(views::GridLayout::FILL,
407 views::GridLayout::FILL, 0,views::GridLayout::FIXED, width, width);
409 layout->StartRowWithPadding(1, 0, 0, views::kButtonVEdgeMarginNew);
410 layout->AddView(title_card);
411 layout->StartRowWithPadding(1, 1, 0, views::kRelatedControlVerticalSpacing);
412 layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
414 layout->StartRow(1, 1);
415 layout->AddView(view);
417 return titled_view;
420 private:
421 virtual void Layout() OVERRIDE{
422 back_button_->SetBounds(
423 0, 0, back_button_->GetPreferredSize().width(), height());
424 title_label_->SetBoundsRect(GetContentsBounds());
427 virtual gfx::Size GetPreferredSize() const OVERRIDE{
428 int height = std::max(title_label_->GetPreferredSize().height(),
429 back_button_->GetPreferredSize().height());
430 return gfx::Size(width(), height);
433 views::ImageButton* back_button_;
434 views::Label* title_label_;
436 DISALLOW_COPY_AND_ASSIGN(TitleCard);
439 // ProfileChooserView ---------------------------------------------------------
441 // static
442 ProfileChooserView* ProfileChooserView::profile_bubble_ = NULL;
443 bool ProfileChooserView::close_on_deactivate_for_testing_ = true;
445 // static
446 void ProfileChooserView::ShowBubble(
447 profiles::BubbleViewMode view_mode,
448 signin::GAIAServiceType service_type,
449 views::View* anchor_view,
450 views::BubbleBorder::Arrow arrow,
451 views::BubbleBorder::BubbleAlignment border_alignment,
452 const gfx::Rect& anchor_rect,
453 Browser* browser) {
454 if (IsShowing())
455 return;
457 profile_bubble_ = new ProfileChooserView(anchor_view, arrow, anchor_rect,
458 browser, view_mode, service_type);
459 views::BubbleDelegateView::CreateBubble(profile_bubble_);
460 profile_bubble_->set_close_on_deactivate(close_on_deactivate_for_testing_);
461 profile_bubble_->SetAlignment(border_alignment);
462 profile_bubble_->GetWidget()->Show();
463 profile_bubble_->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
466 // static
467 bool ProfileChooserView::IsShowing() {
468 return profile_bubble_ != NULL;
471 // static
472 void ProfileChooserView::Hide() {
473 if (IsShowing())
474 profile_bubble_->GetWidget()->Close();
477 ProfileChooserView::ProfileChooserView(views::View* anchor_view,
478 views::BubbleBorder::Arrow arrow,
479 const gfx::Rect& anchor_rect,
480 Browser* browser,
481 profiles::BubbleViewMode view_mode,
482 signin::GAIAServiceType service_type)
483 : BubbleDelegateView(anchor_view, arrow),
484 browser_(browser),
485 view_mode_(view_mode),
486 tutorial_mode_(profiles::TUTORIAL_MODE_NONE),
487 gaia_service_type_(service_type) {
488 // Reset the default margins inherited from the BubbleDelegateView.
489 set_margins(gfx::Insets());
491 ResetView();
493 avatar_menu_.reset(new AvatarMenu(
494 &g_browser_process->profile_manager()->GetProfileInfoCache(),
495 this,
496 browser_));
497 avatar_menu_->RebuildMenu();
499 ProfileOAuth2TokenService* oauth2_token_service =
500 ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile());
501 if (oauth2_token_service)
502 oauth2_token_service->AddObserver(this);
505 ProfileChooserView::~ProfileChooserView() {
506 ProfileOAuth2TokenService* oauth2_token_service =
507 ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile());
508 if (oauth2_token_service)
509 oauth2_token_service->RemoveObserver(this);
512 void ProfileChooserView::ResetView() {
513 question_mark_button_ = NULL;
514 manage_accounts_link_ = NULL;
515 signin_current_profile_link_ = NULL;
516 users_button_ = NULL;
517 lock_button_ = NULL;
518 add_account_link_ = NULL;
519 current_profile_photo_ = NULL;
520 current_profile_name_ = NULL;
521 tutorial_ok_button_ = NULL;
522 tutorial_learn_more_link_ = NULL;
523 tutorial_enable_new_profile_management_button_ = NULL;
524 tutorial_end_preview_link_ = NULL;
525 tutorial_send_feedback_button_ = NULL;
526 end_preview_and_relaunch_button_ = NULL;
527 end_preview_cancel_button_ = NULL;
528 remove_account_button_ = NULL;
529 account_removal_cancel_button_ = NULL;
530 gaia_signin_cancel_button_ = NULL;
531 open_other_profile_indexes_map_.clear();
532 delete_account_button_map_.clear();
533 reauth_account_button_map_.clear();
534 tutorial_mode_ = profiles::TUTORIAL_MODE_NONE;
537 void ProfileChooserView::Init() {
538 // If view mode is PROFILE_CHOOSER but there is an auth error, force
539 // ACCOUNT_MANAGEMENT mode.
540 if (view_mode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER &&
541 HasAuthError(browser_->profile())) {
542 view_mode_ = profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT;
545 ShowView(view_mode_, avatar_menu_.get());
548 void ProfileChooserView::OnAvatarMenuChanged(
549 AvatarMenu* avatar_menu) {
550 // Refresh the view with the new menu. We can't just update the local copy
551 // as this may have been triggered by a sign out action, in which case
552 // the view is being destroyed.
553 ShowView(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER, avatar_menu);
556 void ProfileChooserView::OnRefreshTokenAvailable(
557 const std::string& account_id) {
558 // Refresh the account management view when a new account is added to the
559 // profile.
560 if (view_mode_ == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT ||
561 view_mode_ == profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN ||
562 view_mode_ == profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT ||
563 view_mode_ == profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH) {
564 ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, avatar_menu_.get());
568 void ProfileChooserView::OnRefreshTokenRevoked(const std::string& account_id) {
569 // Refresh the account management view when an account is removed from the
570 // profile.
571 if (view_mode_ == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT)
572 ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, avatar_menu_.get());
575 void ProfileChooserView::ShowView(profiles::BubbleViewMode view_to_display,
576 AvatarMenu* avatar_menu) {
577 // The account management view should only be displayed if the active profile
578 // is signed in.
579 if (view_to_display == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT) {
580 const AvatarMenu::Item& active_item = avatar_menu->GetItemAt(
581 avatar_menu->GetActiveProfileIndex());
582 DCHECK(active_item.signed_in);
585 // Records the last tutorial mode.
586 profiles::TutorialMode last_tutorial_mode = tutorial_mode_;
587 ResetView();
588 RemoveAllChildViews(true);
589 view_mode_ = view_to_display;
591 views::GridLayout* layout;
592 views::View* sub_view;
593 switch (view_mode_) {
594 case profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN:
595 case profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT:
596 case profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH:
597 layout = CreateSingleColumnLayout(this, kFixedGaiaViewWidth);
598 sub_view = CreateGaiaSigninView();
599 break;
600 case profiles::BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL:
601 layout = CreateSingleColumnLayout(this, kFixedAccountRemovalViewWidth);
602 sub_view = CreateAccountRemovalView();
603 break;
604 case profiles::BUBBLE_VIEW_MODE_END_PREVIEW:
605 layout = CreateSingleColumnLayout(this, kFixedEndPreviewViewWidth);
606 sub_view = CreateEndPreviewView();
607 break;
608 default:
609 layout = CreateSingleColumnLayout(this, kFixedMenuWidth);
610 sub_view = CreateProfileChooserView(avatar_menu, last_tutorial_mode);
612 sub_view->set_background(views::Background::CreateSolidBackground(
613 GetNativeTheme()->GetSystemColor(
614 ui::NativeTheme::kColorId_DialogBackground)));
616 layout->StartRow(1, 0);
617 layout->AddView(sub_view);
618 Layout();
619 if (GetBubbleFrameView())
620 SizeToContents();
623 void ProfileChooserView::WindowClosing() {
624 DCHECK_EQ(profile_bubble_, this);
625 profile_bubble_ = NULL;
628 void ProfileChooserView::ButtonPressed(views::Button* sender,
629 const ui::Event& event) {
630 // Disable button after clicking so that it doesn't get clicked twice and
631 // start a second action... which can crash Chrome. But don't disable if it
632 // has no parent (like in tests) because that will also crash.
633 if (sender->parent())
634 sender->SetEnabled(false);
636 if (sender == users_button_) {
637 profiles::ShowUserManagerMaybeWithTutorial(browser_->profile());
638 // If this is a guest session, also close all the guest browser windows.
639 if (browser_->profile()->IsGuestSession())
640 profiles::CloseGuestProfileWindows();
641 } else if (sender == lock_button_) {
642 profiles::LockProfile(browser_->profile());
643 PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_LOCK);
644 } else if (sender == tutorial_ok_button_) {
645 // If the user manually dismissed the tutorial, never show it again by
646 // setting the number of times shown to the maximum plus 1, so that later we
647 // could distinguish between the dismiss case and the case when the tutorial
648 // is indeed shown for the maximum number of times.
649 browser_->profile()->GetPrefs()->SetInteger(
650 prefs::kProfileAvatarTutorialShown, kProfileAvatarTutorialShowMax + 1);
652 ProfileMetrics::LogProfileUpgradeEnrollment(
653 ProfileMetrics::PROFILE_ENROLLMENT_CLOSE_WELCOME_CARD);
654 ShowView(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER, avatar_menu_.get());
655 } else if (sender == tutorial_enable_new_profile_management_button_) {
656 ProfileMetrics::LogProfileUpgradeEnrollment(
657 ProfileMetrics::PROFILE_ENROLLMENT_ACCEPT_NEW_PROFILE_MGMT);
658 profiles::EnableNewProfileManagementPreview();
659 } else if (sender == remove_account_button_) {
660 RemoveAccount();
661 } else if (sender == account_removal_cancel_button_) {
662 account_id_to_remove_.clear();
663 ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, avatar_menu_.get());
664 } else if (sender == gaia_signin_cancel_button_) {
665 std::string primary_account =
666 SigninManagerFactory::GetForProfile(browser_->profile())->
667 GetAuthenticatedUsername();
668 ShowView(primary_account.empty() ?
669 profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER :
670 profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT,
671 avatar_menu_.get());
672 } else if (sender == question_mark_button_) {
673 tutorial_mode_ = profiles::TUTORIAL_MODE_SEND_FEEDBACK;
674 ShowView(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER, avatar_menu_.get());
675 } else if (sender == tutorial_send_feedback_button_) {
676 ProfileMetrics::LogProfileUpgradeEnrollment(
677 ProfileMetrics::PROFILE_ENROLLMENT_SEND_FEEDBACK);
678 chrome::OpenFeedbackDialog(browser_);
679 } else if (sender == end_preview_and_relaunch_button_) {
680 ProfileMetrics::LogProfileUpgradeEnrollment(
681 ProfileMetrics::PROFILE_ENROLLMENT_DISABLE_NEW_PROFILE_MGMT);
682 profiles::DisableNewProfileManagementPreview();
683 } else if (sender == end_preview_cancel_button_) {
684 tutorial_mode_ = profiles::TUTORIAL_MODE_SEND_FEEDBACK;
685 ShowView(profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER, avatar_menu_.get());
686 } else if (current_profile_photo_ &&
687 sender == current_profile_photo_->change_photo_button()) {
688 avatar_menu_->EditProfile(avatar_menu_->GetActiveProfileIndex());
689 PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_EDIT_IMAGE);
690 } else if (sender == signin_current_profile_link_) {
691 // Only show the inline signin if the new UI flag is flipped. Otherwise,
692 // use the tab signin page.
693 if (switches::IsNewProfileManagement())
694 ShowView(profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN, avatar_menu_.get());
695 else
696 chrome::ShowBrowserSignin(browser_, signin::SOURCE_MENU);
697 } else {
698 // Either one of the "other profiles", or one of the profile accounts
699 // buttons was pressed.
700 ButtonIndexes::const_iterator profile_match =
701 open_other_profile_indexes_map_.find(sender);
702 if (profile_match != open_other_profile_indexes_map_.end()) {
703 avatar_menu_->SwitchToProfile(
704 profile_match->second,
705 ui::DispositionFromEventFlags(event.flags()) == NEW_WINDOW,
706 ProfileMetrics::SWITCH_PROFILE_ICON);
707 } else {
708 // This was a profile accounts button.
709 AccountButtonIndexes::const_iterator account_match =
710 delete_account_button_map_.find(sender);
711 if (account_match != delete_account_button_map_.end()) {
712 account_id_to_remove_ = account_match->second;
713 ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL,
714 avatar_menu_.get());
715 } else {
716 account_match = reauth_account_button_map_.find(sender);
717 DCHECK(account_match != reauth_account_button_map_.end());
718 ShowView(profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH, avatar_menu_.get());
724 void ProfileChooserView::RemoveAccount() {
725 DCHECK(!account_id_to_remove_.empty());
726 MutableProfileOAuth2TokenService* oauth2_token_service =
727 ProfileOAuth2TokenServiceFactory::GetPlatformSpecificForProfile(
728 browser_->profile());
729 if (oauth2_token_service) {
730 oauth2_token_service->RevokeCredentials(account_id_to_remove_);
731 PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_REMOVE_ACCT);
733 account_id_to_remove_.clear();
735 ShowView(profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT, avatar_menu_.get());
738 void ProfileChooserView::LinkClicked(views::Link* sender, int event_flags) {
739 if (sender == manage_accounts_link_) {
740 // This link can either mean show/hide the account management view,
741 // depending on which view it is displayed. ShowView() will DCHECK if
742 // the account management view is displayed for non signed-in users.
743 ShowView(
744 view_mode_ == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT ?
745 profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER :
746 profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT,
747 avatar_menu_.get());
748 } else if (sender == add_account_link_) {
749 ShowView(profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT, avatar_menu_.get());
750 PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_ADD_ACCT);
751 } else if (sender == tutorial_learn_more_link_) {
752 ProfileMetrics::LogProfileUpgradeEnrollment(
753 ProfileMetrics::PROFILE_ENROLLMENT_LAUNCH_LEARN_MORE);
754 // TODO(guohui): update |learn_more_url| once it is decided.
755 const GURL lear_more_url("https://support.google.com/chrome/?hl=en#to");
756 chrome::NavigateParams params(
757 browser_->profile(),
758 lear_more_url,
759 content::PAGE_TRANSITION_LINK);
760 params.disposition = NEW_FOREGROUND_TAB;
761 chrome::Navigate(&params);
762 } else {
763 DCHECK(sender == tutorial_end_preview_link_);
764 ShowView(profiles::BUBBLE_VIEW_MODE_END_PREVIEW, avatar_menu_.get());
768 void ProfileChooserView::StyledLabelLinkClicked(
769 const gfx::Range& range, int event_flags) {
770 chrome::ShowSettings(browser_);
773 bool ProfileChooserView::HandleKeyEvent(views::Textfield* sender,
774 const ui::KeyEvent& key_event) {
775 views::Textfield* name_textfield =
776 current_profile_name_->profile_name_textfield();
777 DCHECK(sender == name_textfield);
779 if (key_event.key_code() == ui::VKEY_RETURN ||
780 key_event.key_code() == ui::VKEY_TAB) {
781 // Pressing Tab/Enter commits the new profile name, unless it's empty.
782 base::string16 new_profile_name = name_textfield->text();
783 if (new_profile_name.empty())
784 return true;
786 const AvatarMenu::Item& active_item = avatar_menu_->GetItemAt(
787 avatar_menu_->GetActiveProfileIndex());
788 Profile* profile = g_browser_process->profile_manager()->GetProfile(
789 active_item.profile_path);
790 DCHECK(profile);
792 if (profile->IsSupervised())
793 return true;
795 profiles::UpdateProfileName(profile, new_profile_name);
796 PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_EDIT_NAME);
797 current_profile_name_->ShowReadOnlyView();
798 return true;
800 return false;
803 void ProfileChooserView::PostActionPerformed(
804 ProfileMetrics::ProfileDesktopMenu action_performed) {
805 ProfileMetrics::LogProfileDesktopMenu(action_performed, gaia_service_type_);
806 gaia_service_type_ = signin::GAIA_SERVICE_TYPE_NONE;
809 views::View* ProfileChooserView::CreateProfileChooserView(
810 AvatarMenu* avatar_menu,
811 profiles::TutorialMode last_tutorial_mode) {
812 // TODO(guohui, noms): the view should be customized based on whether new
813 // profile management preview is enabled or not.
815 views::View* view = new views::View();
816 views::GridLayout* layout = CreateSingleColumnLayout(view, kFixedMenuWidth);
817 // Separate items into active and alternatives.
818 Indexes other_profiles;
819 views::View* tutorial_view = NULL;
820 views::View* current_profile_view = NULL;
821 views::View* current_profile_accounts = NULL;
822 views::View* option_buttons_view = NULL;
823 bool is_new_profile_management = switches::IsNewProfileManagement();
824 for (size_t i = 0; i < avatar_menu->GetNumberOfItems(); ++i) {
825 const AvatarMenu::Item& item = avatar_menu->GetItemAt(i);
826 if (item.active) {
827 option_buttons_view = CreateOptionsView(item.signed_in);
828 current_profile_view = CreateCurrentProfileView(item, false);
829 if (view_mode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER) {
830 if (is_new_profile_management) {
831 tutorial_view =
832 last_tutorial_mode == profiles::TUTORIAL_MODE_SEND_FEEDBACK ?
833 CreateSendPreviewFeedbackView() :
834 CreatePreviewEnabledTutorialView(
835 item, last_tutorial_mode == profiles::TUTORIAL_MODE_WELCOME);
836 } else {
837 tutorial_view = CreateNewProfileManagementPreviewView();
839 } else {
840 current_profile_accounts = CreateCurrentProfileAccountsView(item);
842 } else {
843 other_profiles.push_back(i);
847 if (tutorial_view) {
848 // Be sure not to track the tutorial display on View refresh, and only count
849 // the preview-promo view, shown when New Profile Management is off.
850 if (tutorial_mode_ != last_tutorial_mode && !is_new_profile_management) {
851 ProfileMetrics::LogProfileUpgradeEnrollment(
852 ProfileMetrics::PROFILE_ENROLLMENT_SHOW_PREVIEW_PROMO);
854 layout->StartRow(1, 0);
855 layout->AddView(tutorial_view);
858 if (!current_profile_view) {
859 // Guest windows don't have an active profile.
860 current_profile_view = CreateGuestProfileView();
861 option_buttons_view = CreateOptionsView(false);
864 layout->StartRow(1, 0);
865 layout->AddView(current_profile_view);
867 if (browser_->profile()->IsSupervised()) {
868 layout->StartRow(0, 0);
869 layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
870 layout->StartRow(1, 0);
871 layout->AddView(CreateSupervisedUserDisclaimerView());
874 if (view_mode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER) {
875 layout->StartRow(1, 0);
876 if (switches::IsFastUserSwitching())
877 layout->AddView(CreateOtherProfilesView(other_profiles));
878 } else {
879 DCHECK(current_profile_accounts);
880 layout->StartRow(0, 0);
881 layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
882 layout->StartRow(1, 0);
883 layout->AddView(current_profile_accounts);
886 layout->StartRow(0, 0);
887 layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
889 // Option buttons. Only available with the new profile management flag.
890 if (option_buttons_view) {
891 layout->StartRow(0, 0);
892 layout->AddView(option_buttons_view);
895 return view;
898 views::View* ProfileChooserView::CreatePreviewEnabledTutorialView(
899 const AvatarMenu::Item& current_avatar_item,
900 bool tutorial_shown) {
901 if (!switches::IsNewProfileManagementPreviewEnabled())
902 return NULL;
904 Profile* profile = browser_->profile();
905 const int show_count = profile->GetPrefs()->GetInteger(
906 prefs::kProfileAvatarTutorialShown);
907 // Do not show the tutorial if user has dismissed it.
908 if (show_count > kProfileAvatarTutorialShowMax)
909 return NULL;
911 if (!tutorial_shown) {
912 if (show_count == kProfileAvatarTutorialShowMax)
913 return NULL;
914 profile->GetPrefs()->SetInteger(
915 prefs::kProfileAvatarTutorialShown, show_count + 1);
918 return CreateTutorialView(
919 profiles::TUTORIAL_MODE_WELCOME,
920 l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_ENABLED_TUTORIAL_TITLE),
921 l10n_util::GetStringUTF16(
922 IDS_PROFILES_PREVIEW_ENABLED_TUTORIAL_CONTENT_TEXT),
923 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_TUTORIAL_LEARN_MORE),
924 l10n_util::GetStringUTF16(IDS_PROFILES_TUTORIAL_OK_BUTTON),
925 &tutorial_learn_more_link_,
926 &tutorial_ok_button_);
929 views::View* ProfileChooserView::CreateSendPreviewFeedbackView() {
930 return CreateTutorialView(
931 profiles::TUTORIAL_MODE_SEND_FEEDBACK,
932 l10n_util::GetStringUTF16(IDS_PROFILES_FEEDBACK_TUTORIAL_TITLE),
933 l10n_util::GetStringUTF16(
934 IDS_PROFILES_FEEDBACK_TUTORIAL_CONTENT_TEXT),
935 l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW),
936 l10n_util::GetStringUTF16(IDS_PROFILES_SEND_FEEDBACK_BUTTON),
937 &tutorial_end_preview_link_,
938 &tutorial_send_feedback_button_);
941 views::View* ProfileChooserView::CreateTutorialView(
942 profiles::TutorialMode tutorial_mode,
943 const base::string16& title_text,
944 const base::string16& content_text,
945 const base::string16& link_text,
946 const base::string16& button_text,
947 views::Link** link,
948 views::LabelButton** button) {
949 tutorial_mode_ = tutorial_mode;
951 views::View* view = new views::View();
952 view->set_background(views::Background::CreateSolidBackground(
953 profiles::kAvatarTutorialBackgroundColor));
954 views::GridLayout* layout = CreateSingleColumnLayout(view,
955 kFixedMenuWidth - 2 * views::kButtonHEdgeMarginNew);
956 layout->SetInsets(views::kButtonVEdgeMarginNew,
957 views::kButtonHEdgeMarginNew,
958 views::kButtonVEdgeMarginNew,
959 views::kButtonHEdgeMarginNew);
961 // Adds title.
962 views::Label* title_label = new views::Label(title_text);
963 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
964 title_label->SetAutoColorReadabilityEnabled(false);
965 title_label->SetEnabledColor(SK_ColorWHITE);
966 title_label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
967 ui::ResourceBundle::MediumFont));
968 layout->StartRow(1, 0);
969 layout->AddView(title_label);
971 // Adds body content.
972 views::Label* content_label = new views::Label(content_text);
973 content_label->SetMultiLine(true);
974 content_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
975 content_label->SetAutoColorReadabilityEnabled(false);
976 content_label->SetEnabledColor(profiles::kAvatarTutorialContentTextColor);
977 layout->StartRowWithPadding(1, 0, 0, views::kRelatedControlVerticalSpacing);
978 layout->AddView(content_label);
980 // Adds links and buttons.
981 views::View* button_row = new views::View();
982 views::GridLayout* button_layout = new views::GridLayout(button_row);
983 views::ColumnSet* button_columns = button_layout->AddColumnSet(0);
984 button_columns->AddColumn(views::GridLayout::LEADING,
985 views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0);
986 button_columns->AddPaddingColumn(
987 1, views::kUnrelatedControlHorizontalSpacing);
988 button_columns->AddColumn(views::GridLayout::TRAILING,
989 views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0);
990 button_row->SetLayoutManager(button_layout);
992 *link = CreateLink(link_text, this);
993 (*link)->SetHorizontalAlignment(gfx::ALIGN_LEFT);
994 (*link)->SetAutoColorReadabilityEnabled(false);
995 (*link)->SetEnabledColor(SK_ColorWHITE);
996 button_layout->StartRow(1, 0);
997 button_layout->AddView(*link);
999 *button = new views::LabelButton(this, button_text);
1000 (*button)->SetHorizontalAlignment(gfx::ALIGN_CENTER);
1001 (*button)->SetStyle(views::Button::STYLE_BUTTON);
1002 button_layout->AddView(*button);
1004 layout->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing);
1005 layout->AddView(button_row);
1007 // Adds a padded caret image at the bottom.
1008 views::View* padded_caret_view = new views::View();
1009 views::GridLayout* padded_caret_layout =
1010 new views::GridLayout(padded_caret_view);
1011 views::ColumnSet* padded_columns = padded_caret_layout->AddColumnSet(0);
1012 padded_columns->AddPaddingColumn(0, views::kButtonHEdgeMarginNew);
1013 padded_columns->AddColumn(views::GridLayout::LEADING,
1014 views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0);
1015 padded_caret_view->SetLayoutManager(padded_caret_layout);
1017 views::ImageView* caret_image_view = new views::ImageView();
1018 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1019 caret_image_view->SetImage(
1020 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_CARET));
1022 padded_caret_layout->StartRow(1, 0);
1023 padded_caret_layout->AddView(caret_image_view);
1025 views::View* view_with_caret = new views::View();
1026 views::GridLayout* layout_with_caret =
1027 CreateSingleColumnLayout(view_with_caret, kFixedMenuWidth);
1028 layout_with_caret->StartRow(1, 0);
1029 layout_with_caret->AddView(view);
1030 layout_with_caret->StartRow(1, 0);
1031 layout_with_caret->AddView(padded_caret_view);
1032 return view_with_caret;
1035 views::View* ProfileChooserView::CreateCurrentProfileView(
1036 const AvatarMenu::Item& avatar_item,
1037 bool is_guest) {
1038 views::View* view = new views::View();
1039 int column_width = kFixedMenuWidth - 2 * views::kButtonHEdgeMarginNew;
1040 views::GridLayout* layout = CreateSingleColumnLayout(view, column_width);
1041 layout->SetInsets(views::kButtonVEdgeMarginNew,
1042 views::kButtonHEdgeMarginNew,
1043 views::kUnrelatedControlVerticalSpacing,
1044 views::kButtonHEdgeMarginNew);
1046 // Profile icon, centered.
1047 int x_offset = (column_width - kLargeImageSide) / 2;
1048 current_profile_photo_ = new EditableProfilePhoto(
1049 this, avatar_item.icon, !is_guest,
1050 gfx::Rect(x_offset, 0, kLargeImageSide, kLargeImageSide));
1051 SizedContainer* profile_icon_container =
1052 new SizedContainer(gfx::Size(column_width, kLargeImageSide));
1053 profile_icon_container->AddChildView(current_profile_photo_);
1055 if (switches::IsNewProfileManagementPreviewEnabled()) {
1056 question_mark_button_ = new views::ImageButton(this);
1057 question_mark_button_->SetImageAlignment(
1058 views::ImageButton::ALIGN_LEFT, views::ImageButton::ALIGN_MIDDLE);
1059 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1060 question_mark_button_->SetImage(views::ImageButton::STATE_NORMAL,
1061 rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_STABLE));
1062 question_mark_button_->SetImage(views::ImageButton::STATE_HOVERED,
1063 rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_HOVER));
1064 question_mark_button_->SetImage(views::ImageButton::STATE_PRESSED,
1065 rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_QUESTION_SELECT));
1066 gfx::Size preferred_size = question_mark_button_->GetPreferredSize();
1067 question_mark_button_->SetBounds(
1068 0, 0, preferred_size.width(), preferred_size.height());
1069 profile_icon_container->AddChildView(question_mark_button_);
1072 if (browser_->profile()->IsSupervised()) {
1073 views::ImageView* supervised_icon = new views::ImageView();
1074 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1075 supervised_icon->SetImage(
1076 rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_SUPERVISED));
1077 gfx::Size preferred_size = supervised_icon->GetPreferredSize();
1078 gfx::Rect parent_bounds = current_profile_photo_->bounds();
1079 supervised_icon->SetBounds(
1080 parent_bounds.right() - preferred_size.width(),
1081 parent_bounds.bottom() - preferred_size.height(),
1082 preferred_size.width(),
1083 preferred_size.height());
1084 profile_icon_container->AddChildView(supervised_icon);
1087 layout->StartRow(1, 0);
1088 layout->AddView(profile_icon_container);
1090 // Profile name, centered.
1091 bool editing_allowed = !is_guest && !browser_->profile()->IsSupervised();
1092 current_profile_name_ = new EditableProfileName(
1093 this, profiles::GetAvatarNameForProfile(browser_->profile()),
1094 editing_allowed);
1095 layout->StartRow(1, 0);
1096 layout->AddView(current_profile_name_);
1098 if (is_guest)
1099 return view;
1101 // The available links depend on the type of profile that is active.
1102 if (avatar_item.signed_in) {
1103 layout->StartRow(1, 0);
1104 if (switches::IsNewProfileManagement()) {
1105 base::string16 link_title = l10n_util::GetStringUTF16(
1106 view_mode_ == profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER ?
1107 IDS_PROFILES_PROFILE_MANAGE_ACCOUNTS_BUTTON :
1108 IDS_PROFILES_PROFILE_HIDE_MANAGE_ACCOUNTS_BUTTON);
1109 manage_accounts_link_ = CreateLink(link_title, this);
1110 manage_accounts_link_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
1111 layout->AddView(manage_accounts_link_);
1112 } else {
1113 views::Label* email_label = new views::Label(avatar_item.sync_state);
1114 email_label->SetHorizontalAlignment(gfx::ALIGN_CENTER);
1115 layout->AddView(email_label);
1117 } else {
1118 SigninManagerBase* signin_manager =
1119 SigninManagerFactory::GetForProfile(
1120 browser_->profile()->GetOriginalProfile());
1121 if (signin_manager->IsSigninAllowed()) {
1122 signin_current_profile_link_ = new views::BlueButton(
1123 this, l10n_util::GetStringFUTF16(IDS_SYNC_START_SYNC_BUTTON_LABEL,
1124 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)));
1125 layout->StartRow(1, 0);
1126 layout->AddView(signin_current_profile_link_);
1130 return view;
1133 views::View* ProfileChooserView::CreateGuestProfileView() {
1134 gfx::Image guest_icon =
1135 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
1136 profiles::GetPlaceholderAvatarIconResourceID());
1137 AvatarMenu::Item guest_avatar_item(0, 0, guest_icon);
1138 guest_avatar_item.active = true;
1139 guest_avatar_item.name = l10n_util::GetStringUTF16(
1140 IDS_PROFILES_GUEST_PROFILE_NAME);
1141 guest_avatar_item.signed_in = false;
1143 return CreateCurrentProfileView(guest_avatar_item, true);
1146 views::View* ProfileChooserView::CreateOtherProfilesView(
1147 const Indexes& avatars_to_show) {
1148 views::View* view = new views::View();
1149 views::GridLayout* layout = CreateSingleColumnLayout(view, kFixedMenuWidth);
1151 int num_avatars_to_show = avatars_to_show.size();
1152 for (int i = 0; i < num_avatars_to_show; ++i) {
1153 const size_t index = avatars_to_show[i];
1154 const AvatarMenu::Item& item = avatar_menu_->GetItemAt(index);
1155 const int kSmallImageSide = 32;
1157 gfx::Image image = profiles::GetSizedAvatarIcon(
1158 item.icon, true, kSmallImageSide, kSmallImageSide);
1160 views::LabelButton* button = new BackgroundColorHoverButton(
1161 this,
1162 item.name,
1163 *image.ToImageSkia(),
1164 *image.ToImageSkia());
1165 button->set_min_size(gfx::Size(
1166 0, kButtonHeight + views::kRelatedControlVerticalSpacing));
1168 open_other_profile_indexes_map_[button] = index;
1170 layout->StartRow(1, 0);
1171 layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
1172 layout->StartRow(1, 0);
1173 layout->AddView(button);
1176 return view;
1179 views::View* ProfileChooserView::CreateOptionsView(bool enable_lock) {
1180 if (!switches::IsNewProfileManagement())
1181 return NULL;
1183 views::View* view = new views::View();
1184 views::GridLayout* layout;
1186 // Only signed-in users have the ability to lock.
1187 if (enable_lock) {
1188 layout = new views::GridLayout(view);
1189 views::ColumnSet* columns = layout->AddColumnSet(0);
1190 int width_of_lock_button =
1191 2 * views::kUnrelatedControlLargeHorizontalSpacing + 12;
1192 int width_of_users_button = kFixedMenuWidth - width_of_lock_button;
1193 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
1194 views::GridLayout::FIXED, width_of_users_button,
1195 width_of_users_button);
1196 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
1197 views::GridLayout::FIXED, width_of_lock_button,
1198 width_of_lock_button);
1199 view->SetLayoutManager(layout);
1200 } else {
1201 layout = CreateSingleColumnLayout(view, kFixedMenuWidth);
1204 base::string16 text = browser_->profile()->IsGuestSession() ?
1205 l10n_util::GetStringUTF16(IDS_PROFILES_EXIT_GUEST) :
1206 l10n_util::GetStringFUTF16(IDS_PROFILES_NOT_YOU_BUTTON,
1207 profiles::GetAvatarNameForProfile(browser_->profile()));
1208 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1209 users_button_ = new BackgroundColorHoverButton(
1210 this,
1211 text,
1212 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_AVATAR),
1213 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_AVATAR));
1214 users_button_->set_min_size(gfx::Size(
1215 0, kButtonHeight + views::kRelatedControlVerticalSpacing));
1217 layout->StartRow(1, 0);
1218 layout->AddView(users_button_);
1220 if (enable_lock) {
1221 lock_button_ = new BackgroundColorHoverButton(
1222 this,
1223 base::string16(),
1224 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_LOCK),
1225 *rb->GetImageSkiaNamed(IDR_ICON_PROFILES_MENU_LOCK));
1226 lock_button_->set_min_size(gfx::Size(
1227 0, kButtonHeight + views::kRelatedControlVerticalSpacing));
1228 layout->AddView(lock_button_);
1230 return view;
1233 views::View* ProfileChooserView::CreateSupervisedUserDisclaimerView() {
1234 views::View* view = new views::View();
1235 views::GridLayout* layout = CreateSingleColumnLayout(
1236 view, kFixedMenuWidth - 2 * views::kButtonHEdgeMarginNew);
1237 layout->SetInsets(views::kRelatedControlVerticalSpacing,
1238 views::kButtonHEdgeMarginNew,
1239 views::kRelatedControlVerticalSpacing,
1240 views::kButtonHEdgeMarginNew);
1241 views::Label* disclaimer = new views::Label(
1242 avatar_menu_->GetSupervisedUserInformation());
1243 disclaimer->SetMultiLine(true);
1244 disclaimer->SetHorizontalAlignment(gfx::ALIGN_LEFT);
1245 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1246 disclaimer->SetFontList(rb->GetFontList(ui::ResourceBundle::SmallFont));
1247 layout->StartRow(1, 0);
1248 layout->AddView(disclaimer);
1250 return view;
1253 views::View* ProfileChooserView::CreateCurrentProfileAccountsView(
1254 const AvatarMenu::Item& avatar_item) {
1255 DCHECK(avatar_item.signed_in);
1256 views::View* view = new views::View();
1257 view->set_background(views::Background::CreateSolidBackground(
1258 profiles::kAvatarBubbleAccountsBackgroundColor));
1259 views::GridLayout* layout = CreateSingleColumnLayout(view, kFixedMenuWidth);
1261 Profile* profile = browser_->profile();
1262 std::string primary_account =
1263 SigninManagerFactory::GetForProfile(profile)->GetAuthenticatedUsername();
1264 DCHECK(!primary_account.empty());
1265 std::vector<std::string>accounts =
1266 profiles::GetSecondaryAccountsForProfile(profile, primary_account);
1268 // Get state of authentication error, if any.
1269 std::string error_account_id = GetAuthErrorAccountId(profile);
1271 // The primary account should always be listed first.
1272 // TODO(rogerta): we still need to further differentiate the primary account
1273 // from the others in the UI, so more work is likely required here:
1274 // crbug.com/311124.
1275 CreateAccountButton(layout, primary_account, true,
1276 error_account_id == primary_account, kFixedMenuWidth);
1277 for (size_t i = 0; i < accounts.size(); ++i)
1278 CreateAccountButton(layout, accounts[i], false,
1279 error_account_id == accounts[i], kFixedMenuWidth);
1280 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
1282 add_account_link_ = CreateLink(l10n_util::GetStringFUTF16(
1283 IDS_PROFILES_PROFILE_ADD_ACCOUNT_BUTTON, avatar_item.name), this);
1284 add_account_link_->SetBorder(views::Border::CreateEmptyBorder(
1285 0, views::kButtonVEdgeMarginNew,
1286 views::kRelatedControlVerticalSpacing, 0));
1287 layout->StartRow(1, 0);
1288 layout->AddView(add_account_link_);
1289 return view;
1292 void ProfileChooserView::CreateAccountButton(views::GridLayout* layout,
1293 const std::string& account,
1294 bool is_primary_account,
1295 bool reauth_required,
1296 int width) {
1297 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1298 const gfx::ImageSkia* delete_default_image =
1299 rb->GetImageNamed(IDR_CLOSE_1).ToImageSkia();
1300 const int kDeleteButtonWidth = delete_default_image->width();
1301 const gfx::ImageSkia* warning_default_image = reauth_required ?
1302 rb->GetImageNamed(IDR_WARNING).ToImageSkia() : NULL;
1303 const int kWarningButtonWidth = reauth_required ?
1304 warning_default_image->width() + views::kRelatedButtonHSpacing : 0;
1305 int available_width = width -
1306 kDeleteButtonWidth - kWarningButtonWidth - views::kButtonHEdgeMarginNew;
1308 views::LabelButton* email_button = new BackgroundColorHoverButton(
1309 NULL,
1310 gfx::ElideText(base::UTF8ToUTF16(account), gfx::FontList(),
1311 available_width, gfx::ELIDE_EMAIL),
1312 gfx::ImageSkia(),
1313 gfx::ImageSkia());
1314 layout->StartRow(1, 0);
1315 layout->AddView(email_button);
1317 // Delete button.
1318 views::ImageButton* delete_button = new views::ImageButton(this);
1319 delete_button->SetImageAlignment(views::ImageButton::ALIGN_RIGHT,
1320 views::ImageButton::ALIGN_MIDDLE);
1321 delete_button->SetImage(views::ImageButton::STATE_NORMAL,
1322 delete_default_image);
1323 delete_button->SetImage(views::ImageButton::STATE_HOVERED,
1324 rb->GetImageSkiaNamed(IDR_CLOSE_1_H));
1325 delete_button->SetImage(views::ImageButton::STATE_PRESSED,
1326 rb->GetImageSkiaNamed(IDR_CLOSE_1_P));
1327 delete_button->SetBounds(
1328 available_width + kWarningButtonWidth, 0,
1329 kDeleteButtonWidth, kButtonHeight);
1331 email_button->set_notify_enter_exit_on_child(true);
1332 email_button->AddChildView(delete_button);
1334 // Save the original email address, as the button text could be elided.
1335 delete_account_button_map_[delete_button] = account;
1337 // Warning button.
1338 if (reauth_required) {
1339 views::ImageButton* reauth_button = new views::ImageButton(this);
1340 reauth_button->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
1341 views::ImageButton::ALIGN_MIDDLE);
1342 reauth_button->SetImage(views::ImageButton::STATE_NORMAL,
1343 warning_default_image);
1344 reauth_button->SetBounds(
1345 available_width, 0, kWarningButtonWidth, kButtonHeight);
1347 email_button->AddChildView(reauth_button);
1348 reauth_account_button_map_[reauth_button] = account;
1352 views::View* ProfileChooserView::CreateGaiaSigninView() {
1353 GURL url;
1354 int message_id;
1356 switch (view_mode_) {
1357 case profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN:
1358 url = signin::GetPromoURL(signin::SOURCE_AVATAR_BUBBLE_SIGN_IN,
1359 false /* auto_close */,
1360 true /* is_constrained */);
1361 message_id = IDS_PROFILES_GAIA_SIGNIN_TITLE;
1362 break;
1363 case profiles::BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT:
1364 url = signin::GetPromoURL(signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT,
1365 false /* auto_close */,
1366 true /* is_constrained */);
1367 message_id = IDS_PROFILES_GAIA_ADD_ACCOUNT_TITLE;
1368 break;
1369 case profiles::BUBBLE_VIEW_MODE_GAIA_REAUTH: {
1370 DCHECK(HasAuthError(browser_->profile()));
1371 url = signin::GetReauthURL(browser_->profile(),
1372 GetAuthErrorUsername(browser_->profile()));
1373 message_id = IDS_PROFILES_GAIA_REAUTH_TITLE;
1374 break;
1376 default:
1377 NOTREACHED() << "Called with invalid mode=" << view_mode_;
1378 return NULL;
1381 // Adds Gaia signin webview
1382 Profile* profile = browser_->profile();
1383 views::WebView* web_view = new views::WebView(profile);
1384 web_view->LoadInitialURL(url);
1385 web_view->SetPreferredSize(
1386 gfx::Size(kFixedGaiaViewWidth, kFixedGaiaViewHeight));
1388 TitleCard* title_card = new TitleCard(message_id, this,
1389 &gaia_signin_cancel_button_);
1390 return TitleCard::AddPaddedTitleCard(
1391 web_view, title_card, kFixedGaiaViewWidth);
1394 views::View* ProfileChooserView::CreateAccountRemovalView() {
1395 views::View* view = new views::View();
1396 views::GridLayout* layout = CreateSingleColumnLayout(
1397 view, kFixedAccountRemovalViewWidth - 2 * views::kButtonHEdgeMarginNew);
1398 layout->SetInsets(0,
1399 views::kButtonHEdgeMarginNew,
1400 views::kButtonVEdgeMarginNew,
1401 views::kButtonHEdgeMarginNew);
1403 const std::string& primary_account = SigninManagerFactory::GetForProfile(
1404 browser_->profile())->GetAuthenticatedUsername();
1405 bool is_primary_account = primary_account == account_id_to_remove_;
1407 // Adds main text.
1408 layout->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing);
1409 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1410 const gfx::FontList& small_font_list =
1411 rb->GetFontList(ui::ResourceBundle::SmallFont);
1413 if (is_primary_account) {
1414 std::vector<size_t> offsets;
1415 const base::string16 settings_text =
1416 l10n_util::GetStringUTF16(IDS_PROFILES_SETTINGS_LINK);
1417 const base::string16 primary_account_removal_text =
1418 l10n_util::GetStringFUTF16(IDS_PROFILES_PRIMARY_ACCOUNT_REMOVAL_TEXT,
1419 base::UTF8ToUTF16(account_id_to_remove_), settings_text, &offsets);
1420 views::StyledLabel* primary_account_removal_label =
1421 new views::StyledLabel(primary_account_removal_text, this);
1422 primary_account_removal_label->AddStyleRange(
1423 gfx::Range(offsets[1], offsets[1] + settings_text.size()),
1424 views::StyledLabel::RangeStyleInfo::CreateForLink());
1425 primary_account_removal_label->SetBaseFontList(small_font_list);
1426 layout->AddView(primary_account_removal_label);
1427 } else {
1428 views::Label* content_label = new views::Label(
1429 l10n_util::GetStringUTF16(IDS_PROFILES_ACCOUNT_REMOVAL_TEXT));
1430 content_label->SetMultiLine(true);
1431 content_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
1432 content_label->SetFontList(small_font_list);
1433 layout->AddView(content_label);
1436 // Adds button.
1437 if (!is_primary_account) {
1438 remove_account_button_ = new views::BlueButton(
1439 this, l10n_util::GetStringUTF16(IDS_PROFILES_ACCOUNT_REMOVAL_BUTTON));
1440 remove_account_button_->SetHorizontalAlignment(
1441 gfx::ALIGN_CENTER);
1442 layout->StartRowWithPadding(
1443 1, 0, 0, views::kUnrelatedControlVerticalSpacing);
1444 layout->AddView(remove_account_button_);
1445 } else {
1446 layout->AddPaddingRow(0, views::kUnrelatedControlVerticalSpacing);
1449 TitleCard* title_card = new TitleCard(IDS_PROFILES_ACCOUNT_REMOVAL_TITLE,
1450 this, &account_removal_cancel_button_);
1451 return TitleCard::AddPaddedTitleCard(view, title_card,
1452 kFixedAccountRemovalViewWidth);
1455 views::View* ProfileChooserView::CreateNewProfileManagementPreviewView() {
1456 return CreateTutorialView(
1457 profiles::TUTORIAL_MODE_ENABLE_PREVIEW,
1458 l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_TUTORIAL_TITLE),
1459 l10n_util::GetStringUTF16(IDS_PROFILES_PREVIEW_TUTORIAL_CONTENT_TEXT),
1460 l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_TUTORIAL_LEARN_MORE),
1461 l10n_util::GetStringUTF16(IDS_PROFILES_TUTORIAL_TRY_BUTTON),
1462 &tutorial_learn_more_link_,
1463 &tutorial_enable_new_profile_management_button_);
1466 views::View* ProfileChooserView::CreateEndPreviewView() {
1467 views::View* view = new views::View();
1468 views::GridLayout* layout = CreateSingleColumnLayout(
1469 view, kFixedAccountRemovalViewWidth - 2 * views::kButtonHEdgeMarginNew);
1470 layout->SetInsets(0,
1471 views::kButtonHEdgeMarginNew,
1472 views::kButtonVEdgeMarginNew,
1473 views::kButtonHEdgeMarginNew);
1475 // Adds main text.
1476 views::Label* content_label = new views::Label(
1477 l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW_TEXT));
1478 content_label->SetMultiLine(true);
1479 content_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
1480 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
1481 const gfx::FontList& small_font_list =
1482 rb->GetFontList(ui::ResourceBundle::SmallFont);
1483 content_label->SetFontList(small_font_list);
1484 layout->StartRowWithPadding(1, 0, 0, views::kUnrelatedControlVerticalSpacing);
1485 layout->AddView(content_label);
1487 // Adds button.
1488 end_preview_and_relaunch_button_ = new views::BlueButton(
1489 this, l10n_util::GetStringUTF16(IDS_PROFILES_END_PREVIEW_AND_RELAUNCH));
1490 end_preview_and_relaunch_button_->SetHorizontalAlignment(
1491 gfx::ALIGN_CENTER);
1492 layout->StartRowWithPadding(
1493 1, 0, 0, views::kUnrelatedControlVerticalSpacing);
1494 layout->AddView(end_preview_and_relaunch_button_);
1496 TitleCard* title_card = new TitleCard(
1497 IDS_PROFILES_END_PREVIEW, this, &end_preview_cancel_button_);
1498 return TitleCard::AddPaddedTitleCard(
1499 view, title_card, kFixedAccountRemovalViewWidth);