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