text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "chrome/browser/ui/views/avatar_menu_bubble_view.h"
  
  #include <algorithm>
  
  #include "base/strings/string16.h"
  #include "base/strings/utf_string_conversions.h"
  #include "chrome/app/chrome_command_ids.h"
  #include "chrome/browser/browser_process.h"
  #include "chrome/browser/profiles/avatar_menu.h"
  #include "chrome/browser/profiles/profile_info_cache.h"
  #include "chrome/browser/profiles/profile_info_util.h"
  #include "chrome/browser/profiles/profile_manager.h"
  #include "chrome/browser/profiles/profile_window.h"
  #include "chrome/browser/signin/signin_manager_factory.h"
  #include "chrome/browser/ui/browser.h"
  #include "chrome/browser/ui/browser_commands.h"
  #include "chrome/browser/ui/browser_list.h"
  #include "chrome/browser/ui/browser_window.h"
  #include "chrome/browser/ui/chrome_pages.h"
  #include "chrome/common/profile_management_switches.h"
  #include "chrome/common/url_constants.h"
  #include "components/signin/core/browser/signin_manager.h"
  #include "content/public/browser/page_navigator.h"
  #include "content/public/browser/web_contents.h"
  #include "grit/generated_resources.h"
  #include "grit/theme_resources.h"
  #include "ui/base/l10n/l10n_util.h"
  #include "ui/base/resource/resource_bundle.h"
  #include "ui/gfx/canvas.h"
  #include "ui/gfx/image/canvas_image_source.h"
  #include "ui/gfx/image/image.h"
  #include "ui/views/controls/button/custom_button.h"
  #include "ui/views/controls/button/image_button.h"
  #include "ui/views/controls/button/label_button.h"
  #include "ui/views/controls/image_view.h"
  #include "ui/views/controls/label.h"
  #include "ui/views/controls/link.h"
  #include "ui/views/controls/separator.h"
  #include "ui/views/layout/grid_layout.h"
  #include "ui/views/layout/layout_constants.h"
  #include "ui/views/widget/widget.h"
  
  namespace {
  
  const int kItemHeight = 44;
  const int kItemMarginY = 4;
  const int kIconMarginX = 6;
  const int kSeparatorPaddingY = 5;
  const int kMaxItemTextWidth = 200;
  const SkColor kHighlightColor = 0xFFE3EDF6;
  
  inline int Round(double x) {
    return static_cast<int>(x + 0.5);
  }
  
  gfx::Rect GetCenteredAndScaledRect(int src_width, int src_height,
                                     int dst_x, int dst_y,
                                     int dst_width, int dst_height) {
    int scaled_width;
    int scaled_height;
    if (src_width > src_height) {
      scaled_width = std::min(src_width, dst_width);
      float scale = static_cast<float>(scaled_width) /
                    static_cast<float>(src_width);
      scaled_height = Round(src_height * scale);
    } else {
      scaled_height = std::min(src_height, dst_height);
      float scale = static_cast<float>(scaled_height) /
                    static_cast<float>(src_height);
      scaled_width = Round(src_width * scale);
    }
    int x = dst_x + (dst_width - scaled_width) / 2;
    int y = dst_y + (dst_height - scaled_height) / 2;
    return gfx::Rect(x, y, scaled_width, scaled_height);
  }
  
  // BadgeImageSource -----------------------------------------------------------
  class BadgeImageSource: public gfx::CanvasImageSource {
   public:
    BadgeImageSource(const gfx::ImageSkia& icon,
                     const gfx::Size& icon_size,
                     const gfx::ImageSkia& badge);
  
    virtual ~BadgeImageSource();
  
    // Overridden from CanvasImageSource:
    virtual void Draw(gfx::Canvas* canvas) OVERRIDE;
  
   private:
    gfx::Size ComputeSize(const gfx::ImageSkia& icon,
                          const gfx::Size& size,
                          const gfx::ImageSkia& badge);
  
    const gfx::ImageSkia icon_;
    gfx::Size icon_size_;
    const gfx::ImageSkia badge_;
  
    DISALLOW_COPY_AND_ASSIGN(BadgeImageSource);
  };
  
  BadgeImageSource::BadgeImageSource(const gfx::ImageSkia& icon,
                                     const gfx::Size& icon_size,
                                     const gfx::ImageSkia& badge)
      : gfx::CanvasImageSource(ComputeSize(icon, icon_size, badge), false),
        icon_(icon),
        icon_size_(icon_size),
        badge_(badge) {
  }
  
  BadgeImageSource::~BadgeImageSource() {
  }
  
  void BadgeImageSource::Draw(gfx::Canvas* canvas) {
    canvas->DrawImageInt(icon_, 0, 0, icon_.width(), icon_.height(), 0, 0,
                         icon_size_.width(), icon_size_.height(), true);
    canvas->DrawImageInt(badge_, size().width() - badge_.width(),
                         size().height() - badge_.height());
  }
  
  gfx::Size BadgeImageSource::ComputeSize(const gfx::ImageSkia& icon,
                                          const gfx::Size& icon_size,
                                          const gfx::ImageSkia& badge) {
    const float kBadgeOverlapRatioX = 1.0f / 5.0f;
    int width = icon_size.width() + badge.width() * kBadgeOverlapRatioX;
    const float kBadgeOverlapRatioY = 1.0f / 3.0f;
    int height = icon_size.height() + badge.height() * kBadgeOverlapRatioY;
    return gfx::Size(width, height);
  }
  
  // HighlightDelegate ----------------------------------------------------------
  
  // Delegate to callback when the highlight state of a control changes.
  class HighlightDelegate {
   public:
    virtual ~HighlightDelegate() {}
    virtual void OnHighlightStateChanged() = 0;
    virtual void OnFocusStateChanged(bool has_focus) = 0;
  };
  
  
  // EditProfileLink ------------------------------------------------------------
  
  // A custom Link control that forwards highlight state changes. We need to do
  // this to make sure that the ProfileItemView looks highlighted even when
  // the mouse is over this link.
  class EditProfileLink : public views::Link {
   public:
    explicit EditProfileLink(const base::string16& title,
                             HighlightDelegate* delegate);
  
    virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE;
    virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE;
    virtual void OnFocus() OVERRIDE;
    virtual void OnBlur() OVERRIDE;
  
    views::CustomButton::ButtonState state() { return state_; }
  
   private:
    HighlightDelegate* delegate_;
    views::CustomButton::ButtonState state_;
  };
  
  EditProfileLink::EditProfileLink(const base::string16& title,
                                   HighlightDelegate* delegate)
      : views::Link(title),
        delegate_(delegate),
        state_(views::CustomButton::STATE_NORMAL) {
  }
  
  void EditProfileLink::OnMouseEntered(const ui::MouseEvent& event) {
    views::Link::OnMouseEntered(event);
    state_ = views::CustomButton::STATE_HOVERED;
    delegate_->OnHighlightStateChanged();
  }
  
  void EditProfileLink::OnMouseExited(const ui::MouseEvent& event) {
    views::Link::OnMouseExited(event);
    state_ = views::CustomButton::STATE_NORMAL;
    delegate_->OnHighlightStateChanged();
  }
  
  void EditProfileLink::OnFocus() {
    views::Link::OnFocus();
    delegate_->OnFocusStateChanged(true);
  }
  
  void EditProfileLink::OnBlur() {
    views::Link::OnBlur();
    state_ = views::CustomButton::STATE_NORMAL;
    delegate_->OnFocusStateChanged(false);
  }
  
  
  // ProfileImageView -----------------------------------------------------------
  
  // A custom image view that ignores mouse events so that the parent can receive
  // them instead.
  class ProfileImageView : public views::ImageView {
   public:
    virtual bool HitTestRect(const gfx::Rect& rect) const OVERRIDE;
  };
  
  bool ProfileImageView::HitTestRect(const gfx::Rect& rect) const {
    return false;
  }
  
  }  // namespace
  
  // ProfileItemView ------------------------------------------------------------
  
  // Control that shows information about a single profile.
  class ProfileItemView : public views::CustomButton,
                          public HighlightDelegate {
   public:
    ProfileItemView(const AvatarMenu::Item& item,
                    AvatarMenuBubbleView* parent,
                    AvatarMenu* menu);
  
    virtual gfx::Size GetPreferredSize() OVERRIDE;
    virtual void Layout() OVERRIDE;
    virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE;
    virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE;
    virtual void OnFocus() OVERRIDE;
    virtual void OnBlur() OVERRIDE;
  
    virtual void OnHighlightStateChanged() OVERRIDE;
    virtual void OnFocusStateChanged(bool has_focus) OVERRIDE;
  
    const AvatarMenu::Item& item() const { return item_; }
    EditProfileLink* edit_link() { return edit_link_; }
  
   private:
    gfx::ImageSkia GetBadgedIcon(const gfx::ImageSkia& icon);
  
    bool IsHighlighted();
  
    AvatarMenu::Item item_;
    AvatarMenuBubbleView* parent_;
    AvatarMenu* menu_;
    views::ImageView* image_view_;
    views::Label* name_label_;
    views::Label* sync_state_label_;
    EditProfileLink* edit_link_;
  
    DISALLOW_COPY_AND_ASSIGN(ProfileItemView);
  };
  
  ProfileItemView::ProfileItemView(const AvatarMenu::Item& item,
                                   AvatarMenuBubbleView* parent,
                                   AvatarMenu* menu)
      : views::CustomButton(parent),
        item_(item),
        parent_(parent),
        menu_(menu) {
    set_notify_enter_exit_on_child(true);
  
    image_view_ = new ProfileImageView();
    gfx::ImageSkia profile_icon = *item_.icon.ToImageSkia();
    if (item_.active || item_.signin_required)
      image_view_->SetImage(GetBadgedIcon(profile_icon));
    else
      image_view_->SetImage(profile_icon);
    AddChildView(image_view_);
  
    // Add a label to show the profile name.
    ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
    name_label_ = new views::Label(item_.name,
                                   rb->GetFontList(item_.active ?
                                                   ui::ResourceBundle::BoldFont :
                                                   ui::ResourceBundle::BaseFont));
    name_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    AddChildView(name_label_);
  
    // Add a label to show the sync state.
    sync_state_label_ = new views::Label(item_.sync_state);
    if (item_.signed_in)
      sync_state_label_->SetElideBehavior(views::Label::ELIDE_AS_EMAIL);
    sync_state_label_->SetFontList(
        rb->GetFontList(ui::ResourceBundle::SmallFont));
    sync_state_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    sync_state_label_->SetEnabled(false);
    AddChildView(sync_state_label_);
  
    // Add an edit profile link.
    edit_link_ = new EditProfileLink(
        l10n_util::GetStringUTF16(IDS_PROFILES_EDIT_PROFILE_LINK), this);
    edit_link_->set_listener(parent);
    edit_link_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    AddChildView(edit_link_);
  
    OnHighlightStateChanged();
  }
  
  gfx::Size ProfileItemView::GetPreferredSize() {
    int text_width = std::max(name_label_->GetPreferredSize().width(),
                              sync_state_label_->GetPreferredSize().width());
    text_width = std::max(edit_link_->GetPreferredSize().width(), text_width);
    text_width = std::min(kMaxItemTextWidth, text_width);
    return gfx::Size(profiles::kAvatarIconWidth + kIconMarginX + text_width,
                     kItemHeight);
  }
  
  void ProfileItemView::Layout() {
    // Profile icon.
    gfx::Rect icon_rect;
    if (item_.active) {
      // If this is the active item then the icon is already scaled and so
      // just use the preferred size.
      icon_rect.set_size(image_view_->GetPreferredSize());
      icon_rect.set_y((height() - icon_rect.height()) / 2);
    } else {
      const gfx::ImageSkia& icon = image_view_->GetImage();
      icon_rect = GetCenteredAndScaledRect(icon.width(), icon.height(), 0, 0,
          profiles::kAvatarIconWidth, height());
    }
    image_view_->SetBoundsRect(icon_rect);
  
    int label_x = profiles::kAvatarIconWidth + kIconMarginX;
    int max_label_width = std::max(width() - label_x, 0);
    gfx::Size name_size = name_label_->GetPreferredSize();
    name_size.set_width(std::min(name_size.width(), max_label_width));
    gfx::Size state_size = sync_state_label_->GetPreferredSize();
    state_size.set_width(std::min(state_size.width(), max_label_width));
    gfx::Size edit_size = edit_link_->GetPreferredSize();
    edit_size.set_width(std::min(edit_size.width(), max_label_width));
  
    const int kNameStatePaddingY = 2;
    int labels_height = name_size.height() + kNameStatePaddingY +
        std::max(state_size.height(), edit_size.height());
    int y = (height() - labels_height) / 2;
    name_label_->SetBounds(label_x, y, name_size.width(), name_size.height());
  
    int bottom = y + labels_height;
    sync_state_label_->SetBounds(label_x, bottom - state_size.height(),
                                 state_size.width(), state_size.height());
    // The edit link overlaps the sync state label.
    edit_link_->SetBounds(label_x, bottom - edit_size.height(),
                          edit_size.width(), edit_size.height());
  }
  
  void ProfileItemView::OnMouseEntered(const ui::MouseEvent& event) {
    views::CustomButton::OnMouseEntered(event);
    OnHighlightStateChanged();
  }
  
  void ProfileItemView::OnMouseExited(const ui::MouseEvent& event) {
    views::CustomButton::OnMouseExited(event);
    OnHighlightStateChanged();
  }
  
  void ProfileItemView::OnFocus() {
    views::CustomButton::OnFocus();
    OnFocusStateChanged(true);
  }
  
  void ProfileItemView::OnBlur() {
    views::CustomButton::OnBlur();
    OnFocusStateChanged(false);
  }
  
  void ProfileItemView::OnHighlightStateChanged() {
    const SkColor color = IsHighlighted() ? kHighlightColor : parent_->color();
    set_background(views::Background::CreateSolidBackground(color));
    name_label_->SetBackgroundColor(color);
    sync_state_label_->SetBackgroundColor(color);
    edit_link_->SetBackgroundColor(color);
  
    bool show_edit = IsHighlighted() && item_.active &&
        menu_->ShouldShowEditProfileLink();
    sync_state_label_->SetVisible(!show_edit);
    edit_link_->SetVisible(show_edit);
    SchedulePaint();
  }
  
  void ProfileItemView::OnFocusStateChanged(bool has_focus) {
    if (!has_focus && state() != views::CustomButton::STATE_DISABLED)
      SetState(views::CustomButton::STATE_NORMAL);
    OnHighlightStateChanged();
  }
  
  // static
  gfx::ImageSkia ProfileItemView::GetBadgedIcon(const gfx::ImageSkia& icon) {
    ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
    const gfx::ImageSkia* badge = NULL;
  
    if (item_.active)
      badge = rb->GetImageSkiaNamed(IDR_PROFILE_SELECTED);
    else if (item_.signin_required)  // TODO(bcwhite): create new icon
      badge = rb->GetImageSkiaNamed(IDR_OMNIBOX_HTTPS_VALID);
    else
      NOTREACHED();  // function should only be called if one of above is true
  
    gfx::Size icon_size = GetCenteredAndScaledRect(icon.width(), icon.height(),
        0, 0, profiles::kAvatarIconWidth, kItemHeight).size();
    gfx::CanvasImageSource* source =
        new BadgeImageSource(icon, icon_size, *badge);
    // ImageSkia takes ownership of |source|.
    return gfx::ImageSkia(source, source->size());
  }
  
  bool ProfileItemView::IsHighlighted() {
    return state() == views::CustomButton::STATE_PRESSED ||
           state() == views::CustomButton::STATE_HOVERED ||
           edit_link_->state() == views::CustomButton::STATE_PRESSED ||
           edit_link_->state() == views::CustomButton::STATE_HOVERED ||
           HasFocus() ||
           edit_link_->HasFocus();
  }
  
  
  // ActionButtonView -----------------------------------------------------------
  
  // A custom view that manages the "action" buttons at the bottom of the list
  // of profiles.
  class ActionButtonView : public views::View {
   public:
    ActionButtonView(views::ButtonListener* listener, Profile* profile);
  
   private:
    views::LabelButton* manage_button_;
    views::LabelButton* signout_button_;
  
    DISALLOW_COPY_AND_ASSIGN(ActionButtonView);
  };
  
  
  ActionButtonView::ActionButtonView(views::ButtonListener* listener,
                                     Profile* profile)
    : manage_button_(NULL),
      signout_button_(NULL) {
    std::string username;
    SigninManagerBase* signin =
        SigninManagerFactory::GetForProfile(profile);
    if (signin != NULL)
      username = signin->GetAuthenticatedUsername();
  
    manage_button_ = new views::LabelButton(
        listener, l10n_util::GetStringUTF16(IDS_PROFILES_MANAGE_PROFILES_BUTTON));
    manage_button_->SetStyle(views::Button::STYLE_BUTTON);
    manage_button_->SetTooltipText(
        l10n_util::GetStringUTF16(IDS_PROFILES_MANAGE_PROFILES_BUTTON_TIP));
    manage_button_->set_tag(IDS_PROFILES_MANAGE_PROFILES_BUTTON);
  
    signout_button_ = new views::LabelButton(
        listener, l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_SIGNOUT_BUTTON));
    signout_button_->SetStyle(views::Button::STYLE_BUTTON);
    if (username.empty()) {
      signout_button_->SetTooltipText(
          l10n_util::GetStringUTF16(
              IDS_PROFILES_PROFILE_SIGNOUT_BUTTON_TIP_UNAVAILABLE));
      signout_button_->SetEnabled(false);
    } else {
      signout_button_->SetTooltipText(
          l10n_util::GetStringFUTF16(IDS_PROFILES_PROFILE_SIGNOUT_BUTTON_TIP,
                                     base::UTF8ToUTF16(username)));
    }
    signout_button_->set_tag(IDS_PROFILES_PROFILE_SIGNOUT_BUTTON);
  
    views::GridLayout* layout = new views::GridLayout(this);
    views::ColumnSet* columns = layout->AddColumnSet(0);
    columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1,
                       views::GridLayout::USE_PREF, 0, 0);
    columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL, 1,
                       views::GridLayout::USE_PREF, 0, 0);
    layout->StartRow(0, 0);
    layout->AddView(signout_button_);
    layout->AddView(manage_button_);
    SetLayoutManager(layout);
  }
  
  
  // AvatarMenuBubbleView -------------------------------------------------------
  
  // static
  AvatarMenuBubbleView* AvatarMenuBubbleView::avatar_bubble_ = NULL;
  bool AvatarMenuBubbleView::close_on_deactivate_for_testing_ = true;
  
  // static
  void AvatarMenuBubbleView::ShowBubble(
      views::View* anchor_view,
      views::BubbleBorder::Arrow arrow,
      views::BubbleBorder::BubbleAlignment border_alignment,
      const gfx::Rect& anchor_rect,
      Browser* browser) {
    if (IsShowing())
      return;
  
    DCHECK(chrome::IsCommandEnabled(browser, IDC_SHOW_AVATAR_MENU));
    avatar_bubble_ = new AvatarMenuBubbleView(
        anchor_view, arrow, anchor_rect, browser);
    views::BubbleDelegateView::CreateBubble(avatar_bubble_);
    avatar_bubble_->set_close_on_deactivate(close_on_deactivate_for_testing_);
    avatar_bubble_->SetBackgroundColors();
    avatar_bubble_->SetAlignment(border_alignment);
    avatar_bubble_->GetWidget()->Show();
  }
  
  // static
  bool AvatarMenuBubbleView::IsShowing() {
    return avatar_bubble_ != NULL;
  }
  
  // static
  void AvatarMenuBubbleView::Hide() {
    if (IsShowing())
      avatar_bubble_->GetWidget()->Close();
  }
  
  AvatarMenuBubbleView::AvatarMenuBubbleView(
      views::View* anchor_view,
      views::BubbleBorder::Arrow arrow,
      const gfx::Rect& anchor_rect,
      Browser* browser)
      : BubbleDelegateView(anchor_view, arrow),
        anchor_rect_(anchor_rect),
        browser_(browser),
        separator_(NULL),
        buttons_view_(NULL),
        managed_user_info_(NULL),
        separator_switch_users_(NULL),
        expanded_(false) {
    avatar_menu_.reset(new AvatarMenu(
        &g_browser_process->profile_manager()->GetProfileInfoCache(),
        this,
        browser_));
    avatar_menu_->RebuildMenu();
  }
  
  AvatarMenuBubbleView::~AvatarMenuBubbleView() {
  }
  
  gfx::Size AvatarMenuBubbleView::GetPreferredSize() {
    const int kBubbleViewMinWidth = 175;
    gfx::Size preferred_size(kBubbleViewMinWidth, 0);
    for (size_t i = 0; i < item_views_.size(); ++i) {
      gfx::Size size = item_views_[i]->GetPreferredSize();
      preferred_size.Enlarge(0, size.height() + kItemMarginY);
      preferred_size.SetToMax(size);
    }
  
    if (buttons_view_) {
      preferred_size.Enlarge(
          0, kSeparatorPaddingY * 2 + separator_->GetPreferredSize().height());
  
      gfx::Size buttons_size = buttons_view_->GetPreferredSize();
      preferred_size.Enlarge(0, buttons_size.height());
      preferred_size.SetToMax(buttons_size);
    }
  
  
    if (managed_user_info_) {
      // First handle the switch profile link because it can still affect the
      // preferred width.
      gfx::Size size = switch_profile_link_->GetPreferredSize();
      preferred_size.Enlarge(0, size.height());
      preferred_size.SetToMax(size);
  
      // Add the height of the two separators.
      preferred_size.Enlarge(
          0,
          kSeparatorPaddingY * 4 + separator_->GetPreferredSize().height() * 2);
    }
  
    const int kBubbleViewMaxWidth = 800;
    preferred_size.SetToMin(
        gfx::Size(kBubbleViewMaxWidth, preferred_size.height()));
  
    // We have to do this after the final width is calculated, since the label
    // will wrap based on the width.
    if (managed_user_info_) {
      int remaining_width =
          preferred_size.width() - icon_view_->GetPreferredSize().width() -
          views::kRelatedControlSmallHorizontalSpacing;
      preferred_size.Enlarge(
          0,
          managed_user_info_->GetHeightForWidth(remaining_width) + kItemMarginY);
    }
  
    return preferred_size;
  }
  
  void AvatarMenuBubbleView::Layout() {
    int y = 0;
    for (size_t i = 0; i < item_views_.size(); ++i) {
      views::CustomButton* item_view = item_views_[i];
      int item_height = item_view->GetPreferredSize().height();
      int item_width = width();
      item_view->SetBounds(0, y, item_width, item_height);
      y += item_height + kItemMarginY;
    }
  
    int separator_height;
    if (buttons_view_ || managed_user_info_) {
      separator_height = separator_->GetPreferredSize().height();
      y += kSeparatorPaddingY;
      separator_->SetBounds(0, y, width(), separator_height);
      y += kSeparatorPaddingY + separator_height;
    }
  
    if (buttons_view_) {
      buttons_view_->SetBounds(0, y,
          width(), buttons_view_->GetPreferredSize().height());
    } else if (managed_user_info_) {
      gfx::Size icon_size = icon_view_->GetPreferredSize();
      gfx::Rect icon_bounds(0, y, icon_size.width(), icon_size.height());
      icon_view_->SetBoundsRect(icon_bounds);
      int info_width = width() - icon_bounds.right() -
                       views::kRelatedControlSmallHorizontalSpacing;
      int height = managed_user_info_->GetHeightForWidth(info_width);
      managed_user_info_->SetBounds(
          icon_bounds.right() + views::kRelatedControlSmallHorizontalSpacing,
          y, info_width, height);
      y += height + kItemMarginY + kSeparatorPaddingY;
      separator_switch_users_->SetBounds(0, y, width(), separator_height);
      y += separator_height + kSeparatorPaddingY;
      int link_height = switch_profile_link_->GetPreferredSize().height();
      switch_profile_link_->SetBounds(0, y, width(), link_height);
    }
  }
  
  bool AvatarMenuBubbleView::AcceleratorPressed(
      const ui::Accelerator& accelerator) {
    if (accelerator.key_code() != ui::VKEY_DOWN &&
        accelerator.key_code() != ui::VKEY_UP)
      return BubbleDelegateView::AcceleratorPressed(accelerator);
  
    if (item_views_.empty())
      return true;
  
    // Find the currently focused item. Note that if there is no focused item, the
    // code below correctly handles a |focus_index| of -1.
    int focus_index = -1;
    for (size_t i = 0; i < item_views_.size(); ++i) {
      if (item_views_[i]->HasFocus()) {
        focus_index = i;
        break;
      }
    }
  
    // Moved the focus up or down by 1.
    if (accelerator.key_code() == ui::VKEY_DOWN)
      focus_index = (focus_index + 1) % item_views_.size();
    else
      focus_index = ((focus_index > 0) ? focus_index : item_views_.size()) - 1;
    item_views_[focus_index]->RequestFocus();
  
    return true;
  }
  
  void AvatarMenuBubbleView::ButtonPressed(views::Button* sender,
                                           const ui::Event& event) {
    if (sender->tag() == IDS_PROFILES_MANAGE_PROFILES_BUTTON) {
      std::string subpage = chrome::kSearchUsersSubPage;
      chrome::ShowSettingsSubPage(browser_, subpage);
      return;
    } else if (sender->tag() == IDS_PROFILES_PROFILE_SIGNOUT_BUTTON) {
      profiles::LockProfile(browser_->profile());
      return;
    }
  
    for (size_t i = 0; i < item_views_.size(); ++i) {
      ProfileItemView* item_view = item_views_[i];
      if (sender == item_view) {
        // Clicking on the active profile shouldn't do anything.
        if (!item_view->item().active) {
          avatar_menu_->SwitchToProfile(
              i, ui::DispositionFromEventFlags(event.flags()) == NEW_WINDOW,
              ProfileMetrics::SWITCH_PROFILE_ICON);
        }
        break;
      }
    }
  }
  
  void AvatarMenuBubbleView::LinkClicked(views::Link* source, int event_flags) {
    if (source == buttons_view_) {
      avatar_menu_->AddNewProfile(ProfileMetrics::ADD_NEW_USER_ICON);
      return;
    }
    if (source == switch_profile_link_) {
      expanded_ = true;
      OnAvatarMenuChanged(avatar_menu_.get());
      return;
    }
  
    for (size_t i = 0; i < item_views_.size(); ++i) {
      ProfileItemView* item_view = item_views_[i];
      if (source == item_view->edit_link()) {
        avatar_menu_->EditProfile(i);
        return;
      }
    }
  }
  
  gfx::Rect AvatarMenuBubbleView::GetAnchorRect() {
    return anchor_rect_;
  }
  
  void AvatarMenuBubbleView::Init() {
    // Build the menu for the first time.
    OnAvatarMenuChanged(avatar_menu_.get());
    AddAccelerator(ui::Accelerator(ui::VKEY_DOWN, ui::EF_NONE));
    AddAccelerator(ui::Accelerator(ui::VKEY_UP, ui::EF_NONE));
  }
  
  void AvatarMenuBubbleView::WindowClosing() {
    DCHECK_EQ(avatar_bubble_, this);
    avatar_bubble_ = NULL;
  }
  
  void AvatarMenuBubbleView::InitMenuContents(
      AvatarMenu* avatar_menu) {
    for (size_t i = 0; i < avatar_menu->GetNumberOfItems(); ++i) {
      const AvatarMenu::Item& item = avatar_menu->GetItemAt(i);
      ProfileItemView* item_view = new ProfileItemView(item,
                                                       this,
                                                       avatar_menu_.get());
      item_view->SetAccessibleName(l10n_util::GetStringFUTF16(
          IDS_PROFILES_SWITCH_TO_PROFILE_ACCESSIBLE_NAME, item.name));
      item_view->SetFocusable(true);
      AddChildView(item_view);
      item_views_.push_back(item_view);
    }
  
    if (switches::IsNewProfileManagement()) {
      separator_ = new views::Separator(views::Separator::HORIZONTAL);
      AddChildView(separator_);
      buttons_view_ = new ActionButtonView(this, browser_->profile());
      AddChildView(buttons_view_);
    } else if (avatar_menu_->ShouldShowAddNewProfileLink()) {
      views::Link* add_profile_link = new views::Link(
          l10n_util::GetStringUTF16(IDS_PROFILES_CREATE_NEW_PROFILE_LINK));
      add_profile_link->set_listener(this);
      add_profile_link->SetHorizontalAlignment(gfx::ALIGN_CENTER);
      add_profile_link->SetBackgroundColor(color());
      separator_ = new views::Separator(views::Separator::HORIZONTAL);
      AddChildView(separator_);
      buttons_view_ = add_profile_link;
      AddChildView(buttons_view_);
    }
  }
  
  void AvatarMenuBubbleView::InitManagedUserContents(
      AvatarMenu* avatar_menu) {
    // Show the profile of the managed user.
    size_t active_index = avatar_menu->GetActiveProfileIndex();
    const AvatarMenu::Item& item =
        avatar_menu->GetItemAt(active_index);
    ProfileItemView* item_view = new ProfileItemView(item,
                                                     this,
                                                     avatar_menu_.get());
    item_view->SetAccessibleName(l10n_util::GetStringFUTF16(
        IDS_PROFILES_SWITCH_TO_PROFILE_ACCESSIBLE_NAME, item.name));
    item_views_.push_back(item_view);
    AddChildView(item_view);
    separator_ = new views::Separator(views::Separator::HORIZONTAL);
    AddChildView(separator_);
  
    // Add information about managed users.
    managed_user_info_ =
        new views::Label(avatar_menu_->GetManagedUserInformation(),
                         ui::ResourceBundle::GetSharedInstance().GetFontList(
                             ui::ResourceBundle::SmallFont));
    managed_user_info_->SetMultiLine(true);
    managed_user_info_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    managed_user_info_->SetBackgroundColor(color());
    AddChildView(managed_user_info_);
  
    // Add the managed user icon.
    icon_view_ = new views::ImageView();
    icon_view_->SetImage(avatar_menu_->GetManagedUserIcon().ToImageSkia());
    AddChildView(icon_view_);
  
    // Add a link for switching profiles.
    separator_switch_users_ = new views::Separator(views::Separator::HORIZONTAL);
    AddChildView(separator_switch_users_);
    switch_profile_link_ = new views::Link(
        l10n_util::GetStringUTF16(IDS_PROFILES_SWITCH_PROFILE_LINK));
    switch_profile_link_->set_listener(this);
    switch_profile_link_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
    switch_profile_link_->SetBackgroundColor(color());
    AddChildView(switch_profile_link_);
  }
  
  void AvatarMenuBubbleView::OnAvatarMenuChanged(
      AvatarMenu* avatar_menu) {
    // Unset all our child view references and call RemoveAllChildViews() which
    // will actually delete them.
    buttons_view_ = NULL;
    managed_user_info_ = NULL;
    item_views_.clear();
    RemoveAllChildViews(true);
  
    if (avatar_menu_->GetManagedUserInformation().empty() || expanded_)
      InitMenuContents(avatar_menu);
    else
      InitManagedUserContents(avatar_menu);
  
    // If the bubble has already been shown then resize and reposition the bubble.
    Layout();
    if (GetBubbleFrameView())
      SizeToContents();
  }
  
  void AvatarMenuBubbleView::SetBackgroundColors() {
    for (size_t i = 0; i < item_views_.size(); ++i) {
      item_views_[i]->OnHighlightStateChanged();
    }
  }
  