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