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