Add a minor text member to ui::MenuModel.
[chromium-blink-merge.git] / chrome / browser / ui / views / message_center / web_notification_tray.cc
blobac31aa9198088f5cf556b29db1995973b3c30814
1 // Copyright 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.
5 #include "chrome/browser/ui/views/message_center/web_notification_tray.h"
7 #include "base/i18n/number_formatting.h"
8 #include "base/strings/string16.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/status_icons/status_icon.h"
12 #include "chrome/browser/status_icons/status_icon_menu_model.h"
13 #include "chrome/browser/status_icons/status_tray.h"
14 #include "content/public/browser/notification_service.h"
15 #include "content/public/browser/user_metrics.h"
16 #include "grit/chromium_strings.h"
17 #include "grit/theme_resources.h"
18 #include "grit/ui_strings.h"
19 #include "ui/base/l10n/l10n_util.h"
20 #include "ui/base/resource/resource_bundle.h"
21 #include "ui/gfx/canvas.h"
22 #include "ui/gfx/image/image_skia_operations.h"
23 #include "ui/gfx/rect.h"
24 #include "ui/gfx/screen.h"
25 #include "ui/gfx/size.h"
26 #include "ui/message_center/message_center_tray.h"
27 #include "ui/message_center/message_center_tray_delegate.h"
28 #include "ui/message_center/views/message_popup_collection.h"
29 #include "ui/views/widget/widget.h"
31 namespace {
33 // Tray constants
34 const int kScreenEdgePadding = 2;
36 const int kSystemTrayWidth = 16;
37 const int kSystemTrayHeight = 16;
38 const int kNumberOfSystemTraySprites = 10;
40 // Number of pixels the message center is offset from the mouse.
41 const int kMouseOffset = 5;
43 // Menu commands
44 const int kToggleQuietMode = 0;
45 const int kEnableQuietModeHour = 1;
46 const int kEnableQuietModeDay = 2;
48 gfx::ImageSkia* GetIcon(int unread_count, bool is_quiet_mode) {
49 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
50 int resource_id = IDR_NOTIFICATION_TRAY_EMPTY;
52 if (unread_count) {
53 if (is_quiet_mode)
54 resource_id = IDR_NOTIFICATION_TRAY_DO_NOT_DISTURB_ATTENTION;
55 else
56 resource_id = IDR_NOTIFICATION_TRAY_ATTENTION;
57 } else if (is_quiet_mode) {
58 resource_id = IDR_NOTIFICATION_TRAY_DO_NOT_DISTURB_EMPTY;
61 return rb.GetImageSkiaNamed(resource_id);
64 } // namespace
66 using content::UserMetricsAction;
68 namespace message_center {
70 namespace internal {
72 // Gets the position of the taskbar from the work area bounds. Returns
73 // ALIGNMENT_NONE if position cannot be found.
74 Alignment GetTaskbarAlignment() {
75 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
76 // TODO(dewittj): It's possible GetPrimaryDisplay is wrong.
77 gfx::Rect screen_bounds = screen->GetPrimaryDisplay().bounds();
78 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
79 work_area.Inset(kScreenEdgePadding, kScreenEdgePadding);
81 // Comparing the work area to the screen bounds gives us the location of the
82 // taskbar. If the work area is exactly the same as the screen bounds,
83 // we are unable to locate the taskbar so we say we don't know it's alignment.
84 if (work_area.height() < screen_bounds.height()) {
85 if (work_area.y() > screen_bounds.y())
86 return ALIGNMENT_TOP;
87 return ALIGNMENT_BOTTOM;
89 if (work_area.width() < screen_bounds.width()) {
90 if (work_area.x() > screen_bounds.x())
91 return ALIGNMENT_LEFT;
92 return ALIGNMENT_RIGHT;
95 return ALIGNMENT_NONE;
98 gfx::Point GetClosestCorner(const gfx::Rect& rect, const gfx::Point& query) {
99 gfx::Point center_point = rect.CenterPoint();
100 gfx::Point rv;
102 if (query.x() > center_point.x())
103 rv.set_x(rect.right());
104 else
105 rv.set_x(rect.x());
107 if (query.y() > center_point.y())
108 rv.set_y(rect.bottom());
109 else
110 rv.set_y(rect.y());
112 return rv;
115 // Gets the corner of the screen where the message center should pop up.
116 Alignment GetAnchorAlignment(const gfx::Rect& work_area, gfx::Point corner) {
117 gfx::Point center = work_area.CenterPoint();
119 Alignment anchor_alignment =
120 center.y() > corner.y() ? ALIGNMENT_TOP : ALIGNMENT_BOTTOM;
121 anchor_alignment =
122 (Alignment)(anchor_alignment |
123 (center.x() > corner.x() ? ALIGNMENT_LEFT : ALIGNMENT_RIGHT));
125 return anchor_alignment;
128 } // namespace internal
130 MessageCenterTrayDelegate* CreateMessageCenterTray() {
131 return new WebNotificationTray();
134 WebNotificationTray::WebNotificationTray()
135 : message_center_delegate_(NULL),
136 status_icon_(NULL),
137 status_icon_menu_(NULL),
138 message_center_visible_(false),
139 should_update_tray_content_(true) {
140 message_center_tray_.reset(
141 new MessageCenterTray(this, g_browser_process->message_center()));
142 last_quiet_mode_state_ = message_center()->IsQuietMode();
145 WebNotificationTray::~WebNotificationTray() {
146 // Reset this early so that delegated events during destruction don't cause
147 // problems.
148 message_center_tray_.reset();
149 DestroyStatusIcon();
152 message_center::MessageCenter* WebNotificationTray::message_center() {
153 return message_center_tray_->message_center();
156 bool WebNotificationTray::ShowPopups() {
157 popup_collection_.reset(new message_center::MessagePopupCollection(
158 NULL, message_center(), message_center_tray_.get(), false));
159 return true;
162 void WebNotificationTray::HidePopups() { popup_collection_.reset(); }
164 bool WebNotificationTray::ShowMessageCenterInternal(bool show_settings) {
165 content::RecordAction(UserMetricsAction("Notifications.ShowMessageCenter"));
167 // Message center delegate will be set to NULL when the message center
168 // widget's Close method is called so we don't need to worry about
169 // use-after-free issues.
170 message_center_delegate_ = new MessageCenterWidgetDelegate(
171 this,
172 message_center_tray_.get(),
173 show_settings, // settings initally invisible
174 GetPositionInfo());
176 return true;
179 bool WebNotificationTray::ShowMessageCenter() {
180 return ShowMessageCenterInternal(/*show_settings =*/false);
183 void WebNotificationTray::HideMessageCenter() {
184 if (message_center_delegate_) {
185 views::Widget* widget = message_center_delegate_->GetWidget();
186 if (widget)
187 widget->Close();
191 bool WebNotificationTray::ShowNotifierSettings() {
192 if (message_center_delegate_) {
193 message_center_delegate_->SetSettingsVisible(true);
194 return true;
196 return ShowMessageCenterInternal(/*show_settings =*/true);
199 void WebNotificationTray::OnMessageCenterTrayChanged() {
200 if (status_icon_) {
201 bool quiet_mode_state = message_center()->IsQuietMode();
202 if (last_quiet_mode_state_ != quiet_mode_state) {
203 last_quiet_mode_state_ = quiet_mode_state;
205 // Quiet mode has changed, update the quiet mode menu.
206 status_icon_menu_->SetCommandIdChecked(kToggleQuietMode,
207 quiet_mode_state);
211 // See the comments in ash/system/web_notification/web_notification_tray.cc
212 // for why PostTask.
213 should_update_tray_content_ = true;
214 base::MessageLoop::current()->PostTask(
215 FROM_HERE,
216 base::Bind(&WebNotificationTray::UpdateStatusIcon, AsWeakPtr()));
219 void WebNotificationTray::OnStatusIconClicked() {
220 // TODO(dewittj): It's possible GetNativeScreen is wrong for win-aura.
221 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
222 mouse_click_point_ = screen->GetCursorScreenPoint();
223 message_center_tray_->ToggleMessageCenterBubble();
226 void WebNotificationTray::ExecuteCommand(int command_id, int event_flags) {
227 if (command_id == kToggleQuietMode) {
228 bool in_quiet_mode = message_center()->IsQuietMode();
229 message_center()->SetQuietMode(!in_quiet_mode);
230 return;
232 base::TimeDelta expires_in = command_id == kEnableQuietModeDay
233 ? base::TimeDelta::FromDays(1)
234 : base::TimeDelta::FromHours(1);
235 message_center()->EnterQuietModeWithExpire(expires_in);
238 void WebNotificationTray::UpdateStatusIcon() {
239 if (!should_update_tray_content_)
240 return;
241 should_update_tray_content_ = false;
243 int unread_notifications = message_center()->UnreadNotificationCount();
245 string16 tool_tip;
246 if (unread_notifications > 0) {
247 string16 str_unread_count = base::FormatNumber(unread_notifications);
248 tool_tip = l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_TOOLTIP_UNREAD,
249 str_unread_count);
250 } else {
251 tool_tip = l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_TOOLTIP);
254 gfx::ImageSkia* icon_image = GetIcon(
255 unread_notifications,
256 message_center()->IsQuietMode());
258 if (status_icon_) {
259 status_icon_->SetImage(*icon_image);
260 status_icon_->SetToolTip(tool_tip);
261 return;
264 CreateStatusIcon(*icon_image, tool_tip);
267 void WebNotificationTray::SendHideMessageCenter() {
268 message_center_tray_->HideMessageCenterBubble();
271 void WebNotificationTray::MarkMessageCenterHidden() {
272 if (message_center_delegate_) {
273 message_center_tray_->MarkMessageCenterHidden();
274 message_center_delegate_ = NULL;
278 PositionInfo WebNotificationTray::GetPositionInfo() {
279 PositionInfo pos_info;
281 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
282 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
283 work_area.Inset(kScreenEdgePadding, kScreenEdgePadding);
285 gfx::Point corner = internal::GetClosestCorner(work_area, mouse_click_point_);
287 pos_info.taskbar_alignment = internal::GetTaskbarAlignment();
289 // We assume the taskbar is either at the top or at the bottom if we are not
290 // able to find it.
291 if (pos_info.taskbar_alignment == ALIGNMENT_NONE) {
292 if (mouse_click_point_.y() > corner.y())
293 pos_info.taskbar_alignment = ALIGNMENT_TOP;
294 else
295 pos_info.taskbar_alignment = ALIGNMENT_BOTTOM;
298 pos_info.message_center_alignment =
299 internal::GetAnchorAlignment(work_area, corner);
301 pos_info.inital_anchor_point = corner;
302 pos_info.max_height = work_area.height();
304 if (work_area.Contains(mouse_click_point_)) {
305 // Message center is in the work area. So position it few pixels above the
306 // mouse click point if alignemnt is towards bottom and few pixels below if
307 // alignment is towards top.
308 pos_info.inital_anchor_point.set_y(
309 mouse_click_point_.y() +
310 (pos_info.message_center_alignment & ALIGNMENT_BOTTOM ? -kMouseOffset
311 : kMouseOffset));
313 // Subtract the distance between mouse click point and the closest
314 // (insetted) edge from the max height to show the message center within the
315 // (insetted) work area bounds. Also subtract the offset from the mouse
316 // click point we added earlier.
317 pos_info.max_height -=
318 std::abs(mouse_click_point_.y() - corner.y()) + kMouseOffset;
320 return pos_info;
323 MessageCenterTray* WebNotificationTray::GetMessageCenterTray() {
324 return message_center_tray_.get();
327 void WebNotificationTray::CreateStatusIcon(const gfx::ImageSkia& image,
328 const string16& tool_tip) {
329 if (status_icon_)
330 return;
332 StatusTray* status_tray = g_browser_process->status_tray();
333 if (!status_tray)
334 return;
336 status_icon_ = status_tray->CreateStatusIcon(
337 StatusTray::NOTIFICATION_TRAY_ICON, image, tool_tip);
338 if (!status_icon_)
339 return;
341 status_icon_->AddObserver(this);
342 AddQuietModeMenu(status_icon_);
345 void WebNotificationTray::DestroyStatusIcon() {
346 if (!status_icon_)
347 return;
349 status_icon_->RemoveObserver(this);
350 StatusTray* status_tray = g_browser_process->status_tray();
351 if (status_tray)
352 status_tray->RemoveStatusIcon(status_icon_);
353 status_icon_menu_ = NULL;
354 status_icon_ = NULL;
357 void WebNotificationTray::AddQuietModeMenu(StatusIcon* status_icon) {
358 DCHECK(status_icon);
360 scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(this));
361 menu->AddCheckItem(kToggleQuietMode,
362 l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_QUIET_MODE));
363 menu->SetCommandIdChecked(kToggleQuietMode, message_center()->IsQuietMode());
364 menu->AddItem(kEnableQuietModeHour,
365 l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_QUIET_MODE_1HOUR));
366 menu->AddItem(kEnableQuietModeDay,
367 l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_QUIET_MODE_1DAY));
369 status_icon_menu_ = menu.get();
370 status_icon->SetContextMenu(menu.Pass());
373 MessageCenterWidgetDelegate*
374 WebNotificationTray::GetMessageCenterWidgetDelegateForTest() {
375 return message_center_delegate_;
378 } // namespace message_center