Correct blacklist entry message
[chromium-blink-merge.git] / ui / message_center / views / message_view.cc
blobe3199ccdb4bd805262bb56cbdd20613ca0c9f459
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.
5 #include "ui/message_center/views/message_view.h"
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_tray.h"
18 #include "ui/message_center/message_center_util.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/shadow_border.h"
24 #include "ui/views/widget/widget.h"
26 namespace {
28 const int kCloseIconTopPadding = 5;
29 const int kCloseIconRightPadding = 5;
30 const int kExpandIconBottomPadding = 8;
31 const int kExpandIconRightPadding = 11;
33 const int kShadowOffset = 1;
34 const int kShadowBlur = 4;
36 // Menu constants
37 const int kTogglePermissionCommand = 0;
38 const int kShowSettingsCommand = 1;
40 // ControlButtons are ImageButtons whose image can be padded within the button.
41 // This allows the creation of buttons like the notification close and expand
42 // buttons whose clickable areas extends beyond their image areas
43 // (<http://crbug.com/168822>) without the need to create and maintain
44 // corresponding resource images with alpha padding. In the future, this class
45 // will also allow for buttons whose touch areas extend beyond their clickable
46 // area (<http://crbug.com/168856>).
47 class ControlButton : public views::ImageButton {
48 public:
49 ControlButton(views::ButtonListener* listener);
50 virtual ~ControlButton();
52 // Overridden from views::ImageButton:
53 virtual gfx::Size GetPreferredSize() OVERRIDE;
54 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
55 virtual void OnFocus() OVERRIDE;
56 virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE;
58 // The SetPadding() method also sets the button's image alignment (positive
59 // values yield left/top alignments, negative values yield right/bottom ones,
60 // and zero values center/middle ones). ImageButton::SetImageAlignment() calls
61 // will not affect ControlButton image alignments.
62 void SetPadding(int horizontal_padding, int vertical_padding);
64 void SetNormalImage(int resource_id);
65 void SetHoveredImage(int resource_id);
66 void SetPressedImage(int resource_id);
68 protected:
69 gfx::Point ComputePaddedImagePaintPosition(const gfx::ImageSkia& image);
71 private:
72 gfx::Insets padding_;
74 DISALLOW_COPY_AND_ASSIGN(ControlButton);
77 ControlButton::ControlButton(views::ButtonListener* listener)
78 : views::ImageButton(listener) {
79 set_focusable(true);
80 set_request_focus_on_press(false);
83 ControlButton::~ControlButton() {
86 void ControlButton::SetPadding(int horizontal_padding, int vertical_padding) {
87 padding_.Set(std::max(vertical_padding, 0),
88 std::max(horizontal_padding, 0),
89 std::max(-vertical_padding, 0),
90 std::max(-horizontal_padding, 0));
93 void ControlButton::SetNormalImage(int resource_id) {
94 SetImage(views::CustomButton::STATE_NORMAL,
95 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
96 resource_id));
99 void ControlButton::SetHoveredImage(int resource_id) {
100 SetImage(views::CustomButton::STATE_HOVERED,
101 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
102 resource_id));
105 void ControlButton::SetPressedImage(int resource_id) {
106 SetImage(views::CustomButton::STATE_PRESSED,
107 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
108 resource_id));
111 gfx::Size ControlButton::GetPreferredSize() {
112 return gfx::Size(message_center::kControlButtonSize,
113 message_center::kControlButtonSize);
116 void ControlButton::OnPaint(gfx::Canvas* canvas) {
117 // This is the same implementation as ImageButton::OnPaint except
118 // that it calls ComputePaddedImagePaintPosition() instead of
119 // ComputeImagePaintPosition(), in effect overriding that private method.
120 View::OnPaint(canvas);
121 gfx::ImageSkia image = GetImageToPaint();
122 if (!image.isNull()) {
123 gfx::Point position = ComputePaddedImagePaintPosition(image);
124 if (!background_image_.isNull())
125 canvas->DrawImageInt(background_image_, position.x(), position.y());
126 canvas->DrawImageInt(image, position.x(), position.y());
127 if (!overlay_image_.isNull())
128 canvas->DrawImageInt(overlay_image_, position.x(), position.y());
130 OnPaintFocusBorder(canvas);
133 void ControlButton::OnFocus() {
134 views::ImageButton::OnFocus();
135 ScrollRectToVisible(GetLocalBounds());
138 void ControlButton::OnPaintFocusBorder(gfx::Canvas* canvas) {
139 if (HasFocus() && (focusable() || IsAccessibilityFocusable())) {
140 canvas->DrawRect(gfx::Rect(2, 1, width() - 4, height() - 3),
141 message_center::kFocusBorderColor);
145 gfx::Point ControlButton::ComputePaddedImagePaintPosition(
146 const gfx::ImageSkia& image) {
147 gfx::Vector2d offset;
148 gfx::Rect bounds = GetContentsBounds();
149 bounds.Inset(padding_);
151 if (padding_.left() == 0 && padding_.right() == 0)
152 offset.set_x((bounds.width() - image.width()) / 2); // Center align.
153 else if (padding_.right() > 0)
154 offset.set_x(bounds.width() - image.width()); // Right align.
156 if (padding_.top() == 0 && padding_.bottom() == 0)
157 offset.set_y((bounds.height() - image.height()) / 2); // Middle align.
158 else if (padding_.bottom() > 0)
159 offset.set_y(bounds.height() - image.height()); // Bottom align.
161 return bounds.origin() + offset;
164 // A dropdown menu for notifications.
165 class MenuModel : public ui::SimpleMenuModel,
166 public ui::SimpleMenuModel::Delegate {
167 public:
168 MenuModel(message_center::MessageCenter* message_center,
169 message_center::MessageCenterTray* tray,
170 const std::string& notification_id,
171 const string16& display_source,
172 const message_center::NotifierId& notifier_id);
173 virtual ~MenuModel();
175 // Overridden from ui::SimpleMenuModel::Delegate:
176 virtual bool IsItemForCommandIdDynamic(int command_id) const OVERRIDE;
177 virtual bool IsCommandIdChecked(int command_id) const OVERRIDE;
178 virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE;
179 virtual bool GetAcceleratorForCommandId(
180 int command_id,
181 ui::Accelerator* accelerator) OVERRIDE;
182 virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE;
184 private:
185 message_center::MessageCenter* message_center_; // Weak reference.
186 message_center::MessageCenterTray* tray_; // Weak reference.
187 std::string notification_id_;
188 message_center::NotifierId notifier_id_;
190 DISALLOW_COPY_AND_ASSIGN(MenuModel);
193 MenuModel::MenuModel(message_center::MessageCenter* message_center,
194 message_center::MessageCenterTray* tray,
195 const std::string& notification_id,
196 const string16& display_source,
197 const message_center::NotifierId& notifier_id)
198 : ui::SimpleMenuModel(this),
199 message_center_(message_center),
200 tray_(tray),
201 notification_id_(notification_id),
202 notifier_id_(notifier_id) {
203 // Add 'disable notifications' menu item.
204 if (!display_source.empty()) {
205 AddItem(kTogglePermissionCommand,
206 l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_NOTIFIER_DISABLE,
207 display_source));
209 // Add settings menu item.
210 AddItem(kShowSettingsCommand,
211 l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_SETTINGS));
214 MenuModel::~MenuModel() {
217 bool MenuModel::IsItemForCommandIdDynamic(int command_id) const {
218 return false;
221 bool MenuModel::IsCommandIdChecked(int command_id) const {
222 return false;
225 bool MenuModel::IsCommandIdEnabled(int command_id) const {
226 return true;
229 bool MenuModel::GetAcceleratorForCommandId(int command_id,
230 ui::Accelerator* accelerator) {
231 return false;
234 void MenuModel::ExecuteCommand(int command_id, int event_flags) {
235 switch (command_id) {
236 case kTogglePermissionCommand:
237 message_center_->DisableNotificationsByNotifier(notifier_id_);
238 break;
239 case kShowSettingsCommand:
240 // |tray_| may be NULL in tests.
241 if (tray_)
242 tray_->ShowNotifierSettingsBubble();
243 break;
244 default:
245 NOTREACHED();
249 } // namespace
251 namespace message_center {
253 class MessageViewContextMenuController : public views::ContextMenuController {
254 public:
255 MessageViewContextMenuController(
256 MessageCenter* message_center,
257 MessageCenterTray* tray,
258 const Notification& notification);
259 virtual ~MessageViewContextMenuController();
261 protected:
262 // Overridden from views::ContextMenuController:
263 virtual void ShowContextMenuForView(views::View* source,
264 const gfx::Point& point,
265 ui::MenuSourceType source_type) OVERRIDE;
267 MessageCenter* message_center_; // Weak reference.
268 MessageCenterTray* tray_; // Weak reference.
269 std::string notification_id_;
270 string16 display_source_;
271 NotifierId notifier_id_;
274 MessageViewContextMenuController::MessageViewContextMenuController(
275 MessageCenter* message_center,
276 MessageCenterTray* tray,
277 const Notification& notification)
278 : message_center_(message_center),
279 tray_(tray),
280 notification_id_(notification.id()),
281 display_source_(notification.display_source()),
282 notifier_id_(notification.notifier_id()) {
285 MessageViewContextMenuController::~MessageViewContextMenuController() {
288 void MessageViewContextMenuController::ShowContextMenuForView(
289 views::View* source,
290 const gfx::Point& point,
291 ui::MenuSourceType source_type) {
292 MenuModel menu_model(message_center_, tray_, notification_id_,
293 display_source_, notifier_id_);
294 if (menu_model.GetItemCount() == 0)
295 return;
297 views::MenuRunner menu_runner(&menu_model);
299 ignore_result(menu_runner.RunMenuAt(
300 source->GetWidget()->GetTopLevelWidget(),
301 NULL,
302 gfx::Rect(point, gfx::Size()),
303 views::MenuItemView::TOPRIGHT,
304 source_type,
305 views::MenuRunner::HAS_MNEMONICS));
308 MessageView::MessageView(const Notification& notification,
309 MessageCenter* message_center,
310 MessageCenterTray* tray,
311 bool expanded)
312 : message_center_(message_center),
313 notification_id_(notification.id()),
314 context_menu_controller_(new MessageViewContextMenuController(
315 message_center, tray, notification)),
316 scroller_(NULL),
317 is_expanded_(expanded) {
318 set_focusable(true);
319 set_context_menu_controller(context_menu_controller_.get());
321 ControlButton *close = new ControlButton(this);
322 close->SetPadding(-kCloseIconRightPadding, kCloseIconTopPadding);
323 close->SetNormalImage(IDR_NOTIFICATION_CLOSE);
324 close->SetHoveredImage(IDR_NOTIFICATION_CLOSE_HOVER);
325 close->SetPressedImage(IDR_NOTIFICATION_CLOSE_PRESSED);
326 close->set_owned_by_client();
327 close->set_animate_on_state_change(false);
328 close->SetAccessibleName(l10n_util::GetStringUTF16(
329 IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_ACCESSIBLE_NAME));
330 close_button_.reset(close);
332 ControlButton *expand = new ControlButton(this);
333 expand->SetPadding(-kExpandIconRightPadding, -kExpandIconBottomPadding);
334 expand->SetNormalImage(IDR_NOTIFICATION_EXPAND);
335 expand->SetHoveredImage(IDR_NOTIFICATION_EXPAND_HOVER);
336 expand->SetPressedImage(IDR_NOTIFICATION_EXPAND_PRESSED);
337 expand->set_owned_by_client();
338 expand->set_animate_on_state_change(false);
339 expand->SetAccessibleName(l10n_util::GetStringUTF16(
340 IDS_MESSAGE_CENTER_EXPAND_NOTIFICATION_BUTTON_ACCESSIBLE_NAME));
341 expand_button_.reset(expand);
344 MessageView::MessageView() {
347 MessageView::~MessageView() {
350 // static
351 gfx::Insets MessageView::GetShadowInsets() {
352 return gfx::Insets(kShadowBlur / 2 - kShadowOffset,
353 kShadowBlur / 2,
354 kShadowBlur / 2 + kShadowOffset,
355 kShadowBlur / 2);
358 void MessageView::CreateShadowBorder() {
359 set_border(new views::ShadowBorder(kShadowBlur,
360 message_center::kShadowColor,
361 kShadowOffset, // Vertical offset.
362 0)); // Horizontal offset.
365 bool MessageView::IsCloseButtonFocused() {
366 views::FocusManager* focus_manager = GetFocusManager();
367 return focus_manager && focus_manager->GetFocusedView() == close_button();
370 void MessageView::RequestFocusOnCloseButton() {
371 close_button_->RequestFocus();
374 void MessageView::GetAccessibleState(ui::AccessibleViewState* state) {
375 state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
376 state->name = accessible_name_;
379 bool MessageView::OnMousePressed(const ui::MouseEvent& event) {
380 if (event.IsOnlyLeftMouseButton()) {
381 message_center_->ClickOnNotification(notification_id_);
382 return true;
384 return false;
387 bool MessageView::OnKeyPressed(const ui::KeyEvent& event) {
388 if (event.flags() != ui::EF_NONE)
389 return false;
391 if (event.key_code() == ui::VKEY_RETURN) {
392 message_center_->ClickOnNotification(notification_id_);
393 return true;
394 } else if ((event.key_code() == ui::VKEY_DELETE ||
395 event.key_code() == ui::VKEY_BACK)) {
396 message_center_->RemoveNotification(notification_id_, true); // By user.
397 return true;
400 return false;
403 bool MessageView::OnKeyReleased(const ui::KeyEvent& event) {
404 // Space key handling is triggerred at key-release timing. See
405 // ui/views/controls/buttons/custom_button.cc for why.
406 if (event.flags() != ui::EF_NONE || event.flags() != ui::VKEY_SPACE)
407 return false;
409 message_center_->ClickOnNotification(notification_id_);
410 return true;
413 void MessageView::OnGestureEvent(ui::GestureEvent* event) {
414 if (event->type() == ui::ET_GESTURE_TAP) {
415 message_center_->ClickOnNotification(notification_id_);
416 event->SetHandled();
417 return;
420 SlideOutView::OnGestureEvent(event);
421 // Do not return here by checking handled(). SlideOutView calls SetHandled()
422 // even though the scroll gesture doesn't make no (or little) effects on the
423 // slide-out behavior. See http://crbug.com/172991
425 if (!event->IsScrollGestureEvent() && !event->IsFlingScrollEvent())
426 return;
428 if (scroller_)
429 scroller_->OnGestureEvent(event);
430 event->SetHandled();
433 void MessageView::OnPaintFocusBorder(gfx::Canvas* canvas) {
434 if (HasFocus()) {
435 canvas->DrawRect(gfx::Rect(1, 0, width() - 2, height() - 2),
436 message_center::kFocusBorderColor);
440 void MessageView::ButtonPressed(views::Button* sender,
441 const ui::Event& event) {
442 if (sender == close_button()) {
443 message_center_->RemoveNotification(notification_id_, true); // By user.
444 } else if (sender == expand_button()) {
445 is_expanded_ = true;
446 message_center_->ExpandNotification(notification_id_);
450 void MessageView::OnSlideOut() {
451 message_center_->RemoveNotification(notification_id_, true); // By user.
454 } // namespace message_center