root/ui/message_center/views/notifier_settings_view.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. CreateFocusPainter
  2. Layout
  3. GetPreferredSize
  4. GetAccessibleState
  5. OnFocus
  6. OnKeyPressed
  7. OnKeyReleased
  8. OnPaint
  9. OnBlur
  10. notifier_settings_provider_
  11. IsCommandIdChecked
  12. IsCommandIdEnabled
  13. GetAcceleratorForCommandId
  14. ExecuteCommand
  15. learn_more_
  16. UpdateIconImage
  17. SetChecked
  18. checked
  19. has_learn_more
  20. notifier
  21. SendLearnMorePressedForTest
  22. ButtonPressed
  23. GetAccessibleState
  24. ShouldHaveLearnMoreButton
  25. GridChanged
  26. provider_
  27. IsScrollable
  28. UpdateIconImage
  29. NotifierGroupChanged
  30. UpdateContentsView
  31. Layout
  32. GetMinimumSize
  33. GetPreferredSize
  34. OnKeyPressed
  35. OnMouseWheel
  36. ButtonPressed
  37. OnMenuButtonClicked

// Copyright (c) 2013 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 "ui/message_center/views/notifier_settings_view.h"

#include <set>
#include <string>

#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "grit/ui_resources.h"
#include "grit/ui_strings.h"
#include "skia/ext/image_operations.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/simple_menu_model.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/size.h"
#include "ui/message_center/message_center_style.h"
#include "ui/message_center/views/message_center_view.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/checkbox.h"
#include "ui/views/controls/button/custom_button.h"
#include "ui/views/controls/button/label_button_border.h"
#include "ui/views/controls/button/menu_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/link_listener.h"
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/controls/scrollbar/overlay_scroll_bar.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/layout/grid_layout.h"
#include "ui/views/painter.h"
#include "ui/views/widget/widget.h"

#if defined(USE_AURA)
#include "ui/aura/window.h"
#endif

namespace message_center {
namespace settings {

// Additional views-specific parameters.

// The width of the settings pane in pixels.
const int kWidth = 360;

// The width of the learn more icon in pixels.
const int kLearnMoreSize = 12;

// The width of the click target that contains the learn more button in pixels.
const int kLearnMoreTargetWidth = 28;

// The height of the click target that contains the learn more button in pixels.
const int kLearnMoreTargetHeight = 40;

// The minimum height of the settings pane in pixels.
const int kMinimumHeight = 480;

// The horizontal margin of the title area of the settings pane in addition to
// the standard margin from settings::kHorizontalMargin.
const int kTitleMargin = 10;

}  // namespace settings

namespace {

// The amount of built-in padding for the notifier group switcher.
const int kButtonPainterInsets = 5;

// Menu button metrics to make the text line up.
const int kMenuButtonInnateMargin = 2;
const int kMenuButtonLeftPadding = 12;
const int kMenuButtonRightPadding = 13;
const int kMenuButtonVerticalPadding = 9;

// Used to place the context menu correctly.
const int kMenuWhitespaceOffset = 2;

// The innate vertical blank space in the label for the title of the settings
// pane.
const int kInnateTitleBottomMargin = 1;
const int kInnateTitleTopMargin = 7;

// The innate top blank space in the label for the description of the settings
// pane.
const int kInnateDescriptionTopMargin = 2;

// Checkboxes have some built-in right padding blank space.
const int kInnateCheckboxRightPadding = 2;

// Spec defines the checkbox size; the innate padding throws this measurement
// off so we need to compute a slightly different area for the checkbox to
// inhabit.
const int kComputedCheckboxSize =
    settings::kCheckboxSizeWithPadding - kInnateCheckboxRightPadding;

// The menubutton has innate margin, so we need to compensate for that when
// figuring the margin of the title area.
const int kComputedContentsTitleMargin = 0 - kMenuButtonInnateMargin;

// The spec doesn't include the bottom blank area of the title bar or the innate
// blank area in the description label, so we'll use this as the space between
// the title and description.
const int kComputedTitleBottomMargin = settings::kDescriptionToSwitcherSpace -
                                       kInnateTitleBottomMargin -
                                       kInnateDescriptionTopMargin;

// The blank space above the title needs to be adjusted by the amount of blank
// space included in the title label.
const int kComputedTitleTopMargin =
    settings::kTopMargin - kInnateTitleTopMargin;

// The switcher has a lot of blank space built in so we should include that when
// spacing the title area vertically.
const int kComputedTitleElementSpacing =
    settings::kDescriptionToSwitcherSpace - kButtonPainterInsets - 1;

// A function to create a focus border.
scoped_ptr<views::Painter> CreateFocusPainter() {
  return views::Painter::CreateSolidFocusPainter(kFocusBorderColor,
                                                 gfx::Insets(1, 2, 3, 2));
}

// EntryView ------------------------------------------------------------------

// The view to guarantee the 48px height and place the contents at the
// middle. It also guarantee the left margin.
class EntryView : public views::View {
 public:
  explicit EntryView(views::View* contents);
  virtual ~EntryView();

  // views::View:
  virtual void Layout() OVERRIDE;
  virtual gfx::Size GetPreferredSize() OVERRIDE;
  virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE;
  virtual void OnFocus() OVERRIDE;
  virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE;
  virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE;
  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
  virtual void OnBlur() OVERRIDE;

 private:
  scoped_ptr<views::Painter> focus_painter_;

  DISALLOW_COPY_AND_ASSIGN(EntryView);
};

EntryView::EntryView(views::View* contents)
    : focus_painter_(CreateFocusPainter()) {
  AddChildView(contents);
}

EntryView::~EntryView() {}

void EntryView::Layout() {
  DCHECK_EQ(1, child_count());
  views::View* content = child_at(0);
  int content_width = width();
  int content_height = content->GetHeightForWidth(content_width);
  int y = std::max((height() - content_height) / 2, 0);
  content->SetBounds(0, y, content_width, content_height);
}

gfx::Size EntryView::GetPreferredSize() {
  DCHECK_EQ(1, child_count());
  gfx::Size size = child_at(0)->GetPreferredSize();
  size.SetToMax(gfx::Size(settings::kWidth, settings::kEntryHeight));
  return size;
}

void EntryView::GetAccessibleState(ui::AXViewState* state) {
  DCHECK_EQ(1, child_count());
  child_at(0)->GetAccessibleState(state);
}

void EntryView::OnFocus() {
  views::View::OnFocus();
  ScrollRectToVisible(GetLocalBounds());
  // We render differently when focused.
  SchedulePaint();
}

bool EntryView::OnKeyPressed(const ui::KeyEvent& event) {
  return child_at(0)->OnKeyPressed(event);
}

bool EntryView::OnKeyReleased(const ui::KeyEvent& event) {
  return child_at(0)->OnKeyReleased(event);
}

void EntryView::OnPaint(gfx::Canvas* canvas) {
  View::OnPaint(canvas);
  views::Painter::PaintFocusPainter(this, canvas, focus_painter_.get());
}

void EntryView::OnBlur() {
  View::OnBlur();
  // We render differently when focused.
  SchedulePaint();
}

}  // namespace


// NotifierGroupMenuModel -----------------------------------------------------

class NotifierGroupMenuModel : public ui::SimpleMenuModel,
                               public ui::SimpleMenuModel::Delegate {
 public:
  NotifierGroupMenuModel(NotifierSettingsProvider* notifier_settings_provider);
  virtual ~NotifierGroupMenuModel();

  // ui::SimpleMenuModel::Delegate:
  virtual bool IsCommandIdChecked(int command_id) const OVERRIDE;
  virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE;
  virtual bool GetAcceleratorForCommandId(
      int command_id,
      ui::Accelerator* accelerator) OVERRIDE;
  virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;

 private:
  NotifierSettingsProvider* notifier_settings_provider_;

  DISALLOW_COPY_AND_ASSIGN(NotifierGroupMenuModel);
};

NotifierGroupMenuModel::NotifierGroupMenuModel(
    NotifierSettingsProvider* notifier_settings_provider)
    : ui::SimpleMenuModel(this),
      notifier_settings_provider_(notifier_settings_provider) {
  if (!notifier_settings_provider_)
    return;

  size_t num_menu_items = notifier_settings_provider_->GetNotifierGroupCount();
  for (size_t i = 0; i < num_menu_items; ++i) {
    const NotifierGroup& group =
        notifier_settings_provider_->GetNotifierGroupAt(i);

    AddCheckItem(i, group.login_info.empty() ? group.name : group.login_info);
  }
}

NotifierGroupMenuModel::~NotifierGroupMenuModel() {}

bool NotifierGroupMenuModel::IsCommandIdChecked(int command_id) const {
  // If there's no provider, assume only one notifier group - the active one.
  return !notifier_settings_provider_ ||
      notifier_settings_provider_->IsNotifierGroupActiveAt(command_id);
}

bool NotifierGroupMenuModel::IsCommandIdEnabled(int command_id) const {
  return true;
}

bool NotifierGroupMenuModel::GetAcceleratorForCommandId(
    int command_id,
    ui::Accelerator* accelerator) {
  return false;
}

void NotifierGroupMenuModel::ExecuteCommand(int command_id, int event_flags) {
  if (!notifier_settings_provider_)
    return;

  size_t notifier_group_index = static_cast<size_t>(command_id);
  size_t num_notifier_groups =
      notifier_settings_provider_->GetNotifierGroupCount();
  if (notifier_group_index >= num_notifier_groups)
    return;

  notifier_settings_provider_->SwitchToNotifierGroup(notifier_group_index);
}


// NotifierSettingsView::NotifierButton ---------------------------------------

// We do not use views::Checkbox class directly because it doesn't support
// showing 'icon'.
NotifierSettingsView::NotifierButton::NotifierButton(
    NotifierSettingsProvider* provider,
    Notifier* notifier,
    views::ButtonListener* listener)
    : views::CustomButton(listener),
      provider_(provider),
      notifier_(notifier),
      icon_view_(new views::ImageView()),
      name_view_(new views::Label(notifier_->name)),
      checkbox_(new views::Checkbox(base::string16())),
      learn_more_(NULL) {
  DCHECK(provider);
  DCHECK(notifier);

  // Since there may never be an icon (but that could change at a later time),
  // we own the icon view here.
  icon_view_->set_owned_by_client();

  checkbox_->SetChecked(notifier_->enabled);
  checkbox_->set_listener(this);
  checkbox_->SetFocusable(false);
  checkbox_->SetAccessibleName(notifier_->name);

  if (ShouldHaveLearnMoreButton()) {
    // Create a more-info button that will be right-aligned.
    learn_more_ = new views::ImageButton(this);
    learn_more_->SetFocusPainter(CreateFocusPainter());
    learn_more_->set_request_focus_on_press(false);
    learn_more_->SetFocusable(true);

    ui::ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    learn_more_->SetImage(
        views::Button::STATE_NORMAL,
        rb.GetImageSkiaNamed(IDR_NOTIFICATION_ADVANCED_SETTINGS));
    learn_more_->SetImage(
        views::Button::STATE_HOVERED,
        rb.GetImageSkiaNamed(IDR_NOTIFICATION_ADVANCED_SETTINGS_HOVER));
    learn_more_->SetImage(
        views::Button::STATE_PRESSED,
        rb.GetImageSkiaNamed(IDR_NOTIFICATION_ADVANCED_SETTINGS_PRESSED));
    learn_more_->SetState(views::Button::STATE_NORMAL);
    int learn_more_border_width =
        (settings::kLearnMoreTargetWidth - settings::kLearnMoreSize) / 2;
    int learn_more_border_height =
        (settings::kLearnMoreTargetHeight - settings::kLearnMoreSize) / 2;
    // The image itself is quite small, this large invisible border creates a
    // much bigger click target.
    learn_more_->SetBorder(
        views::Border::CreateEmptyBorder(learn_more_border_height,
                                         learn_more_border_width,
                                         learn_more_border_height,
                                         learn_more_border_width));
    learn_more_->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
                                   views::ImageButton::ALIGN_MIDDLE);
  }

  UpdateIconImage(notifier_->icon);
}

NotifierSettingsView::NotifierButton::~NotifierButton() {
}

void NotifierSettingsView::NotifierButton::UpdateIconImage(
    const gfx::Image& icon) {
  bool has_icon_view = false;

  notifier_->icon = icon;
  if (!icon.IsEmpty()) {
    icon_view_->SetImage(icon.ToImageSkia());
    icon_view_->SetImageSize(
        gfx::Size(settings::kEntryIconSize, settings::kEntryIconSize));
    has_icon_view = true;
  }
  GridChanged(ShouldHaveLearnMoreButton(), has_icon_view);
}

void NotifierSettingsView::NotifierButton::SetChecked(bool checked) {
  checkbox_->SetChecked(checked);
  notifier_->enabled = checked;
}

bool NotifierSettingsView::NotifierButton::checked() const {
  return checkbox_->checked();
}

bool NotifierSettingsView::NotifierButton::has_learn_more() const {
  return learn_more_ != NULL;
}

const Notifier& NotifierSettingsView::NotifierButton::notifier() const {
  return *notifier_.get();
}

void NotifierSettingsView::NotifierButton::SendLearnMorePressedForTest() {
  if (learn_more_ == NULL)
    return;
  gfx::Point point(110, 120);
  ui::MouseEvent pressed(
      ui::ET_MOUSE_PRESSED, point, point, ui::EF_LEFT_MOUSE_BUTTON,
      ui::EF_LEFT_MOUSE_BUTTON);
  ButtonPressed(learn_more_, pressed);
}

void NotifierSettingsView::NotifierButton::ButtonPressed(
    views::Button* button,
    const ui::Event& event) {
  if (button == checkbox_) {
    // The checkbox state has already changed at this point, but we'll update
    // the state on NotifierSettingsView::ButtonPressed() too, so here change
    // back to the previous state.
    checkbox_->SetChecked(!checkbox_->checked());
    CustomButton::NotifyClick(event);
  } else if (button == learn_more_) {
    DCHECK(provider_);
    provider_->OnNotifierAdvancedSettingsRequested(notifier_->notifier_id,
                                                   NULL);
  }
}

void NotifierSettingsView::NotifierButton::GetAccessibleState(
    ui::AXViewState* state) {
  static_cast<views::View*>(checkbox_)->GetAccessibleState(state);
}

bool NotifierSettingsView::NotifierButton::ShouldHaveLearnMoreButton() const {
  if (!provider_)
    return false;

  return provider_->NotifierHasAdvancedSettings(notifier_->notifier_id);
}

void NotifierSettingsView::NotifierButton::GridChanged(bool has_learn_more,
                                                       bool has_icon_view) {
  using views::ColumnSet;
  using views::GridLayout;

  GridLayout* layout = new GridLayout(this);
  SetLayoutManager(layout);
  ColumnSet* cs = layout->AddColumnSet(0);
  // Add a column for the checkbox.
  cs->AddPaddingColumn(0, kInnateCheckboxRightPadding);
  cs->AddColumn(GridLayout::CENTER,
                GridLayout::CENTER,
                0,
                GridLayout::FIXED,
                kComputedCheckboxSize,
                0);
  cs->AddPaddingColumn(0, settings::kInternalHorizontalSpacing);

  if (has_icon_view) {
    // Add a column for the icon.
    cs->AddColumn(GridLayout::CENTER,
                  GridLayout::CENTER,
                  0,
                  GridLayout::FIXED,
                  settings::kEntryIconSize,
                  0);
    cs->AddPaddingColumn(0, settings::kInternalHorizontalSpacing);
  }

  // Add a column for the name.
  cs->AddColumn(
      GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0);

  // Add a padding column which contains expandable blank space.
  cs->AddPaddingColumn(1, 0);

  // Add a column for the learn more button if necessary.
  if (has_learn_more) {
    cs->AddPaddingColumn(0, settings::kInternalHorizontalSpacing);
    cs->AddColumn(
        GridLayout::CENTER, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0);
  }

  layout->StartRow(0, 0);
  layout->AddView(checkbox_);
  if (has_icon_view)
    layout->AddView(icon_view_.get());
  layout->AddView(name_view_);
  if (has_learn_more)
    layout->AddView(learn_more_);

  Layout();
}


// NotifierSettingsView -------------------------------------------------------

NotifierSettingsView::NotifierSettingsView(NotifierSettingsProvider* provider)
    : title_arrow_(NULL),
      title_label_(NULL),
      notifier_group_selector_(NULL),
      scroller_(NULL),
      provider_(provider) {
  // |provider_| may be NULL in tests.
  if (provider_)
    provider_->AddObserver(this);

  SetFocusable(true);
  set_background(
      views::Background::CreateSolidBackground(kMessageCenterBackgroundColor));
  SetPaintToLayer(true);

  title_label_ = new views::Label(
      l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_SETTINGS_BUTTON_LABEL),
      ui::ResourceBundle::GetSharedInstance().GetFontList(
          ui::ResourceBundle::MediumFont));
  title_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  title_label_->SetMultiLine(true);
  title_label_->SetBorder(
      views::Border::CreateEmptyBorder(kComputedTitleTopMargin,
                                       settings::kTitleMargin,
                                       kComputedTitleBottomMargin,
                                       settings::kTitleMargin));

  AddChildView(title_label_);

  scroller_ = new views::ScrollView();
  scroller_->SetVerticalScrollBar(new views::OverlayScrollBar(false));
  AddChildView(scroller_);

  std::vector<Notifier*> notifiers;
  if (provider_)
    provider_->GetNotifierList(&notifiers);

  UpdateContentsView(notifiers);
}

NotifierSettingsView::~NotifierSettingsView() {
  // |provider_| may be NULL in tests.
  if (provider_)
    provider_->RemoveObserver(this);
}

bool NotifierSettingsView::IsScrollable() {
  return scroller_->height() < scroller_->contents()->height();
}

void NotifierSettingsView::UpdateIconImage(const NotifierId& notifier_id,
                                           const gfx::Image& icon) {
  for (std::set<NotifierButton*>::iterator iter = buttons_.begin();
       iter != buttons_.end();
       ++iter) {
    if ((*iter)->notifier().notifier_id == notifier_id) {
      (*iter)->UpdateIconImage(icon);
      return;
    }
  }
}

void NotifierSettingsView::NotifierGroupChanged() {
  std::vector<Notifier*> notifiers;
  if (provider_)
    provider_->GetNotifierList(&notifiers);

  UpdateContentsView(notifiers);
}

void NotifierSettingsView::UpdateContentsView(
    const std::vector<Notifier*>& notifiers) {
  buttons_.clear();

  views::View* contents_view = new views::View();
  contents_view->SetLayoutManager(new views::BoxLayout(
      views::BoxLayout::kVertical, settings::kHorizontalMargin, 0, 0));

  views::View* contents_title_view = new views::View();
  contents_title_view->SetLayoutManager(
      new views::BoxLayout(views::BoxLayout::kVertical,
                           kComputedContentsTitleMargin,
                           0,
                           kComputedTitleElementSpacing));

  bool need_account_switcher =
      provider_ && provider_->GetNotifierGroupCount() > 1;
  int top_label_resource_id =
      need_account_switcher ? IDS_MESSAGE_CENTER_SETTINGS_DESCRIPTION_MULTIUSER
                            : IDS_MESSAGE_CENTER_SETTINGS_DIALOG_DESCRIPTION;

  views::Label* top_label =
      new views::Label(l10n_util::GetStringUTF16(top_label_resource_id));

  top_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  top_label->SetMultiLine(true);
  top_label->SetBorder(views::Border::CreateEmptyBorder(
      0,
      settings::kTitleMargin + kMenuButtonInnateMargin,
      0,
      settings::kTitleMargin + kMenuButtonInnateMargin));
  contents_title_view->AddChildView(top_label);

  if (need_account_switcher) {
    const NotifierGroup& active_group = provider_->GetActiveNotifierGroup();
    base::string16 notifier_group_text = active_group.login_info.empty() ?
        active_group.name : active_group.login_info;
    notifier_group_selector_ =
        new views::MenuButton(NULL, notifier_group_text, this, true);
    scoped_ptr<views::TextButtonDefaultBorder> selector_border(
        new views::TextButtonDefaultBorder());
    ui::ResourceBundle* rb = &ResourceBundle::GetSharedInstance();
    gfx::Insets painter_insets(kButtonPainterInsets, kButtonPainterInsets,
                               kButtonPainterInsets, kButtonPainterInsets);
    selector_border->set_normal_painter(views::Painter::CreateImagePainter(
        *rb->GetImageSkiaNamed(IDR_BUTTON_NORMAL), painter_insets));
    selector_border->set_hot_painter(views::Painter::CreateImagePainter(
        *rb->GetImageSkiaNamed(IDR_BUTTON_HOVER), painter_insets));
    selector_border->set_pushed_painter(views::Painter::CreateImagePainter(
        *rb->GetImageSkiaNamed(IDR_BUTTON_PRESSED), painter_insets));
    selector_border->SetInsets(gfx::Insets(
        kMenuButtonVerticalPadding, kMenuButtonLeftPadding,
        kMenuButtonVerticalPadding, kMenuButtonRightPadding));
    notifier_group_selector_->SetBorder(
        selector_border.PassAs<views::Border>());
    notifier_group_selector_->SetFocusPainter(scoped_ptr<views::Painter>());
    notifier_group_selector_->set_animate_on_state_change(false);
    notifier_group_selector_->SetFocusable(true);
    contents_title_view->AddChildView(notifier_group_selector_);
  }

  contents_view->AddChildView(contents_title_view);

  size_t notifier_count = notifiers.size();
  for (size_t i = 0; i < notifier_count; ++i) {
    NotifierButton* button = new NotifierButton(provider_, notifiers[i], this);
    EntryView* entry = new EntryView(button);

    // This code emulates separators using borders.  We will create an invisible
    // border on the last notifier, as the spec leaves a space for it.
    scoped_ptr<views::Border> entry_border;
    if (i == notifier_count - 1) {
      entry_border = views::Border::CreateEmptyBorder(
          0, 0, settings::kEntrySeparatorHeight, 0);
    } else {
      entry_border =
          views::Border::CreateSolidSidedBorder(0,
                                                0,
                                                settings::kEntrySeparatorHeight,
                                                0,
                                                settings::kEntrySeparatorColor);
    }
    entry->SetBorder(entry_border.Pass());
    entry->SetFocusable(true);
    contents_view->AddChildView(entry);
    buttons_.insert(button);
  }

  scroller_->SetContents(contents_view);

  contents_view->SetBoundsRect(gfx::Rect(contents_view->GetPreferredSize()));
  InvalidateLayout();
}

void NotifierSettingsView::Layout() {
  int title_height = title_label_->GetHeightForWidth(width());
  title_label_->SetBounds(settings::kTitleMargin,
                          0,
                          width() - settings::kTitleMargin * 2,
                          title_height);

  views::View* contents_view = scroller_->contents();
  int content_width = width();
  int content_height = contents_view->GetHeightForWidth(content_width);
  if (title_height + content_height > height()) {
    content_width -= scroller_->GetScrollBarWidth();
    content_height = contents_view->GetHeightForWidth(content_width);
  }
  contents_view->SetBounds(0, 0, content_width, content_height);
  scroller_->SetBounds(0, title_height, width(), height() - title_height);
}

gfx::Size NotifierSettingsView::GetMinimumSize() {
  gfx::Size size(settings::kWidth, settings::kMinimumHeight);
  int total_height = title_label_->GetPreferredSize().height() +
                     scroller_->contents()->GetPreferredSize().height();
  if (total_height > settings::kMinimumHeight)
    size.Enlarge(scroller_->GetScrollBarWidth(), 0);
  return size;
}

gfx::Size NotifierSettingsView::GetPreferredSize() {
  gfx::Size preferred_size;
  gfx::Size title_size = title_label_->GetPreferredSize();
  gfx::Size content_size = scroller_->contents()->GetPreferredSize();
  return gfx::Size(std::max(title_size.width(), content_size.width()),
                   title_size.height() + content_size.height());
}

bool NotifierSettingsView::OnKeyPressed(const ui::KeyEvent& event) {
  if (event.key_code() == ui::VKEY_ESCAPE) {
    GetWidget()->Close();
    return true;
  }

  return scroller_->OnKeyPressed(event);
}

bool NotifierSettingsView::OnMouseWheel(const ui::MouseWheelEvent& event) {
  return scroller_->OnMouseWheel(event);
}

void NotifierSettingsView::ButtonPressed(views::Button* sender,
                                         const ui::Event& event) {
  if (sender == title_arrow_) {
    MessageCenterView* center_view = static_cast<MessageCenterView*>(parent());
    center_view->SetSettingsVisible(!center_view->settings_visible());
    return;
  }

  std::set<NotifierButton*>::iterator iter =
      buttons_.find(static_cast<NotifierButton*>(sender));

  if (iter == buttons_.end())
    return;

  (*iter)->SetChecked(!(*iter)->checked());
  if (provider_)
    provider_->SetNotifierEnabled((*iter)->notifier(), (*iter)->checked());
}

void NotifierSettingsView::OnMenuButtonClicked(views::View* source,
                                               const gfx::Point& point) {
  notifier_group_menu_model_.reset(new NotifierGroupMenuModel(provider_));
  notifier_group_menu_runner_.reset(
      new views::MenuRunner(notifier_group_menu_model_.get()));
  gfx::Rect menu_anchor = source->GetBoundsInScreen();
  menu_anchor.Inset(
      gfx::Insets(0, kMenuWhitespaceOffset, 0, kMenuWhitespaceOffset));
  if (views::MenuRunner::MENU_DELETED ==
      notifier_group_menu_runner_->RunMenuAt(GetWidget(),
                                             notifier_group_selector_,
                                             menu_anchor,
                                             views::MenuItemView::BUBBLE_ABOVE,
                                             ui::MENU_SOURCE_MOUSE,
                                             views::MenuRunner::CONTEXT_MENU))
    return;
  MessageCenterView* center_view = static_cast<MessageCenterView*>(parent());
  center_view->OnSettingsChanged();
}

}  // namespace message_center

/* [<][>][^][v][top][bottom][index][help] */