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 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 80 MenuModel::~MenuModel() { 81 } 82 83 bool MenuModel::IsItemForCommandIdDynamic(int command_id) const { 84 return false; 85 } 86 87 bool MenuModel::IsCommandIdChecked(int command_id) const { 88 return false; 89 } 90 91 bool MenuModel::IsCommandIdEnabled(int command_id) const { 92 return true; 93 } 94 95 bool MenuModel::GetAcceleratorForCommandId(int command_id, 96 ui::Accelerator* accelerator) { 97 return false; 98 } 99 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 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 144 MessageViewContextMenuController::~MessageViewContextMenuController() { 145 } 146 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 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 197 MessageView::~MessageView() { 198 } 199 200 // static 201 gfx::Insets MessageView::GetShadowInsets() { 202 return gfx::Insets(kShadowBlur / 2 - kShadowOffset, 203 kShadowBlur / 2, 204 kShadowBlur / 2 + kShadowOffset, 205 kShadowBlur / 2); 206 } 207 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 215 bool MessageView::IsCloseButtonFocused() { 216 views::FocusManager* focus_manager = GetFocusManager(); 217 return focus_manager && focus_manager->GetFocusedView() == close_button(); 218 } 219 220 void MessageView::RequestFocusOnCloseButton() { 221 close_button_->RequestFocus(); 222 } 223 224 void MessageView::GetAccessibleState(ui::AccessibleViewState* state) { 225 state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON; 226 state->name = accessible_name_; 227 } 228 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 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 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 263 void MessageView::OnPaint(gfx::Canvas* canvas) { 264 SlideOutView::OnPaint(canvas); 265 views::Painter::PaintFocusPainter(this, canvas, focus_painter_.get()); 266 } 267 268 void MessageView::OnFocus() { 269 SlideOutView::OnFocus(); 270 // We paint a focus indicator. 271 SchedulePaint(); 272 } 273 274 void MessageView::OnBlur() { 275 SlideOutView::OnBlur(); 276 // We paint a focus indicator. 277 SchedulePaint(); 278 } 279 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 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 307 void MessageView::OnSlideOut() { 308 controller_->RemoveNotification(notification_id_, true); // By user. 309 } 310 311 } // namespace message_center 312