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