1 // Copyright (c) 2012 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/message_view.h"
6
7 #include "grit/ui_resources.h"
8 #include "grit/ui_strings.h"
9 #include "ui/base/accessibility/accessible_view_state.h"
10 #include "ui/base/l10n/l10n_util.h"
11 #include "ui/base/models/simple_menu_model.h"
12 #include "ui/base/resource/resource_bundle.h"
13 #include "ui/compositor/scoped_layer_animation_settings.h"
14 #include "ui/gfx/canvas.h"
15 #include "ui/message_center/message_center.h"
16 #include "ui/message_center/message_center_style.h"
17 #include "ui/message_center/message_center_util.h"
18 #include "ui/message_center/views/padded_button.h"
19 #include "ui/views/context_menu_controller.h"
20 #include "ui/views/controls/button/image_button.h"
21 #include "ui/views/controls/menu/menu_runner.h"
22 #include "ui/views/controls/scroll_view.h"
23 #include "ui/views/painter.h"
24 #include "ui/views/shadow_border.h"
25 #include "ui/views/widget/widget.h"
26
27 namespace {
28
29 const int kCloseIconTopPadding = 5;
30 const int kCloseIconRightPadding = 5;
31
32 const int kShadowOffset = 1;
33 const int kShadowBlur = 4;
34
35 // Menu constants
36 const int kTogglePermissionCommand = 0;
37 const int kShowSettingsCommand = 1;
38
39 // A dropdown menu for notifications.
40 class MenuModel : public ui::SimpleMenuModel,
41 public ui::SimpleMenuModel::Delegate {
42 public:
43 MenuModel(message_center::MessageViewController* controller,
44 message_center::NotifierId notifier_id,
45 const string16& display_source);
46 virtual ~MenuModel();
47
48 // Overridden from ui::SimpleMenuModel::Delegate:
49 virtual bool IsItemForCommandIdDynamic(int command_id) const OVERRIDE;
50 virtual bool IsCommandIdChecked(int command_id) const OVERRIDE;
51 virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE;
52 virtual bool GetAcceleratorForCommandId(
53 int command_id,
54 ui::Accelerator* accelerator) OVERRIDE;
55 virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;
56
57 private:
58 message_center::MessageViewController* controller_;
59 message_center::NotifierId notifier_id_;
60 DISALLOW_COPY_AND_ASSIGN(MenuModel);
61 };
62
MenuModel(message_center::MessageViewController * controller,message_center::NotifierId notifier_id,const string16 & display_source)63 MenuModel::MenuModel(message_center::MessageViewController* controller,
64 message_center::NotifierId notifier_id,
65 const string16& display_source)
66 : ui::SimpleMenuModel(this),
67 controller_(controller),
68 notifier_id_(notifier_id) {
69 // Add 'disable notifications' menu item.
70 if (!display_source.empty()) {
71 AddItem(kTogglePermissionCommand,
72 l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_NOTIFIER_DISABLE,
73 display_source));
74 }
75 // Add settings menu item.
76 AddItem(kShowSettingsCommand,
77 l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_SETTINGS));
78 }
79
~MenuModel()80 MenuModel::~MenuModel() {
81 }
82
IsItemForCommandIdDynamic(int command_id) const83 bool MenuModel::IsItemForCommandIdDynamic(int command_id) const {
84 return false;
85 }
86
IsCommandIdChecked(int command_id) const87 bool MenuModel::IsCommandIdChecked(int command_id) const {
88 return false;
89 }
90
IsCommandIdEnabled(int command_id) const91 bool MenuModel::IsCommandIdEnabled(int command_id) const {
92 return true;
93 }
94
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator)95 bool MenuModel::GetAcceleratorForCommandId(int command_id,
96 ui::Accelerator* accelerator) {
97 return false;
98 }
99
ExecuteCommand(int command_id,int event_flags)100 void MenuModel::ExecuteCommand(int command_id, int event_flags) {
101 switch (command_id) {
102 case kTogglePermissionCommand:
103 controller_->DisableNotificationsFromThisSource(notifier_id_);
104 break;
105 case kShowSettingsCommand:
106 controller_->ShowNotifierSettingsBubble();
107 break;
108 default:
109 NOTREACHED();
110 }
111 }
112
113 } // namespace
114
115 namespace message_center {
116
117 class MessageViewContextMenuController : public views::ContextMenuController {
118 public:
119 MessageViewContextMenuController(MessageViewController* controller,
120 const NotifierId& notifier_id,
121 const string16& display_source);
122 virtual ~MessageViewContextMenuController();
123
124 protected:
125 // Overridden from views::ContextMenuController:
126 virtual void ShowContextMenuForView(views::View* source,
127 const gfx::Point& point,
128 ui::MenuSourceType source_type) OVERRIDE;
129
130 MessageViewController* controller_; // Weak, owns us.
131 NotifierId notifier_id_;
132 string16 display_source_;
133 };
134
MessageViewContextMenuController(MessageViewController * controller,const NotifierId & notifier_id,const string16 & display_source)135 MessageViewContextMenuController::MessageViewContextMenuController(
136 MessageViewController* controller,
137 const NotifierId& notifier_id,
138 const string16& display_source)
139 : controller_(controller),
140 notifier_id_(notifier_id),
141 display_source_(display_source) {
142 }
143
~MessageViewContextMenuController()144 MessageViewContextMenuController::~MessageViewContextMenuController() {
145 }
146
ShowContextMenuForView(views::View * source,const gfx::Point & point,ui::MenuSourceType source_type)147 void MessageViewContextMenuController::ShowContextMenuForView(
148 views::View* source,
149 const gfx::Point& point,
150 ui::MenuSourceType source_type) {
151 MenuModel menu_model(controller_, notifier_id_, display_source_);
152 if (menu_model.GetItemCount() == 0)
153 return;
154
155 views::MenuRunner menu_runner(&menu_model);
156
157 ignore_result(menu_runner.RunMenuAt(
158 source->GetWidget()->GetTopLevelWidget(),
159 NULL,
160 gfx::Rect(point, gfx::Size()),
161 views::MenuItemView::TOPRIGHT,
162 source_type,
163 views::MenuRunner::HAS_MNEMONICS));
164 }
165
MessageView(MessageViewController * controller,const std::string & notification_id,const NotifierId & notifier_id,const string16 & display_source)166 MessageView::MessageView(MessageViewController* controller,
167 const std::string& notification_id,
168 const NotifierId& notifier_id,
169 const string16& display_source)
170 : controller_(controller),
171 notification_id_(notification_id),
172 notifier_id_(notifier_id),
173 context_menu_controller_(
174 new MessageViewContextMenuController(controller,
175 notifier_id,
176 display_source)),
177 scroller_(NULL) {
178 SetFocusable(true);
179 set_context_menu_controller(context_menu_controller_.get());
180
181 PaddedButton *close = new PaddedButton(this);
182 close->SetPadding(-kCloseIconRightPadding, kCloseIconTopPadding);
183 close->SetNormalImage(IDR_NOTIFICATION_CLOSE);
184 close->SetHoveredImage(IDR_NOTIFICATION_CLOSE_HOVER);
185 close->SetPressedImage(IDR_NOTIFICATION_CLOSE_PRESSED);
186 close->set_owned_by_client();
187 close->set_animate_on_state_change(false);
188 close->SetAccessibleName(l10n_util::GetStringUTF16(
189 IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_ACCESSIBLE_NAME));
190 close_button_.reset(close);
191
192 focus_painter_ = views::Painter::CreateSolidFocusPainter(
193 kFocusBorderColor,
194 gfx::Insets(0, 1, 3, 2)).Pass();
195 }
196
~MessageView()197 MessageView::~MessageView() {
198 }
199
200 // static
GetShadowInsets()201 gfx::Insets MessageView::GetShadowInsets() {
202 return gfx::Insets(kShadowBlur / 2 - kShadowOffset,
203 kShadowBlur / 2,
204 kShadowBlur / 2 + kShadowOffset,
205 kShadowBlur / 2);
206 }
207
CreateShadowBorder()208 void MessageView::CreateShadowBorder() {
209 set_border(new views::ShadowBorder(kShadowBlur,
210 message_center::kShadowColor,
211 kShadowOffset, // Vertical offset.
212 0)); // Horizontal offset.
213 }
214
IsCloseButtonFocused()215 bool MessageView::IsCloseButtonFocused() {
216 views::FocusManager* focus_manager = GetFocusManager();
217 return focus_manager && focus_manager->GetFocusedView() == close_button();
218 }
219
RequestFocusOnCloseButton()220 void MessageView::RequestFocusOnCloseButton() {
221 close_button_->RequestFocus();
222 }
223
GetAccessibleState(ui::AccessibleViewState * state)224 void MessageView::GetAccessibleState(ui::AccessibleViewState* state) {
225 state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
226 state->name = accessible_name_;
227 }
228
OnMousePressed(const ui::MouseEvent & event)229 bool MessageView::OnMousePressed(const ui::MouseEvent& event) {
230 if (!event.IsOnlyLeftMouseButton())
231 return false;
232
233 controller_->ClickOnNotification(notification_id_);
234 return true;
235 }
236
OnKeyPressed(const ui::KeyEvent & event)237 bool MessageView::OnKeyPressed(const ui::KeyEvent& event) {
238 if (event.flags() != ui::EF_NONE)
239 return false;
240
241 if (event.key_code() == ui::VKEY_RETURN) {
242 controller_->ClickOnNotification(notification_id_);
243 return true;
244 } else if ((event.key_code() == ui::VKEY_DELETE ||
245 event.key_code() == ui::VKEY_BACK)) {
246 controller_->RemoveNotification(notification_id_, true); // By user.
247 return true;
248 }
249
250 return false;
251 }
252
OnKeyReleased(const ui::KeyEvent & event)253 bool MessageView::OnKeyReleased(const ui::KeyEvent& event) {
254 // Space key handling is triggerred at key-release timing. See
255 // ui/views/controls/buttons/custom_button.cc for why.
256 if (event.flags() != ui::EF_NONE || event.flags() != ui::VKEY_SPACE)
257 return false;
258
259 controller_->ClickOnNotification(notification_id_);
260 return true;
261 }
262
OnPaint(gfx::Canvas * canvas)263 void MessageView::OnPaint(gfx::Canvas* canvas) {
264 SlideOutView::OnPaint(canvas);
265 views::Painter::PaintFocusPainter(this, canvas, focus_painter_.get());
266 }
267
OnFocus()268 void MessageView::OnFocus() {
269 SlideOutView::OnFocus();
270 // We paint a focus indicator.
271 SchedulePaint();
272 }
273
OnBlur()274 void MessageView::OnBlur() {
275 SlideOutView::OnBlur();
276 // We paint a focus indicator.
277 SchedulePaint();
278 }
279
OnGestureEvent(ui::GestureEvent * event)280 void MessageView::OnGestureEvent(ui::GestureEvent* event) {
281 if (event->type() == ui::ET_GESTURE_TAP) {
282 controller_->ClickOnNotification(notification_id_);
283 event->SetHandled();
284 return;
285 }
286
287 SlideOutView::OnGestureEvent(event);
288 // Do not return here by checking handled(). SlideOutView calls SetHandled()
289 // even though the scroll gesture doesn't make no (or little) effects on the
290 // slide-out behavior. See http://crbug.com/172991
291
292 if (!event->IsScrollGestureEvent() && !event->IsFlingScrollEvent())
293 return;
294
295 if (scroller_)
296 scroller_->OnGestureEvent(event);
297 event->SetHandled();
298 }
299
ButtonPressed(views::Button * sender,const ui::Event & event)300 void MessageView::ButtonPressed(views::Button* sender,
301 const ui::Event& event) {
302 if (sender == close_button()) {
303 controller_->RemoveNotification(notification_id_, true); // By user.
304 }
305 }
306
OnSlideOut()307 void MessageView::OnSlideOut() {
308 controller_->RemoveNotification(notification_id_, true); // By user.
309 }
310
311 } // namespace message_center
312