Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / ui / message_center / views / notifier_settings_view.cc
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/message_center/views/notifier_settings_view.h"
6
7 #include <set>
8 #include <string>
9
10 #include "base/strings/string16.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "skia/ext/image_operations.h"
13 #include "third_party/skia/include/core/SkColor.h"
14 #include "ui/base/l10n/l10n_util.h"
15 #include "ui/base/models/simple_menu_model.h"
16 #include "ui/base/resource/resource_bundle.h"
17 #include "ui/events/keycodes/keyboard_codes.h"
18 #include "ui/gfx/canvas.h"
19 #include "ui/gfx/image/image.h"
20 #include "ui/gfx/size.h"
21 #include "ui/message_center/message_center_style.h"
22 #include "ui/message_center/views/message_center_view.h"
23 #include "ui/resources/grit/ui_resources.h"
24 #include "ui/strings/grit/ui_strings.h"
25 #include "ui/views/background.h"
26 #include "ui/views/border.h"
27 #include "ui/views/controls/button/checkbox.h"
28 #include "ui/views/controls/button/label_button_border.h"
29 #include "ui/views/controls/button/menu_button.h"
30 #include "ui/views/controls/image_view.h"
31 #include "ui/views/controls/label.h"
32 #include "ui/views/controls/link.h"
33 #include "ui/views/controls/link_listener.h"
34 #include "ui/views/controls/menu/menu_runner.h"
35 #include "ui/views/controls/scroll_view.h"
36 #include "ui/views/controls/scrollbar/overlay_scroll_bar.h"
37 #include "ui/views/layout/box_layout.h"
38 #include "ui/views/layout/fill_layout.h"
39 #include "ui/views/layout/grid_layout.h"
40 #include "ui/views/painter.h"
41 #include "ui/views/widget/widget.h"
42
43 namespace message_center {
44 namespace settings {
45
46 // Additional views-specific parameters.
47
48 // The width of the settings pane in pixels.
49 const int kWidth = 360;
50
51 // The width of the learn more icon in pixels.
52 const int kLearnMoreSize = 12;
53
54 // The width of the click target that contains the learn more button in pixels.
55 const int kLearnMoreTargetWidth = 28;
56
57 // The height of the click target that contains the learn more button in pixels.
58 const int kLearnMoreTargetHeight = 40;
59
60 // The minimum height of the settings pane in pixels.
61 const int kMinimumHeight = 480;
62
63 // The horizontal margin of the title area of the settings pane in addition to
64 // the standard margin from settings::kHorizontalMargin.
65 const int kTitleMargin = 10;
66
67 }  // namespace settings
68
69 namespace {
70
71 // Menu button metrics to make the text line up.
72 const int kMenuButtonInnateMargin = 2;
73
74 // Used to place the context menu correctly.
75 const int kMenuWhitespaceOffset = 2;
76
77 // The innate vertical blank space in the label for the title of the settings
78 // pane.
79 const int kInnateTitleBottomMargin = 1;
80 const int kInnateTitleTopMargin = 7;
81
82 // The innate top blank space in the label for the description of the settings
83 // pane.
84 const int kInnateDescriptionTopMargin = 2;
85
86 // Checkboxes have some built-in right padding blank space.
87 const int kInnateCheckboxRightPadding = 2;
88
89 // Spec defines the checkbox size; the innate padding throws this measurement
90 // off so we need to compute a slightly different area for the checkbox to
91 // inhabit.
92 const int kComputedCheckboxSize =
93     settings::kCheckboxSizeWithPadding - kInnateCheckboxRightPadding;
94
95 // The menubutton has innate margin, so we need to compensate for that when
96 // figuring the margin of the title area.
97 const int kComputedContentsTitleMargin = 0 - kMenuButtonInnateMargin;
98
99 // The spec doesn't include the bottom blank area of the title bar or the innate
100 // blank area in the description label, so we'll use this as the space between
101 // the title and description.
102 const int kComputedTitleBottomMargin = settings::kDescriptionToSwitcherSpace -
103                                        kInnateTitleBottomMargin -
104                                        kInnateDescriptionTopMargin;
105
106 // The blank space above the title needs to be adjusted by the amount of blank
107 // space included in the title label.
108 const int kComputedTitleTopMargin =
109     settings::kTopMargin - kInnateTitleTopMargin;
110
111 // The switcher has a lot of blank space built in so we should include that when
112 // spacing the title area vertically.
113 const int kComputedTitleElementSpacing =
114     settings::kDescriptionToSwitcherSpace - 6;
115
116 // A function to create a focus border.
117 scoped_ptr<views::Painter> CreateFocusPainter() {
118   return views::Painter::CreateSolidFocusPainter(kFocusBorderColor,
119                                                  gfx::Insets(1, 2, 3, 2));
120 }
121
122 // EntryView ------------------------------------------------------------------
123
124 // The view to guarantee the 48px height and place the contents at the
125 // middle. It also guarantee the left margin.
126 class EntryView : public views::View {
127  public:
128   explicit EntryView(views::View* contents);
129   virtual ~EntryView();
130
131   // views::View:
132   virtual void Layout() OVERRIDE;
133   virtual gfx::Size GetPreferredSize() const OVERRIDE;
134   virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE;
135   virtual void OnFocus() OVERRIDE;
136   virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE;
137   virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE;
138   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
139   virtual void OnBlur() OVERRIDE;
140
141  private:
142   scoped_ptr<views::Painter> focus_painter_;
143
144   DISALLOW_COPY_AND_ASSIGN(EntryView);
145 };
146
147 EntryView::EntryView(views::View* contents)
148     : focus_painter_(CreateFocusPainter()) {
149   AddChildView(contents);
150 }
151
152 EntryView::~EntryView() {}
153
154 void EntryView::Layout() {
155   DCHECK_EQ(1, child_count());
156   views::View* content = child_at(0);
157   int content_width = width();
158   int content_height = content->GetHeightForWidth(content_width);
159   int y = std::max((height() - content_height) / 2, 0);
160   content->SetBounds(0, y, content_width, content_height);
161 }
162
163 gfx::Size EntryView::GetPreferredSize() const {
164   DCHECK_EQ(1, child_count());
165   gfx::Size size = child_at(0)->GetPreferredSize();
166   size.SetToMax(gfx::Size(settings::kWidth, settings::kEntryHeight));
167   return size;
168 }
169
170 void EntryView::GetAccessibleState(ui::AXViewState* state) {
171   DCHECK_EQ(1, child_count());
172   child_at(0)->GetAccessibleState(state);
173 }
174
175 void EntryView::OnFocus() {
176   views::View::OnFocus();
177   ScrollRectToVisible(GetLocalBounds());
178   // We render differently when focused.
179   SchedulePaint();
180 }
181
182 bool EntryView::OnKeyPressed(const ui::KeyEvent& event) {
183   return child_at(0)->OnKeyPressed(event);
184 }
185
186 bool EntryView::OnKeyReleased(const ui::KeyEvent& event) {
187   return child_at(0)->OnKeyReleased(event);
188 }
189
190 void EntryView::OnPaint(gfx::Canvas* canvas) {
191   View::OnPaint(canvas);
192   views::Painter::PaintFocusPainter(this, canvas, focus_painter_.get());
193 }
194
195 void EntryView::OnBlur() {
196   View::OnBlur();
197   // We render differently when focused.
198   SchedulePaint();
199 }
200
201 }  // namespace
202
203
204 // NotifierGroupMenuModel -----------------------------------------------------
205
206 class NotifierGroupMenuModel : public ui::SimpleMenuModel,
207                                public ui::SimpleMenuModel::Delegate {
208  public:
209   NotifierGroupMenuModel(NotifierSettingsProvider* notifier_settings_provider);
210   virtual ~NotifierGroupMenuModel();
211
212   // ui::SimpleMenuModel::Delegate:
213   virtual bool IsCommandIdChecked(int command_id) const OVERRIDE;
214   virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE;
215   virtual bool GetAcceleratorForCommandId(
216       int command_id,
217       ui::Accelerator* accelerator) OVERRIDE;
218   virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;
219
220  private:
221   NotifierSettingsProvider* notifier_settings_provider_;
222
223   DISALLOW_COPY_AND_ASSIGN(NotifierGroupMenuModel);
224 };
225
226 NotifierGroupMenuModel::NotifierGroupMenuModel(
227     NotifierSettingsProvider* notifier_settings_provider)
228     : ui::SimpleMenuModel(this),
229       notifier_settings_provider_(notifier_settings_provider) {
230   if (!notifier_settings_provider_)
231     return;
232
233   size_t num_menu_items = notifier_settings_provider_->GetNotifierGroupCount();
234   for (size_t i = 0; i < num_menu_items; ++i) {
235     const NotifierGroup& group =
236         notifier_settings_provider_->GetNotifierGroupAt(i);
237
238     AddCheckItem(i, group.login_info.empty() ? group.name : group.login_info);
239   }
240 }
241
242 NotifierGroupMenuModel::~NotifierGroupMenuModel() {}
243
244 bool NotifierGroupMenuModel::IsCommandIdChecked(int command_id) const {
245   // If there's no provider, assume only one notifier group - the active one.
246   return !notifier_settings_provider_ ||
247       notifier_settings_provider_->IsNotifierGroupActiveAt(command_id);
248 }
249
250 bool NotifierGroupMenuModel::IsCommandIdEnabled(int command_id) const {
251   return true;
252 }
253
254 bool NotifierGroupMenuModel::GetAcceleratorForCommandId(
255     int command_id,
256     ui::Accelerator* accelerator) {
257   return false;
258 }
259
260 void NotifierGroupMenuModel::ExecuteCommand(int command_id, int event_flags) {
261   if (!notifier_settings_provider_)
262     return;
263
264   size_t notifier_group_index = static_cast<size_t>(command_id);
265   size_t num_notifier_groups =
266       notifier_settings_provider_->GetNotifierGroupCount();
267   if (notifier_group_index >= num_notifier_groups)
268     return;
269
270   notifier_settings_provider_->SwitchToNotifierGroup(notifier_group_index);
271 }
272
273
274 // NotifierSettingsView::NotifierButton ---------------------------------------
275
276 // We do not use views::Checkbox class directly because it doesn't support
277 // showing 'icon'.
278 NotifierSettingsView::NotifierButton::NotifierButton(
279     NotifierSettingsProvider* provider,
280     Notifier* notifier,
281     views::ButtonListener* listener)
282     : views::CustomButton(listener),
283       provider_(provider),
284       notifier_(notifier),
285       icon_view_(new views::ImageView()),
286       name_view_(new views::Label(notifier_->name)),
287       checkbox_(new views::Checkbox(base::string16())),
288       learn_more_(NULL) {
289   DCHECK(provider);
290   DCHECK(notifier);
291
292   // Since there may never be an icon (but that could change at a later time),
293   // we own the icon view here.
294   icon_view_->set_owned_by_client();
295
296   checkbox_->SetChecked(notifier_->enabled);
297   checkbox_->set_listener(this);
298   checkbox_->SetFocusable(false);
299   checkbox_->SetAccessibleName(notifier_->name);
300
301   if (ShouldHaveLearnMoreButton()) {
302     // Create a more-info button that will be right-aligned.
303     learn_more_ = new views::ImageButton(this);
304     learn_more_->SetFocusPainter(CreateFocusPainter());
305     learn_more_->set_request_focus_on_press(false);
306     learn_more_->SetFocusable(true);
307
308     ui::ResourceBundle& rb = ResourceBundle::GetSharedInstance();
309     learn_more_->SetImage(
310         views::Button::STATE_NORMAL,
311         rb.GetImageSkiaNamed(IDR_NOTIFICATION_ADVANCED_SETTINGS));
312     learn_more_->SetImage(
313         views::Button::STATE_HOVERED,
314         rb.GetImageSkiaNamed(IDR_NOTIFICATION_ADVANCED_SETTINGS_HOVER));
315     learn_more_->SetImage(
316         views::Button::STATE_PRESSED,
317         rb.GetImageSkiaNamed(IDR_NOTIFICATION_ADVANCED_SETTINGS_PRESSED));
318     learn_more_->SetState(views::Button::STATE_NORMAL);
319     int learn_more_border_width =
320         (settings::kLearnMoreTargetWidth - settings::kLearnMoreSize) / 2;
321     int learn_more_border_height =
322         (settings::kLearnMoreTargetHeight - settings::kLearnMoreSize) / 2;
323     // The image itself is quite small, this large invisible border creates a
324     // much bigger click target.
325     learn_more_->SetBorder(
326         views::Border::CreateEmptyBorder(learn_more_border_height,
327                                          learn_more_border_width,
328                                          learn_more_border_height,
329                                          learn_more_border_width));
330     learn_more_->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
331                                    views::ImageButton::ALIGN_MIDDLE);
332   }
333
334   UpdateIconImage(notifier_->icon);
335 }
336
337 NotifierSettingsView::NotifierButton::~NotifierButton() {
338 }
339
340 void NotifierSettingsView::NotifierButton::UpdateIconImage(
341     const gfx::Image& icon) {
342   bool has_icon_view = false;
343
344   notifier_->icon = icon;
345   if (!icon.IsEmpty()) {
346     icon_view_->SetImage(icon.ToImageSkia());
347     icon_view_->SetImageSize(
348         gfx::Size(settings::kEntryIconSize, settings::kEntryIconSize));
349     has_icon_view = true;
350   }
351   GridChanged(ShouldHaveLearnMoreButton(), has_icon_view);
352 }
353
354 void NotifierSettingsView::NotifierButton::SetChecked(bool checked) {
355   checkbox_->SetChecked(checked);
356   notifier_->enabled = checked;
357 }
358
359 bool NotifierSettingsView::NotifierButton::checked() const {
360   return checkbox_->checked();
361 }
362
363 bool NotifierSettingsView::NotifierButton::has_learn_more() const {
364   return learn_more_ != NULL;
365 }
366
367 const Notifier& NotifierSettingsView::NotifierButton::notifier() const {
368   return *notifier_.get();
369 }
370
371 void NotifierSettingsView::NotifierButton::SendLearnMorePressedForTest() {
372   if (learn_more_ == NULL)
373     return;
374   gfx::Point point(110, 120);
375   ui::MouseEvent pressed(
376       ui::ET_MOUSE_PRESSED, point, point, ui::EF_LEFT_MOUSE_BUTTON,
377       ui::EF_LEFT_MOUSE_BUTTON);
378   ButtonPressed(learn_more_, pressed);
379 }
380
381 void NotifierSettingsView::NotifierButton::ButtonPressed(
382     views::Button* button,
383     const ui::Event& event) {
384   if (button == checkbox_) {
385     // The checkbox state has already changed at this point, but we'll update
386     // the state on NotifierSettingsView::ButtonPressed() too, so here change
387     // back to the previous state.
388     checkbox_->SetChecked(!checkbox_->checked());
389     CustomButton::NotifyClick(event);
390   } else if (button == learn_more_) {
391     DCHECK(provider_);
392     provider_->OnNotifierAdvancedSettingsRequested(notifier_->notifier_id,
393                                                    NULL);
394   }
395 }
396
397 void NotifierSettingsView::NotifierButton::GetAccessibleState(
398     ui::AXViewState* state) {
399   static_cast<views::View*>(checkbox_)->GetAccessibleState(state);
400 }
401
402 bool NotifierSettingsView::NotifierButton::ShouldHaveLearnMoreButton() const {
403   if (!provider_)
404     return false;
405
406   return provider_->NotifierHasAdvancedSettings(notifier_->notifier_id);
407 }
408
409 void NotifierSettingsView::NotifierButton::GridChanged(bool has_learn_more,
410                                                        bool has_icon_view) {
411   using views::ColumnSet;
412   using views::GridLayout;
413
414   GridLayout* layout = new GridLayout(this);
415   SetLayoutManager(layout);
416   ColumnSet* cs = layout->AddColumnSet(0);
417   // Add a column for the checkbox.
418   cs->AddPaddingColumn(0, kInnateCheckboxRightPadding);
419   cs->AddColumn(GridLayout::CENTER,
420                 GridLayout::CENTER,
421                 0,
422                 GridLayout::FIXED,
423                 kComputedCheckboxSize,
424                 0);
425   cs->AddPaddingColumn(0, settings::kInternalHorizontalSpacing);
426
427   if (has_icon_view) {
428     // Add a column for the icon.
429     cs->AddColumn(GridLayout::CENTER,
430                   GridLayout::CENTER,
431                   0,
432                   GridLayout::FIXED,
433                   settings::kEntryIconSize,
434                   0);
435     cs->AddPaddingColumn(0, settings::kInternalHorizontalSpacing);
436   }
437
438   // Add a column for the name.
439   cs->AddColumn(
440       GridLayout::LEADING, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0);
441
442   // Add a padding column which contains expandable blank space.
443   cs->AddPaddingColumn(1, 0);
444
445   // Add a column for the learn more button if necessary.
446   if (has_learn_more) {
447     cs->AddPaddingColumn(0, settings::kInternalHorizontalSpacing);
448     cs->AddColumn(
449         GridLayout::CENTER, GridLayout::CENTER, 0, GridLayout::USE_PREF, 0, 0);
450   }
451
452   layout->StartRow(0, 0);
453   layout->AddView(checkbox_);
454   if (has_icon_view)
455     layout->AddView(icon_view_.get());
456   layout->AddView(name_view_);
457   if (has_learn_more)
458     layout->AddView(learn_more_);
459
460   Layout();
461 }
462
463
464 // NotifierSettingsView -------------------------------------------------------
465
466 NotifierSettingsView::NotifierSettingsView(NotifierSettingsProvider* provider)
467     : title_arrow_(NULL),
468       title_label_(NULL),
469       notifier_group_selector_(NULL),
470       scroller_(NULL),
471       provider_(provider) {
472   // |provider_| may be NULL in tests.
473   if (provider_)
474     provider_->AddObserver(this);
475
476   SetFocusable(true);
477   set_background(
478       views::Background::CreateSolidBackground(kMessageCenterBackgroundColor));
479   SetPaintToLayer(true);
480
481   title_label_ = new views::Label(
482       l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_SETTINGS_BUTTON_LABEL),
483       ui::ResourceBundle::GetSharedInstance().GetFontList(
484           ui::ResourceBundle::MediumFont));
485   title_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
486   title_label_->SetMultiLine(true);
487   title_label_->SetBorder(
488       views::Border::CreateEmptyBorder(kComputedTitleTopMargin,
489                                        settings::kTitleMargin,
490                                        kComputedTitleBottomMargin,
491                                        settings::kTitleMargin));
492
493   AddChildView(title_label_);
494
495   scroller_ = new views::ScrollView();
496   scroller_->SetVerticalScrollBar(new views::OverlayScrollBar(false));
497   AddChildView(scroller_);
498
499   std::vector<Notifier*> notifiers;
500   if (provider_)
501     provider_->GetNotifierList(&notifiers);
502
503   UpdateContentsView(notifiers);
504 }
505
506 NotifierSettingsView::~NotifierSettingsView() {
507   // |provider_| may be NULL in tests.
508   if (provider_)
509     provider_->RemoveObserver(this);
510 }
511
512 bool NotifierSettingsView::IsScrollable() {
513   return scroller_->height() < scroller_->contents()->height();
514 }
515
516 void NotifierSettingsView::UpdateIconImage(const NotifierId& notifier_id,
517                                            const gfx::Image& icon) {
518   for (std::set<NotifierButton*>::iterator iter = buttons_.begin();
519        iter != buttons_.end();
520        ++iter) {
521     if ((*iter)->notifier().notifier_id == notifier_id) {
522       (*iter)->UpdateIconImage(icon);
523       return;
524     }
525   }
526 }
527
528 void NotifierSettingsView::NotifierGroupChanged() {
529   std::vector<Notifier*> notifiers;
530   if (provider_)
531     provider_->GetNotifierList(&notifiers);
532
533   UpdateContentsView(notifiers);
534 }
535
536 void NotifierSettingsView::NotifierEnabledChanged(const NotifierId& notifier_id,
537                                                   bool enabled) {}
538
539 void NotifierSettingsView::UpdateContentsView(
540     const std::vector<Notifier*>& notifiers) {
541   buttons_.clear();
542
543   views::View* contents_view = new views::View();
544   contents_view->SetLayoutManager(new views::BoxLayout(
545       views::BoxLayout::kVertical, settings::kHorizontalMargin, 0, 0));
546
547   views::View* contents_title_view = new views::View();
548   contents_title_view->SetLayoutManager(
549       new views::BoxLayout(views::BoxLayout::kVertical,
550                            kComputedContentsTitleMargin,
551                            0,
552                            kComputedTitleElementSpacing));
553
554   bool need_account_switcher =
555       provider_ && provider_->GetNotifierGroupCount() > 1;
556   int top_label_resource_id =
557       need_account_switcher ? IDS_MESSAGE_CENTER_SETTINGS_DESCRIPTION_MULTIUSER
558                             : IDS_MESSAGE_CENTER_SETTINGS_DIALOG_DESCRIPTION;
559
560   views::Label* top_label =
561       new views::Label(l10n_util::GetStringUTF16(top_label_resource_id));
562
563   top_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
564   top_label->SetMultiLine(true);
565   top_label->SetBorder(views::Border::CreateEmptyBorder(
566       0,
567       settings::kTitleMargin + kMenuButtonInnateMargin,
568       0,
569       settings::kTitleMargin + kMenuButtonInnateMargin));
570   contents_title_view->AddChildView(top_label);
571
572   if (need_account_switcher) {
573     const NotifierGroup& active_group = provider_->GetActiveNotifierGroup();
574     base::string16 notifier_group_text = active_group.login_info.empty() ?
575         active_group.name : active_group.login_info;
576     notifier_group_selector_ =
577         new views::MenuButton(NULL, notifier_group_text, this, true);
578     notifier_group_selector_->SetBorder(scoped_ptr<views::Border>(
579         new views::LabelButtonBorder(views::Button::STYLE_BUTTON)).Pass());
580     notifier_group_selector_->SetFocusPainter(scoped_ptr<views::Painter>());
581     notifier_group_selector_->set_animate_on_state_change(false);
582     notifier_group_selector_->SetFocusable(true);
583     contents_title_view->AddChildView(notifier_group_selector_);
584   }
585
586   contents_view->AddChildView(contents_title_view);
587
588   size_t notifier_count = notifiers.size();
589   for (size_t i = 0; i < notifier_count; ++i) {
590     NotifierButton* button = new NotifierButton(provider_, notifiers[i], this);
591     EntryView* entry = new EntryView(button);
592
593     // This code emulates separators using borders.  We will create an invisible
594     // border on the last notifier, as the spec leaves a space for it.
595     scoped_ptr<views::Border> entry_border;
596     if (i == notifier_count - 1) {
597       entry_border = views::Border::CreateEmptyBorder(
598           0, 0, settings::kEntrySeparatorHeight, 0);
599     } else {
600       entry_border =
601           views::Border::CreateSolidSidedBorder(0,
602                                                 0,
603                                                 settings::kEntrySeparatorHeight,
604                                                 0,
605                                                 settings::kEntrySeparatorColor);
606     }
607     entry->SetBorder(entry_border.Pass());
608     entry->SetFocusable(true);
609     contents_view->AddChildView(entry);
610     buttons_.insert(button);
611   }
612
613   scroller_->SetContents(contents_view);
614
615   contents_view->SetBoundsRect(gfx::Rect(contents_view->GetPreferredSize()));
616   InvalidateLayout();
617 }
618
619 void NotifierSettingsView::Layout() {
620   int title_height = title_label_->GetHeightForWidth(width());
621   title_label_->SetBounds(settings::kTitleMargin,
622                           0,
623                           width() - settings::kTitleMargin * 2,
624                           title_height);
625
626   views::View* contents_view = scroller_->contents();
627   int content_width = width();
628   int content_height = contents_view->GetHeightForWidth(content_width);
629   if (title_height + content_height > height()) {
630     content_width -= scroller_->GetScrollBarWidth();
631     content_height = contents_view->GetHeightForWidth(content_width);
632   }
633   contents_view->SetBounds(0, 0, content_width, content_height);
634   scroller_->SetBounds(0, title_height, width(), height() - title_height);
635 }
636
637 gfx::Size NotifierSettingsView::GetMinimumSize() const {
638   gfx::Size size(settings::kWidth, settings::kMinimumHeight);
639   int total_height = title_label_->GetPreferredSize().height() +
640                      scroller_->contents()->GetPreferredSize().height();
641   if (total_height > settings::kMinimumHeight)
642     size.Enlarge(scroller_->GetScrollBarWidth(), 0);
643   return size;
644 }
645
646 gfx::Size NotifierSettingsView::GetPreferredSize() const {
647   gfx::Size preferred_size;
648   gfx::Size title_size = title_label_->GetPreferredSize();
649   gfx::Size content_size = scroller_->contents()->GetPreferredSize();
650   return gfx::Size(std::max(title_size.width(), content_size.width()),
651                    title_size.height() + content_size.height());
652 }
653
654 bool NotifierSettingsView::OnKeyPressed(const ui::KeyEvent& event) {
655   if (event.key_code() == ui::VKEY_ESCAPE) {
656     GetWidget()->Close();
657     return true;
658   }
659
660   return scroller_->OnKeyPressed(event);
661 }
662
663 bool NotifierSettingsView::OnMouseWheel(const ui::MouseWheelEvent& event) {
664   return scroller_->OnMouseWheel(event);
665 }
666
667 void NotifierSettingsView::ButtonPressed(views::Button* sender,
668                                          const ui::Event& event) {
669   if (sender == title_arrow_) {
670     MessageCenterView* center_view = static_cast<MessageCenterView*>(parent());
671     center_view->SetSettingsVisible(!center_view->settings_visible());
672     return;
673   }
674
675   std::set<NotifierButton*>::iterator iter =
676       buttons_.find(static_cast<NotifierButton*>(sender));
677
678   if (iter == buttons_.end())
679     return;
680
681   (*iter)->SetChecked(!(*iter)->checked());
682   if (provider_)
683     provider_->SetNotifierEnabled((*iter)->notifier(), (*iter)->checked());
684 }
685
686 void NotifierSettingsView::OnMenuButtonClicked(views::View* source,
687                                                const gfx::Point& point) {
688   notifier_group_menu_model_.reset(new NotifierGroupMenuModel(provider_));
689   notifier_group_menu_runner_.reset(new views::MenuRunner(
690       notifier_group_menu_model_.get(), views::MenuRunner::CONTEXT_MENU));
691   gfx::Rect menu_anchor = source->GetBoundsInScreen();
692   menu_anchor.Inset(
693       gfx::Insets(0, kMenuWhitespaceOffset, 0, kMenuWhitespaceOffset));
694   if (views::MenuRunner::MENU_DELETED ==
695       notifier_group_menu_runner_->RunMenuAt(GetWidget(),
696                                              notifier_group_selector_,
697                                              menu_anchor,
698                                              views::MENU_ANCHOR_BUBBLE_ABOVE,
699                                              ui::MENU_SOURCE_MOUSE))
700     return;
701   MessageCenterView* center_view = static_cast<MessageCenterView*>(parent());
702   center_view->OnSettingsChanged();
703 }
704
705 }  // namespace message_center