tray_user.cc revision ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16
1// Copyright (c) 2012 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 "ash/system/user/tray_user.h"
6
7#include <algorithm>
8#include <climits>
9#include <vector>
10
11#include "ash/ash_switches.h"
12#include "ash/popup_message.h"
13#include "ash/session_state_delegate.h"
14#include "ash/shell.h"
15#include "ash/shell_delegate.h"
16#include "ash/system/tray/system_tray.h"
17#include "ash/system/tray/system_tray_delegate.h"
18#include "ash/system/tray/system_tray_notifier.h"
19#include "ash/system/tray/tray_constants.h"
20#include "ash/system/tray/tray_item_view.h"
21#include "ash/system/tray/tray_popup_label_button.h"
22#include "ash/system/tray/tray_popup_label_button_border.h"
23#include "ash/system/tray/tray_utils.h"
24#include "base/i18n/rtl.h"
25#include "base/logging.h"
26#include "base/memory/scoped_vector.h"
27#include "base/strings/string16.h"
28#include "base/strings/string_util.h"
29#include "base/strings/utf_string_conversions.h"
30#include "grit/ash_resources.h"
31#include "grit/ash_strings.h"
32#include "skia/ext/image_operations.h"
33#include "third_party/skia/include/core/SkCanvas.h"
34#include "third_party/skia/include/core/SkPaint.h"
35#include "third_party/skia/include/core/SkPath.h"
36#include "ui/aura/window.h"
37#include "ui/base/l10n/l10n_util.h"
38#include "ui/base/range/range.h"
39#include "ui/base/resource/resource_bundle.h"
40#include "ui/base/text/text_elider.h"
41#include "ui/gfx/canvas.h"
42#include "ui/gfx/font.h"
43#include "ui/gfx/image/image.h"
44#include "ui/gfx/image/image_skia_operations.h"
45#include "ui/gfx/insets.h"
46#include "ui/gfx/rect.h"
47#include "ui/gfx/render_text.h"
48#include "ui/gfx/size.h"
49#include "ui/gfx/skia_util.h"
50#include "ui/views/border.h"
51#include "ui/views/bubble/tray_bubble_view.h"
52#include "ui/views/controls/button/button.h"
53#include "ui/views/controls/button/custom_button.h"
54#include "ui/views/controls/image_view.h"
55#include "ui/views/controls/label.h"
56#include "ui/views/controls/link.h"
57#include "ui/views/controls/link_listener.h"
58#include "ui/views/corewm/shadow_types.h"
59#include "ui/views/layout/box_layout.h"
60#include "ui/views/layout/fill_layout.h"
61#include "ui/views/mouse_watcher.h"
62#include "ui/views/painter.h"
63#include "ui/views/view.h"
64#include "ui/views/widget/widget.h"
65
66namespace {
67
68const int kUserDetailsVerticalPadding = 5;
69const int kUserCardVerticalPadding = 10;
70const int kInactiveUserCardVerticalPadding = 4;
71const int kProfileRoundedCornerRadius = 2;
72const int kUserIconSize = 27;
73const int kUserIconLargeSize = 32;
74const int kUserIconLargeCornerRadius = 2;
75const int kUserLabelToIconPadding = 5;
76
77// When a hover border is used, it is starting this many pixels before the icon
78// position.
79const int kTrayUserTileHoverBorderInset = 10;
80
81// The border color of the user button.
82const SkColor kBorderColor = 0xffdcdcdc;
83
84// The invisible word joiner character, used as a marker to indicate the start
85// and end of the user's display name in the public account user card's text.
86const char16 kDisplayNameMark[] = { 0x2060, 0 };
87
88const int kPublicAccountLogoutButtonBorderImagesNormal[] = {
89    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
90    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
91    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
92    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
93    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
94    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
95    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
96    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
97    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_NORMAL_BACKGROUND,
98};
99
100const int kPublicAccountLogoutButtonBorderImagesHovered[] = {
101    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
102    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
103    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
104    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
105    IDR_AURA_TRAY_POPUP_LABEL_BUTTON_HOVER_BACKGROUND,
106    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
107    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
108    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
109    IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER,
110};
111
112// Offsetting the popup message relative to the tray menu.
113const int kPopupMessageOffset = 25;
114
115}  // namespace
116
117namespace ash {
118namespace internal {
119
120namespace tray {
121
122// A custom image view with rounded edges.
123class RoundedImageView : public views::View {
124 public:
125  // Constructs a new rounded image view with rounded corners of radius
126  // |corner_radius|. If |active_user| is set, the icon will be drawn in
127  // full colors - otherwise it will fade into the background.
128  RoundedImageView(int corner_radius, bool active_user);
129  virtual ~RoundedImageView();
130
131  // Set the image that should be displayed. The image contents is copied to the
132  // receiver's image.
133  void SetImage(const gfx::ImageSkia& img, const gfx::Size& size);
134
135  // Set the radii of the corners independantly.
136  void SetCornerRadii(int top_left,
137                      int top_right,
138                      int bottom_right,
139                      int bottom_left);
140
141 private:
142  // Overridden from views::View.
143  virtual gfx::Size GetPreferredSize() OVERRIDE;
144  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
145
146  gfx::ImageSkia image_;
147  gfx::ImageSkia resized_;
148  gfx::Size image_size_;
149  int corner_radius_[4];
150
151  // True if the given user is the active user and the icon should get
152  // painted as active.
153  bool active_user_;
154
155  DISALLOW_COPY_AND_ASSIGN(RoundedImageView);
156};
157
158// The user details shown in public account mode. This is essentially a label
159// but with custom painting code as the text is styled with multiple colors and
160// contains a link.
161class PublicAccountUserDetails : public views::View,
162                                 public views::LinkListener {
163 public:
164  PublicAccountUserDetails(SystemTrayItem* owner, int used_width);
165  virtual ~PublicAccountUserDetails();
166
167 private:
168  // Overridden from views::View.
169  virtual void Layout() OVERRIDE;
170  virtual gfx::Size GetPreferredSize() OVERRIDE;
171  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
172
173  // Overridden from views::LinkListener.
174  virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
175
176  // Calculate a preferred size that ensures the label text and the following
177  // link do not wrap over more than three lines in total for aesthetic reasons
178  // if possible.
179  void CalculatePreferredSize(SystemTrayItem* owner, int used_width);
180
181  base::string16 text_;
182  views::Link* learn_more_;
183  gfx::Size preferred_size_;
184  ScopedVector<gfx::RenderText> lines_;
185
186  DISALLOW_COPY_AND_ASSIGN(PublicAccountUserDetails);
187};
188
189// The button which holds the user card in case of multi profile.
190class UserCard : public views::CustomButton {
191 public:
192  UserCard(views::ButtonListener* listener, bool active_user);
193  virtual ~UserCard();
194
195  // Called when the border should remain even in the non highlighted state.
196  void ForceBorderVisible(bool show);
197
198  // Overridden from views::View
199  virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE;
200  virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE;
201
202  // Check if the item is hovered.
203  bool is_hovered_for_test() {return button_hovered_; }
204
205 private:
206  // Change the hover/active state of the "button" when the status changes.
207  void ShowActive();
208
209  // True if this is the active user.
210  bool is_active_user_;
211
212  // True if button is hovered.
213  bool button_hovered_;
214
215  // True if the border should be visible.
216  bool show_border_;
217
218  DISALLOW_COPY_AND_ASSIGN(UserCard);
219};
220
221class UserViewMouseWatcherHost : public views::MouseWatcherHost {
222public:
223 explicit UserViewMouseWatcherHost(const gfx::Rect& screen_area)
224     : screen_area_(screen_area) {}
225 virtual ~UserViewMouseWatcherHost() {}
226
227 // Implementation of MouseWatcherHost.
228 virtual bool Contains(const gfx::Point& screen_point,
229                       views::MouseWatcherHost::MouseEventType type) OVERRIDE {
230   return screen_area_.Contains(screen_point);
231 }
232
233private:
234 gfx::Rect screen_area_;
235
236 DISALLOW_COPY_AND_ASSIGN(UserViewMouseWatcherHost);
237};
238
239// The view of a user item.
240class UserView : public views::View,
241                 public views::ButtonListener,
242                 public views::MouseWatcherListener {
243 public:
244  UserView(SystemTrayItem* owner,
245           ash::user::LoginStatus login,
246           MultiProfileIndex index);
247  virtual ~UserView();
248
249  // Overridden from MouseWatcherListener:
250  virtual void MouseMovedOutOfHost() OVERRIDE;
251
252  TrayUser::TestState GetStateForTest() const;
253  gfx::Rect GetBoundsInScreenOfUserButtonForTest();
254
255 private:
256  // Overridden from views::View.
257  virtual gfx::Size GetPreferredSize() OVERRIDE;
258  virtual int GetHeightForWidth(int width) OVERRIDE;
259  virtual void Layout() OVERRIDE;
260
261  // Overridden from views::ButtonListener.
262  virtual void ButtonPressed(views::Button* sender,
263                             const ui::Event& event) OVERRIDE;
264
265  void AddLogoutButton(ash::user::LoginStatus login);
266  void AddUserCard(SystemTrayItem* owner, ash::user::LoginStatus login);
267
268  // Create a user icon representation for the user card.
269  views::View* CreateIconForUserCard(ash::user::LoginStatus login);
270
271  // Create the additional user card content for the retail logged in mode.
272  void AddLoggedInRetailModeUserCardContent();
273
274  // Create the additional user card content for the public mode.
275  void AddLoggedInPublicModeUserCardContent(SystemTrayItem* owner);
276
277  // Create the menu option to add another user. If |disabled| is set the user
278  // cannot actively click on the item.
279  void ToggleAddUserMenuOption();
280
281  MultiProfileIndex multiprofile_index_;
282  // The view of the user card.
283  views::View* user_card_view_;
284
285  // This is the owner system tray item of this view.
286  SystemTrayItem* owner_;
287
288  // True if |user_card_view_| is a |UserView| - otherwise it is only a
289  // |views::View|.
290  bool is_user_card_;
291  views::View* logout_button_;
292  scoped_ptr<ash::PopupMessage> popup_message_;
293  scoped_ptr<views::Widget> add_menu_option_;
294
295  // True when the add user panel is visible but not activatable.
296  bool add_user_visible_but_disabled_;
297
298  // The mouse watcher which takes care of out of window hover events.
299  scoped_ptr<views::MouseWatcher> mouse_watcher_;
300
301  DISALLOW_COPY_AND_ASSIGN(UserView);
302};
303
304// The menu item view which gets shown when the user clicks in multi profile
305// mode onto the user item.
306class AddUserView : public views::CustomButton,
307                    public views::ButtonListener {
308 public:
309  // The |owner| is the view for which this view gets created. The |listener|
310  // will get notified when this item gets clicked.
311  AddUserView(UserCard* owner, views::ButtonListener* listener);
312  virtual ~AddUserView();
313
314  // Get the anchor view for a message.
315  views::View* anchor() { return anchor_; }
316
317  // Overridden from views::ButtonListener.
318  virtual void ButtonPressed(views::Button* sender,
319                             const ui::Event& event) OVERRIDE;
320
321 private:
322  // Overridden from views::View.
323  virtual gfx::Size GetPreferredSize() OVERRIDE;
324  virtual int GetHeightForWidth(int width) OVERRIDE;
325  virtual void Layout() OVERRIDE;
326
327  // Create the additional client content for this item.
328  void AddContent();
329
330  // This is the content we create and show.
331  views::View* add_user_;
332
333  // This listener will get informed when someone clicks on this button.
334  views::ButtonListener* listener_;
335
336  // This is the owner view of this item.
337  UserCard* owner_;
338
339  // The anchor view for targetted bubble messages.
340  views::View* anchor_;
341
342  DISALLOW_COPY_AND_ASSIGN(AddUserView);
343};
344
345RoundedImageView::RoundedImageView(int corner_radius, bool active_user)
346    : active_user_(active_user) {
347  for (int i = 0; i < 4; ++i)
348    corner_radius_[i] = corner_radius;
349}
350
351RoundedImageView::~RoundedImageView() {}
352
353void RoundedImageView::SetImage(const gfx::ImageSkia& img,
354                                const gfx::Size& size) {
355  image_ = img;
356  image_size_ = size;
357
358  // Try to get the best image quality for the avatar.
359  resized_ = gfx::ImageSkiaOperations::CreateResizedImage(image_,
360      skia::ImageOperations::RESIZE_BEST, size);
361  if (GetWidget() && visible()) {
362    PreferredSizeChanged();
363    SchedulePaint();
364  }
365}
366
367void RoundedImageView::SetCornerRadii(int top_left,
368                                      int top_right,
369                                      int bottom_right,
370                                      int bottom_left) {
371  corner_radius_[0] = top_left;
372  corner_radius_[1] = top_right;
373  corner_radius_[2] = bottom_right;
374  corner_radius_[3] = bottom_left;
375}
376
377gfx::Size RoundedImageView::GetPreferredSize() {
378  return gfx::Size(image_size_.width() + GetInsets().width(),
379                   image_size_.height() + GetInsets().height());
380}
381
382void RoundedImageView::OnPaint(gfx::Canvas* canvas) {
383  View::OnPaint(canvas);
384  gfx::Rect image_bounds(size());
385  image_bounds.ClampToCenteredSize(GetPreferredSize());
386  image_bounds.Inset(GetInsets());
387  const SkScalar kRadius[8] = {
388    SkIntToScalar(corner_radius_[0]),
389    SkIntToScalar(corner_radius_[0]),
390    SkIntToScalar(corner_radius_[1]),
391    SkIntToScalar(corner_radius_[1]),
392    SkIntToScalar(corner_radius_[2]),
393    SkIntToScalar(corner_radius_[2]),
394    SkIntToScalar(corner_radius_[3]),
395    SkIntToScalar(corner_radius_[3])
396  };
397  SkPath path;
398  path.addRoundRect(gfx::RectToSkRect(image_bounds), kRadius);
399  SkPaint paint;
400  paint.setAntiAlias(true);
401  paint.setXfermodeMode(active_user_ ? SkXfermode::kSrcOver_Mode :
402                                       SkXfermode::kLuminosity_Mode);
403  canvas->DrawImageInPath(resized_, image_bounds.x(), image_bounds.y(),
404                          path, paint);
405}
406
407PublicAccountUserDetails::PublicAccountUserDetails(SystemTrayItem* owner,
408                                                   int used_width)
409    : learn_more_(NULL) {
410  const int inner_padding =
411      kTrayPopupPaddingHorizontal - kTrayPopupPaddingBetweenItems;
412  const bool rtl = base::i18n::IsRTL();
413  set_border(views::Border::CreateEmptyBorder(
414      kUserDetailsVerticalPadding, rtl ? 0 : inner_padding,
415      kUserDetailsVerticalPadding, rtl ? inner_padding : 0));
416
417  // Retrieve the user's display name and wrap it with markers.
418  // Note that since this is a public account it always has to be the primary
419  // user.
420  base::string16 display_name =
421      ash::Shell::GetInstance()->session_state_delegate()->
422          GetUserDisplayName(0);
423  RemoveChars(display_name, kDisplayNameMark, &display_name);
424  display_name = kDisplayNameMark[0] + display_name + kDisplayNameMark[0];
425  // Retrieve the domain managing the device and wrap it with markers.
426  base::string16 domain = UTF8ToUTF16(
427      ash::Shell::GetInstance()->system_tray_delegate()->GetEnterpriseDomain());
428  RemoveChars(domain, kDisplayNameMark, &domain);
429  base::i18n::WrapStringWithLTRFormatting(&domain);
430  // Retrieve the label text, inserting the display name and domain.
431  text_ = l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_PUBLIC_LABEL,
432                                     display_name, domain);
433
434  learn_more_ = new views::Link(l10n_util::GetStringUTF16(IDS_ASH_LEARN_MORE));
435  learn_more_->SetUnderline(false);
436  learn_more_->set_listener(this);
437  AddChildView(learn_more_);
438
439  CalculatePreferredSize(owner, used_width);
440}
441
442PublicAccountUserDetails::~PublicAccountUserDetails() {}
443
444void PublicAccountUserDetails::Layout() {
445  lines_.clear();
446  const gfx::Rect contents_area = GetContentsBounds();
447  if (contents_area.IsEmpty())
448    return;
449
450  // Word-wrap the label text.
451  const gfx::Font font;
452  std::vector<base::string16> lines;
453  ui::ElideRectangleText(text_, font, contents_area.width(),
454                         contents_area.height(), ui::ELIDE_LONG_WORDS, &lines);
455  // Loop through the lines, creating a renderer for each.
456  gfx::Point position = contents_area.origin();
457  ui::Range display_name(ui::Range::InvalidRange());
458  for (std::vector<base::string16>::const_iterator it = lines.begin();
459       it != lines.end(); ++it) {
460    gfx::RenderText* line = gfx::RenderText::CreateInstance();
461    line->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_UI);
462    line->SetText(*it);
463    const gfx::Size size(contents_area.width(), line->GetStringSize().height());
464    line->SetDisplayRect(gfx::Rect(position, size));
465    position.set_y(position.y() + size.height());
466
467    // Set the default text color for the line.
468    line->SetColor(kPublicAccountUserCardTextColor);
469
470    // If a range of the line contains the user's display name, apply a custom
471    // text color to it.
472    if (display_name.is_empty())
473      display_name.set_start(it->find(kDisplayNameMark));
474    if (!display_name.is_empty()) {
475      display_name.set_end(
476          it->find(kDisplayNameMark, display_name.start() + 1));
477      ui::Range line_range(0, it->size());
478      line->ApplyColor(kPublicAccountUserCardNameColor,
479                       display_name.Intersect(line_range));
480      // Update the range for the next line.
481      if (display_name.end() >= line_range.end())
482        display_name.set_start(0);
483      else
484        display_name = ui::Range::InvalidRange();
485    }
486
487    lines_.push_back(line);
488  }
489
490  // Position the link after the label text, separated by a space. If it does
491  // not fit onto the last line of the text, wrap the link onto its own line.
492  const gfx::Size last_line_size = lines_.back()->GetStringSize();
493  const int space_width = font.GetStringWidth(ASCIIToUTF16(" "));
494  const gfx::Size link_size = learn_more_->GetPreferredSize();
495  if (contents_area.width() - last_line_size.width() >=
496      space_width + link_size.width()) {
497    position.set_x(position.x() + last_line_size.width() + space_width);
498    position.set_y(position.y() - last_line_size.height());
499  }
500  position.set_y(position.y() - learn_more_->GetInsets().top());
501  gfx::Rect learn_more_bounds(position, link_size);
502  learn_more_bounds.Intersect(contents_area);
503  if (base::i18n::IsRTL()) {
504    const gfx::Insets insets = GetInsets();
505    learn_more_bounds.Offset(insets.right() - insets.left(), 0);
506  }
507  learn_more_->SetBoundsRect(learn_more_bounds);
508}
509
510gfx::Size PublicAccountUserDetails::GetPreferredSize() {
511  return preferred_size_;
512}
513
514void PublicAccountUserDetails::OnPaint(gfx::Canvas* canvas) {
515  for (ScopedVector<gfx::RenderText>::const_iterator it = lines_.begin();
516       it != lines_.end(); ++it) {
517    (*it)->Draw(canvas);
518  }
519  views::View::OnPaint(canvas);
520}
521
522void PublicAccountUserDetails::LinkClicked(views::Link* source,
523                                           int event_flags) {
524  DCHECK_EQ(source, learn_more_);
525  ash::Shell::GetInstance()->system_tray_delegate()->ShowPublicAccountInfo();
526}
527
528void PublicAccountUserDetails::CalculatePreferredSize(SystemTrayItem* owner,
529                                                      int used_width) {
530  const gfx::Font font;
531  const gfx::Size link_size = learn_more_->GetPreferredSize();
532  const int space_width = font.GetStringWidth(ASCIIToUTF16(" "));
533  const gfx::Insets insets = GetInsets();
534  views::TrayBubbleView* bubble_view =
535      owner->system_tray()->GetSystemBubble()->bubble_view();
536  int min_width = std::max(
537      link_size.width(),
538      bubble_view->GetPreferredSize().width() - (used_width + insets.width()));
539  int max_width = std::min(
540      font.GetStringWidth(text_) + space_width + link_size.width(),
541      bubble_view->GetMaximumSize().width() - (used_width + insets.width()));
542  // Do a binary search for the minimum width that ensures no more than three
543  // lines are needed. The lower bound is the minimum of the current bubble
544  // width and the width of the link (as no wrapping is permitted inside the
545  // link). The upper bound is the maximum of the largest allowed bubble width
546  // and the sum of the label text and link widths when put on a single line.
547  std::vector<base::string16> lines;
548  while (min_width < max_width) {
549    lines.clear();
550    const int width = (min_width + max_width) / 2;
551    const bool too_narrow = ui::ElideRectangleText(
552        text_, font, width, INT_MAX, ui::TRUNCATE_LONG_WORDS, &lines) != 0;
553    int line_count = lines.size();
554    if (!too_narrow && line_count == 3 &&
555            width - font.GetStringWidth(lines.back()) <=
556            space_width + link_size.width()) {
557      ++line_count;
558    }
559    if (too_narrow || line_count > 3)
560      min_width = width + 1;
561    else
562      max_width = width;
563  }
564
565  // Calculate the corresponding height and set the preferred size.
566  lines.clear();
567  ui::ElideRectangleText(
568      text_, font, min_width, INT_MAX, ui::TRUNCATE_LONG_WORDS, &lines);
569  int line_count = lines.size();
570  if (min_width - font.GetStringWidth(lines.back()) <=
571      space_width + link_size.width()) {
572    ++line_count;
573  }
574  const int line_height = font.GetHeight();
575  const int link_extra_height = std::max(
576      link_size.height() - learn_more_->GetInsets().top() - line_height, 0);
577  preferred_size_ = gfx::Size(
578      min_width + insets.width(),
579      line_count * line_height + link_extra_height + insets.height());
580
581  bubble_view->SetWidth(preferred_size_.width() + used_width);
582}
583
584UserCard::UserCard(views::ButtonListener* listener, bool active_user)
585    : CustomButton(listener),
586      is_active_user_(active_user),
587      button_hovered_(false),
588      show_border_(false) {
589  if (is_active_user_) {
590    set_background(
591        views::Background::CreateSolidBackground(kBackgroundColor));
592    ShowActive();
593  }
594}
595
596UserCard::~UserCard() {}
597
598void UserCard::ForceBorderVisible(bool show) {
599  show_border_ = show;
600  ShowActive();
601}
602
603void UserCard::OnMouseEntered(const ui::MouseEvent& event) {
604  if (is_active_user_) {
605    button_hovered_ = true;
606    background()->SetNativeControlColor(kHoverBackgroundColor);
607    ShowActive();
608  }
609}
610
611void UserCard::OnMouseExited(const ui::MouseEvent& event) {
612  if (is_active_user_) {
613    button_hovered_ = false;
614    background()->SetNativeControlColor(kBackgroundColor);
615    ShowActive();
616  }
617}
618
619void UserCard::ShowActive() {
620  int width = button_hovered_ || show_border_ ? 1 : 0;
621  set_border(views::Border::CreateSolidSidedBorder(width, width, width, 1,
622                                                   kBorderColor));
623  SchedulePaint();
624}
625
626UserView::UserView(SystemTrayItem* owner,
627                   ash::user::LoginStatus login,
628                   MultiProfileIndex index)
629    : multiprofile_index_(index),
630      user_card_view_(NULL),
631      owner_(owner),
632      is_user_card_(false),
633      logout_button_(NULL),
634      add_user_visible_but_disabled_(false) {
635  CHECK_NE(ash::user::LOGGED_IN_NONE, login);
636  if (!index) {
637    // Only the logged in user will have a background. All other users will have
638    // to allow the TrayPopupContainer highlighting the menu line.
639    set_background(views::Background::CreateSolidBackground(
640        login == ash::user::LOGGED_IN_PUBLIC ? kPublicAccountBackgroundColor :
641                                               kBackgroundColor));
642  }
643  SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0,
644                                        kTrayPopupPaddingBetweenItems));
645  // The logout button must be added before the user card so that the user card
646  // can correctly calculate the remaining available width.
647  // Note that only the current multiprofile user gets a button.
648  AddLogoutButton(!multiprofile_index_ ? login : ash::user::LOGGED_IN_LOCKED);
649  AddUserCard(owner, login);
650}
651
652UserView::~UserView() {}
653
654void UserView::MouseMovedOutOfHost() {
655  popup_message_.reset();
656  mouse_watcher_.reset();
657  add_menu_option_.reset();
658}
659
660TrayUser::TestState UserView::GetStateForTest() const {
661  if (add_menu_option_.get()) {
662    return add_user_visible_but_disabled_ ? TrayUser::ACTIVE_BUT_DISABLED :
663                                            TrayUser::ACTIVE;
664  }
665
666  if (!is_user_card_)
667    return TrayUser::SHOWN;
668
669  return static_cast<UserCard*>(user_card_view_)->is_hovered_for_test() ?
670      TrayUser::HOVERED : TrayUser::SHOWN;
671}
672
673gfx::Rect UserView::GetBoundsInScreenOfUserButtonForTest() {
674  DCHECK(user_card_view_);
675  return user_card_view_->GetBoundsInScreen();
676}
677
678gfx::Size UserView::GetPreferredSize() {
679  gfx::Size size = views::View::GetPreferredSize();
680  // Only the active user panel will be forced to a certain height.
681  if (!multiprofile_index_) {
682    size.set_height(std::max(size.height(),
683                             kTrayPopupItemHeight + GetInsets().height()));
684  }
685  return size;
686}
687
688int UserView::GetHeightForWidth(int width) {
689  return GetPreferredSize().height();
690}
691
692void UserView::Layout() {
693  gfx::Rect contents_area(GetContentsBounds());
694  if (user_card_view_ && logout_button_) {
695    // Give the logout button the space it requests.
696    gfx::Rect logout_area = contents_area;
697    logout_area.ClampToCenteredSize(logout_button_->GetPreferredSize());
698    logout_area.set_x(contents_area.right() - logout_area.width());
699
700    // Give the remaining space to the user card.
701    gfx::Rect user_card_area = contents_area;
702    int remaining_width = contents_area.width() - logout_area.width();
703    if (ash::Shell::GetInstance()->delegate()->IsMultiProfilesEnabled()) {
704      // In multiprofile case |user_card_view_| and |logout_button_| have to
705      // have the same height.
706      int y = std::min(user_card_area.y(), logout_area.y());
707      int height = std::max(user_card_area.height(), logout_area.height());
708      logout_area.set_y(y);
709      logout_area.set_height(height);
710      user_card_area.set_y(y);
711      user_card_area.set_height(height);
712
713      // In multiprofile mode we have also to increase the size of the card by
714      // the size of the border to make it overlap with the logout button.
715      user_card_area.set_width(std::max(0, remaining_width + 1));
716
717      // To make the logout button symmetrical with the user card we also make
718      // the button longer by the same size the hover area in front of the icon
719      // got inset.
720      logout_area.set_width(logout_area.width() +
721                            kTrayUserTileHoverBorderInset);
722    } else {
723      // In all other modes we have to make sure that there is enough spacing
724      // between the two.
725      remaining_width -= kTrayPopupPaddingBetweenItems;
726    }
727    user_card_area.set_width(remaining_width);
728    user_card_view_->SetBoundsRect(user_card_area);
729    logout_button_->SetBoundsRect(logout_area);
730  } else if (user_card_view_) {
731    user_card_view_->SetBoundsRect(contents_area);
732  } else if (logout_button_) {
733    logout_button_->SetBoundsRect(contents_area);
734  }
735}
736
737void UserView::ButtonPressed(views::Button* sender, const ui::Event& event) {
738  if (sender == logout_button_) {
739    ash::Shell::GetInstance()->system_tray_delegate()->SignOut();
740  } else if (sender == user_card_view_ &&
741             ash::Shell::GetInstance()->delegate()->IsMultiProfilesEnabled()) {
742    if (!multiprofile_index_) {
743      ToggleAddUserMenuOption();
744    } else {
745      ash::SessionStateDelegate* delegate =
746          ash::Shell::GetInstance()->session_state_delegate();
747      delegate->SwitchActiveUser(delegate->GetUserEmail(multiprofile_index_));
748      // Since the user list is about to change the system menu should get
749      // closed.
750      owner_->system_tray()->CloseSystemBubble();
751    }
752  } else if (add_menu_option_.get() &&
753             sender == add_menu_option_->GetContentsView()) {
754    // Let the user add another account to the session.
755    ash::Shell::GetInstance()->system_tray_delegate()->ShowUserLogin();
756  } else {
757    NOTREACHED();
758  }
759}
760
761void UserView::AddLogoutButton(ash::user::LoginStatus login) {
762  // A user should not be able to modify logged-in state when screen is
763  // locked.
764  if (login == ash::user::LOGGED_IN_LOCKED)
765    return;
766
767  const base::string16 title = ash::user::GetLocalizedSignOutStringForStatus(
768      login, true);
769  TrayPopupLabelButton* logout_button = new TrayPopupLabelButton(this, title);
770  logout_button->SetAccessibleName(title);
771  logout_button_ = logout_button;
772  // In public account mode, the logout button border has a custom color.
773  if (login == ash::user::LOGGED_IN_PUBLIC) {
774    TrayPopupLabelButtonBorder* border =
775        static_cast<TrayPopupLabelButtonBorder*>(logout_button_->border());
776    border->SetPainter(false, views::Button::STATE_NORMAL,
777                       views::Painter::CreateImageGridPainter(
778                           kPublicAccountLogoutButtonBorderImagesNormal));
779    border->SetPainter(false, views::Button::STATE_HOVERED,
780                       views::Painter::CreateImageGridPainter(
781                           kPublicAccountLogoutButtonBorderImagesHovered));
782    border->SetPainter(false, views::Button::STATE_PRESSED,
783                       views::Painter::CreateImageGridPainter(
784                           kPublicAccountLogoutButtonBorderImagesHovered));
785  }
786  AddChildView(logout_button_);
787}
788
789void UserView::AddUserCard(SystemTrayItem* owner,
790                           ash::user::LoginStatus login) {
791  // Add padding around the panel.
792  set_border(views::Border::CreateEmptyBorder(
793      kUserCardVerticalPadding, kTrayPopupPaddingHorizontal,
794      kUserCardVerticalPadding, kTrayPopupPaddingHorizontal));
795
796  if (ash::Shell::GetInstance()->delegate()->IsMultiProfilesEnabled() &&
797      login != ash::user::LOGGED_IN_RETAIL_MODE) {
798    user_card_view_ = new UserCard(this, multiprofile_index_ == 0);
799    is_user_card_ = true;
800  } else {
801    user_card_view_ = new views::View();
802    is_user_card_ = false;
803  }
804
805  user_card_view_->SetLayoutManager(new views::BoxLayout(
806      views::BoxLayout::kHorizontal, 0, 0 , kTrayPopupPaddingBetweenItems));
807  AddChildViewAt(user_card_view_, 0);
808
809  if (login == ash::user::LOGGED_IN_RETAIL_MODE) {
810    AddLoggedInRetailModeUserCardContent();
811    return;
812  }
813
814  // The entire user card should trigger hover (the inner items get disabled).
815  user_card_view_->SetEnabled(true);
816  user_card_view_->set_notify_enter_exit_on_child(true);
817
818  if (login == ash::user::LOGGED_IN_PUBLIC) {
819    AddLoggedInPublicModeUserCardContent(owner);
820    return;
821  }
822
823  views::View* icon = CreateIconForUserCard(login);
824  user_card_view_->AddChildView(icon);
825
826  // To allow the border to start before the icon, reduce the size before and
827  // add an inset to the icon to get the spacing.
828  if (multiprofile_index_ == 0 &&
829      ash::Shell::GetInstance()->delegate()->IsMultiProfilesEnabled()) {
830    icon->set_border(views::Border::CreateEmptyBorder(
831        0, kTrayUserTileHoverBorderInset, 0, 0));
832    set_border(views::Border::CreateEmptyBorder(
833        kUserCardVerticalPadding,
834        kTrayPopupPaddingHorizontal - kTrayUserTileHoverBorderInset,
835        kUserCardVerticalPadding,
836        kTrayPopupPaddingHorizontal));
837  }
838  ash::SessionStateDelegate* delegate =
839      ash::Shell::GetInstance()->session_state_delegate();
840  views::Label* username = NULL;
841  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
842  if (!multiprofile_index_) {
843    base::string16 user_name_string =
844        login == ash::user::LOGGED_IN_GUEST ?
845            bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_GUEST_LABEL) :
846            delegate->GetUserDisplayName(multiprofile_index_);
847    if (!user_name_string.empty()) {
848      username = new views::Label(user_name_string);
849      username->SetHorizontalAlignment(gfx::ALIGN_LEFT);
850    }
851  }
852
853  views::Label* additional = NULL;
854  if (login != ash::user::LOGGED_IN_GUEST) {
855    base::string16 user_email_string =
856        login == ash::user::LOGGED_IN_LOCALLY_MANAGED ?
857            bundle.GetLocalizedString(
858                IDS_ASH_STATUS_TRAY_LOCALLY_MANAGED_LABEL) :
859            UTF8ToUTF16(delegate->GetUserEmail(multiprofile_index_));
860    if (!user_email_string.empty()) {
861      additional = new views::Label(user_email_string);
862      additional->SetFont(bundle.GetFont(ui::ResourceBundle::SmallFont));
863      additional->SetHorizontalAlignment(gfx::ALIGN_LEFT);
864    }
865  }
866
867  // Adjust text properties dependent on if it is an active or inactive user.
868  if (multiprofile_index_) {
869    // Fade the text of non active users to 50%.
870    SkColor text_color = additional->enabled_color();
871    text_color = SkColorSetA(text_color, SkColorGetA(text_color) / 2);
872    if (additional)
873      additional->SetDisabledColor(text_color);
874    if (username)
875      username->SetDisabledColor(text_color);
876  }
877
878  if (additional && username) {
879    views::View* details = new views::View;
880    details->SetLayoutManager(new views::BoxLayout(
881        views::BoxLayout::kVertical, 0, kUserDetailsVerticalPadding, 0));
882    details->AddChildView(username);
883    details->AddChildView(additional);
884    user_card_view_->AddChildView(details);
885  } else {
886    if (username)
887      user_card_view_->AddChildView(username);
888    if (additional)
889      user_card_view_->AddChildView(additional);
890  }
891}
892
893views::View* UserView::CreateIconForUserCard(ash::user::LoginStatus login) {
894  RoundedImageView* icon = new RoundedImageView(kProfileRoundedCornerRadius,
895                                                multiprofile_index_ == 0);
896  icon->SetEnabled(false);
897  if (login == ash::user::LOGGED_IN_GUEST) {
898    icon->SetImage(*ui::ResourceBundle::GetSharedInstance().
899        GetImageNamed(IDR_AURA_UBER_TRAY_GUEST_ICON).ToImageSkia(),
900        gfx::Size(kUserIconSize, kUserIconSize));
901  } else {
902    icon->SetImage(
903        ash::Shell::GetInstance()->session_state_delegate()->
904            GetUserImage(multiprofile_index_),
905        gfx::Size(kUserIconSize, kUserIconSize));
906  }
907  return icon;
908}
909
910void UserView::AddLoggedInRetailModeUserCardContent() {
911  views::Label* details = new views::Label;
912  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
913  details->SetText(
914      bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_KIOSK_LABEL));
915  details->set_border(views::Border::CreateEmptyBorder(0, 4, 0, 1));
916  details->SetHorizontalAlignment(gfx::ALIGN_LEFT);
917  user_card_view_->AddChildView(details);
918}
919
920void UserView::AddLoggedInPublicModeUserCardContent(SystemTrayItem* owner) {
921  user_card_view_->AddChildView(
922      CreateIconForUserCard(ash::user::LOGGED_IN_PUBLIC));
923  user_card_view_->AddChildView(new PublicAccountUserDetails(
924      owner, GetPreferredSize().width() + kTrayPopupPaddingBetweenItems));
925}
926
927void UserView::ToggleAddUserMenuOption() {
928  if (add_menu_option_.get()) {
929    popup_message_.reset();
930    mouse_watcher_.reset();
931    add_menu_option_.reset();
932    return;
933  }
934
935  // Note: We do not need to install a global event handler to delete this
936  // item since it will destroyed automatically before the menu / user menu item
937  // gets destroyed..
938  const SessionStateDelegate* session_state_delegate =
939      ash::Shell::GetInstance()->session_state_delegate();
940  add_user_visible_but_disabled_ =
941      session_state_delegate->NumberOfLoggedInUsers() >=
942          session_state_delegate->GetMaximumNumberOfLoggedInUsers();
943  add_menu_option_.reset(new views::Widget);
944  views::Widget::InitParams params;
945  params.type = views::Widget::InitParams::TYPE_TOOLTIP;
946  params.keep_on_top = true;
947  params.context = this->GetWidget()->GetNativeWindow();
948  params.accept_events = true;
949  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
950  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
951  add_menu_option_->Init(params);
952  add_menu_option_->SetOpacity(0xFF);
953  add_menu_option_->GetNativeWindow()->set_owned_by_parent(false);
954  SetShadowType(add_menu_option_->GetNativeView(),
955                views::corewm::SHADOW_TYPE_NONE);
956
957  // Position it below our user card.
958  gfx::Rect bounds = user_card_view_->GetBoundsInScreen();
959  bounds.set_y(bounds.y() + bounds.height());
960  add_menu_option_->SetBounds(bounds);
961
962  // Show the content.
963  AddUserView* add_user_view = new AddUserView(
964      static_cast<UserCard*>(user_card_view_), this);
965  add_menu_option_->SetContentsView(add_user_view);
966  add_menu_option_->SetAlwaysOnTop(true);
967  add_menu_option_->Show();
968  if (add_user_visible_but_disabled_) {
969    ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
970    popup_message_.reset(new PopupMessage(
971        bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_CAPTION_CANNOT_ADD_USER),
972        bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_MESSAGE_CANNOT_ADD_USER),
973        PopupMessage::ICON_WARNING,
974        add_user_view->anchor(),
975        views::BubbleBorder::TOP_LEFT,
976        gfx::Size(parent()->bounds().width() - kPopupMessageOffset, 0),
977        2 * kPopupMessageOffset));
978  }
979  // Find the screen area which encloses both elements and sets then a mouse
980  // watcher which will close the "menu".
981  gfx::Rect area = user_card_view_->GetBoundsInScreen();
982  area.set_height(2 * area.height());
983  mouse_watcher_.reset(new views::MouseWatcher(
984      new UserViewMouseWatcherHost(area),
985      this));
986  mouse_watcher_->Start();
987}
988
989AddUserView::AddUserView(UserCard* owner, views::ButtonListener* listener)
990    : CustomButton(listener_),
991      add_user_(NULL),
992      listener_(listener),
993      owner_(owner),
994      anchor_(NULL) {
995  AddContent();
996  owner_->ForceBorderVisible(true);
997}
998
999AddUserView::~AddUserView() {
1000  owner_->ForceBorderVisible(false);
1001}
1002
1003gfx::Size AddUserView::GetPreferredSize() {
1004  return owner_->bounds().size();
1005}
1006
1007int AddUserView::GetHeightForWidth(int width) {
1008  return owner_->bounds().size().height();
1009}
1010
1011void AddUserView::Layout() {
1012  gfx::Rect contents_area(GetContentsBounds());
1013  add_user_->SetBoundsRect(contents_area);
1014}
1015
1016void AddUserView::ButtonPressed(views::Button* sender, const ui::Event& event) {
1017  if (add_user_ == sender)
1018    listener_->ButtonPressed(this, event);
1019  else
1020    NOTREACHED();
1021}
1022
1023void AddUserView::AddContent() {
1024  set_notify_enter_exit_on_child(true);
1025
1026  const SessionStateDelegate* delegate =
1027      ash::Shell::GetInstance()->session_state_delegate();
1028  bool enable = delegate->NumberOfLoggedInUsers() <
1029                    delegate->GetMaximumNumberOfLoggedInUsers();
1030
1031  SetLayoutManager(new views::FillLayout());
1032  set_background(views::Background::CreateSolidBackground(kBackgroundColor));
1033
1034  // Add padding around the panel.
1035  set_border(views::Border::CreateSolidBorder(1, kBorderColor));
1036
1037  add_user_ = new UserCard(this, enable);
1038  add_user_->set_border(views::Border::CreateEmptyBorder(
1039      kUserCardVerticalPadding,
1040      kTrayPopupPaddingHorizontal- kTrayUserTileHoverBorderInset,
1041      kUserCardVerticalPadding,
1042      kTrayPopupPaddingHorizontal- kTrayUserTileHoverBorderInset));
1043
1044  add_user_->SetLayoutManager(new views::BoxLayout(
1045      views::BoxLayout::kHorizontal, 0, 0 , kTrayPopupPaddingBetweenItems));
1046  AddChildViewAt(add_user_, 0);
1047
1048  // Add the [+] icon which is also the anchor for messages.
1049  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
1050  RoundedImageView* icon = new RoundedImageView(kProfileRoundedCornerRadius,
1051                                                true);
1052  anchor_ = icon;
1053  icon->SetImage(*ui::ResourceBundle::GetSharedInstance().
1054      GetImageNamed(IDR_AURA_UBER_TRAY_ADD_MULTIPROFILE_USER).ToImageSkia(),
1055      gfx::Size(kUserIconSize, kUserIconSize));
1056  add_user_->AddChildView(icon);
1057
1058  // Add the command text.
1059  views::Label* command_label = new views::Label(
1060      bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_SIGN_IN_ANOTHER_ACCOUNT));
1061  command_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
1062  add_user_->AddChildView(command_label);
1063}
1064
1065}  // namespace tray
1066
1067TrayUser::TrayUser(SystemTray* system_tray, MultiProfileIndex index)
1068    : SystemTrayItem(system_tray),
1069      multiprofile_index_(index),
1070      user_(NULL),
1071      layout_view_(NULL),
1072      avatar_(NULL),
1073      label_(NULL),
1074      separator_shown_(false) {
1075  Shell::GetInstance()->system_tray_notifier()->AddUserObserver(this);
1076}
1077
1078TrayUser::~TrayUser() {
1079  Shell::GetInstance()->system_tray_notifier()->RemoveUserObserver(this);
1080}
1081
1082TrayUser::TestState TrayUser::GetStateForTest() const {
1083  if (separator_shown_)
1084    return SEPARATOR;
1085  if (!user_)
1086    return HIDDEN;
1087  return user_->GetStateForTest();
1088}
1089
1090gfx::Rect TrayUser::GetUserPanelBoundsInScreenForTest() const {
1091  DCHECK(user_);
1092  return user_->GetBoundsInScreenOfUserButtonForTest();
1093}
1094
1095views::View* TrayUser::CreateTrayView(user::LoginStatus status) {
1096  CHECK(layout_view_ == NULL);
1097  // Only the current user gets an icon. All other users will only be
1098  // represented in the tray menu.
1099  if (multiprofile_index_)
1100    return NULL;
1101
1102  layout_view_ = new views::View();
1103  layout_view_->SetLayoutManager(
1104      new views::BoxLayout(views::BoxLayout::kHorizontal,
1105                           0, 0, kUserLabelToIconPadding));
1106  UpdateAfterLoginStatusChange(status);
1107  return layout_view_;
1108}
1109
1110views::View* TrayUser::CreateDefaultView(user::LoginStatus status) {
1111  if (status == user::LOGGED_IN_NONE)
1112    return NULL;
1113
1114  CHECK(user_ == NULL);
1115
1116  const SessionStateDelegate* session_state_delegate =
1117      ash::Shell::GetInstance()->session_state_delegate();
1118  int logged_in_users = session_state_delegate->NumberOfLoggedInUsers();
1119
1120  // If there are multiple users logged in, the users will be separated from the
1121  // rest of the menu by a separator.
1122  if (multiprofile_index_ ==
1123          session_state_delegate->GetMaximumNumberOfLoggedInUsers() &&
1124      logged_in_users > 1) {
1125    separator_shown_ = true;
1126    return new views::View();
1127  }
1128
1129  // Do not show more UserView's then there are logged in users.
1130  if (multiprofile_index_ >= logged_in_users)
1131    return NULL;
1132
1133  user_ = new tray::UserView(this, status, multiprofile_index_);
1134  return user_;
1135}
1136
1137views::View* TrayUser::CreateDetailedView(user::LoginStatus status) {
1138  return NULL;
1139}
1140
1141void TrayUser::DestroyTrayView() {
1142  layout_view_ = NULL;
1143  avatar_ = NULL;
1144  label_ = NULL;
1145  separator_shown_ = false;
1146}
1147
1148void TrayUser::DestroyDefaultView() {
1149  user_ = NULL;
1150}
1151
1152void TrayUser::DestroyDetailedView() {
1153}
1154
1155void TrayUser::UpdateAfterLoginStatusChange(user::LoginStatus status) {
1156  // Only the active user is represented in the tray.
1157  if (!layout_view_)
1158    return;
1159  bool need_label = false;
1160  bool need_avatar = false;
1161  switch (status) {
1162    case user::LOGGED_IN_LOCKED:
1163    case user::LOGGED_IN_USER:
1164    case user::LOGGED_IN_OWNER:
1165    case user::LOGGED_IN_PUBLIC:
1166      need_avatar = true;
1167      break;
1168    case user::LOGGED_IN_LOCALLY_MANAGED:
1169      need_avatar = true;
1170      need_label = true;
1171      break;
1172    case user::LOGGED_IN_GUEST:
1173      need_avatar = true;
1174      break;
1175    case user::LOGGED_IN_RETAIL_MODE:
1176    case user::LOGGED_IN_KIOSK_APP:
1177    case user::LOGGED_IN_NONE:
1178      break;
1179  }
1180
1181  if ((need_avatar != (avatar_ != NULL)) ||
1182      (need_label != (label_ != NULL))) {
1183    layout_view_->RemoveAllChildViews(true);
1184    if (need_label) {
1185      label_ = new views::Label;
1186      SetupLabelForTray(label_);
1187      layout_view_->AddChildView(label_);
1188    } else {
1189      label_ = NULL;
1190    }
1191    if (need_avatar) {
1192      avatar_ = new tray::RoundedImageView(kProfileRoundedCornerRadius, true);
1193      layout_view_->AddChildView(avatar_);
1194    } else {
1195      avatar_ = NULL;
1196    }
1197  }
1198
1199  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
1200  if (status == user::LOGGED_IN_LOCALLY_MANAGED) {
1201    label_->SetText(
1202        bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOCALLY_MANAGED_LABEL));
1203  }
1204
1205  if (avatar_ && ash::switches::UseAlternateShelfLayout()) {
1206    avatar_->SetCornerRadii(0,
1207                            kUserIconLargeCornerRadius,
1208                            kUserIconLargeCornerRadius,
1209                            0);
1210    avatar_->set_border(NULL);
1211  }
1212  UpdateAvatarImage(status);
1213}
1214
1215void TrayUser::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
1216  // Inactive users won't have a layout.
1217  if (!layout_view_)
1218    return;
1219  if (alignment == SHELF_ALIGNMENT_BOTTOM ||
1220      alignment == SHELF_ALIGNMENT_TOP) {
1221    if (avatar_) {
1222      if (ash::switches::UseAlternateShelfLayout()) {
1223        avatar_->set_border(NULL);
1224        avatar_->SetCornerRadii(0,
1225                                kUserIconLargeCornerRadius,
1226                                kUserIconLargeCornerRadius,
1227                                0);
1228      } else {
1229        avatar_->set_border(views::Border::CreateEmptyBorder(
1230            0, kTrayImageItemHorizontalPaddingBottomAlignment + 2,
1231            0, kTrayImageItemHorizontalPaddingBottomAlignment));
1232      }
1233    }
1234    if (label_) {
1235      label_->set_border(views::Border::CreateEmptyBorder(
1236          0, kTrayLabelItemHorizontalPaddingBottomAlignment,
1237          0, kTrayLabelItemHorizontalPaddingBottomAlignment));
1238    }
1239    layout_view_->SetLayoutManager(
1240        new views::BoxLayout(views::BoxLayout::kHorizontal,
1241                             0, 0, kUserLabelToIconPadding));
1242  } else {
1243    if (avatar_) {
1244      if (ash::switches::UseAlternateShelfLayout()) {
1245        avatar_->set_border(NULL);
1246        avatar_->SetCornerRadii(0,
1247                                0,
1248                                kUserIconLargeCornerRadius,
1249                                kUserIconLargeCornerRadius);
1250      } else {
1251        SetTrayImageItemBorder(avatar_, alignment);
1252      }
1253    }
1254    if (label_) {
1255      label_->set_border(views::Border::CreateEmptyBorder(
1256          kTrayLabelItemVerticalPaddingVeriticalAlignment,
1257          kTrayLabelItemHorizontalPaddingBottomAlignment,
1258          kTrayLabelItemVerticalPaddingVeriticalAlignment,
1259          kTrayLabelItemHorizontalPaddingBottomAlignment));
1260    }
1261    layout_view_->SetLayoutManager(
1262        new views::BoxLayout(views::BoxLayout::kVertical,
1263                             0, 0, kUserLabelToIconPadding));
1264  }
1265}
1266
1267void TrayUser::OnUserUpdate() {
1268  UpdateAvatarImage(Shell::GetInstance()->system_tray_delegate()->
1269      GetUserLoginStatus());
1270}
1271
1272void TrayUser::UpdateAvatarImage(user::LoginStatus status) {
1273  if (!avatar_)
1274    return;
1275
1276  int icon_size = ash::switches::UseAlternateShelfLayout() ?
1277      kUserIconLargeSize : kUserIconSize;
1278
1279  if (status == user::LOGGED_IN_GUEST) {
1280    int image_name = ash::switches::UseAlternateShelfLayout() ?
1281        IDR_AURA_UBER_TRAY_GUEST_ICON_LARGE :
1282        IDR_AURA_UBER_TRAY_GUEST_ICON;
1283    avatar_->SetImage(*ui::ResourceBundle::GetSharedInstance().
1284        GetImageNamed(image_name).ToImageSkia(),
1285        gfx::Size(icon_size, icon_size));
1286  } else {
1287    avatar_->SetImage(
1288        ash::Shell::GetInstance()->session_state_delegate()->GetUserImage(
1289            multiprofile_index_),
1290        gfx::Size(icon_size, icon_size));
1291  }
1292}
1293
1294}  // namespace internal
1295}  // namespace ash
1296