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