Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / views / message_center / web_notification_tray.cc
blob8cab3faddd05c7013c199b228514f77a0a5ba9c8
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 "grit/chromium_strings.h"
16 #include "grit/theme_resources.h"
17 #include "grit/ui_strings.h"
18 #include "ui/base/l10n/l10n_util.h"
19 #include "ui/base/resource/resource_bundle.h"
20 #include "ui/gfx/canvas.h"
21 #include "ui/gfx/image/image_skia_operations.h"
22 #include "ui/gfx/rect.h"
23 #include "ui/gfx/screen.h"
24 #include "ui/gfx/size.h"
25 #include "ui/message_center/message_center_tray.h"
26 #include "ui/message_center/message_center_tray_delegate.h"
27 #include "ui/message_center/views/message_popup_collection.h"
28 #include "ui/views/widget/widget.h"
30 namespace {
32 // Tray constants
33 const int kScreenEdgePadding = 2;
35 // Number of pixels the message center is offset from the mouse.
36 const int kMouseOffset = 5;
38 // Menu commands
39 const int kToggleQuietMode = 0;
40 const int kEnableQuietModeHour = 1;
41 const int kEnableQuietModeDay = 2;
43 gfx::ImageSkia* GetIcon(int unread_count, bool is_quiet_mode) {
44 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
45 int resource_id = IDR_NOTIFICATION_TRAY_EMPTY;
47 if (unread_count) {
48 if (is_quiet_mode)
49 resource_id = IDR_NOTIFICATION_TRAY_DO_NOT_DISTURB_ATTENTION;
50 else
51 resource_id = IDR_NOTIFICATION_TRAY_ATTENTION;
52 } else if (is_quiet_mode) {
53 resource_id = IDR_NOTIFICATION_TRAY_DO_NOT_DISTURB_EMPTY;
56 return rb.GetImageSkiaNamed(resource_id);
59 } // namespace
61 namespace message_center {
63 namespace internal {
65 // Gets the position of the taskbar from the work area bounds. Returns
66 // ALIGNMENT_NONE if position cannot be found.
67 Alignment GetTaskbarAlignment() {
68 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
69 // TODO(dewittj): It's possible GetPrimaryDisplay is wrong.
70 gfx::Rect screen_bounds = screen->GetPrimaryDisplay().bounds();
71 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
72 work_area.Inset(kScreenEdgePadding, kScreenEdgePadding);
74 // Comparing the work area to the screen bounds gives us the location of the
75 // taskbar. If the work area is exactly the same as the screen bounds,
76 // we are unable to locate the taskbar so we say we don't know it's alignment.
77 if (work_area.height() < screen_bounds.height()) {
78 if (work_area.y() > screen_bounds.y())
79 return ALIGNMENT_TOP;
80 return ALIGNMENT_BOTTOM;
82 if (work_area.width() < screen_bounds.width()) {
83 if (work_area.x() > screen_bounds.x())
84 return ALIGNMENT_LEFT;
85 return ALIGNMENT_RIGHT;
88 return ALIGNMENT_NONE;
91 gfx::Point GetClosestCorner(const gfx::Rect& rect, const gfx::Point& query) {
92 gfx::Point center_point = rect.CenterPoint();
93 gfx::Point rv;
95 if (query.x() > center_point.x())
96 rv.set_x(rect.right());
97 else
98 rv.set_x(rect.x());
100 if (query.y() > center_point.y())
101 rv.set_y(rect.bottom());
102 else
103 rv.set_y(rect.y());
105 return rv;
108 // Gets the corner of the screen where the message center should pop up.
109 Alignment GetAnchorAlignment(const gfx::Rect& work_area, gfx::Point corner) {
110 gfx::Point center = work_area.CenterPoint();
112 Alignment anchor_alignment =
113 center.y() > corner.y() ? ALIGNMENT_TOP : ALIGNMENT_BOTTOM;
114 anchor_alignment =
115 (Alignment)(anchor_alignment |
116 (center.x() > corner.x() ? ALIGNMENT_LEFT : ALIGNMENT_RIGHT));
118 return anchor_alignment;
121 } // namespace internal
123 MessageCenterTrayDelegate* CreateMessageCenterTray() {
124 return new WebNotificationTray();
127 WebNotificationTray::WebNotificationTray()
128 : message_center_delegate_(NULL),
129 status_icon_(NULL),
130 status_icon_menu_(NULL),
131 message_center_visible_(false),
132 should_update_tray_content_(true) {
133 message_center_tray_.reset(
134 new MessageCenterTray(this, g_browser_process->message_center()));
135 last_quiet_mode_state_ = message_center()->IsQuietMode();
138 WebNotificationTray::~WebNotificationTray() {
139 // Reset this early so that delegated events during destruction don't cause
140 // problems.
141 message_center_tray_.reset();
142 DestroyStatusIcon();
145 message_center::MessageCenter* WebNotificationTray::message_center() {
146 return message_center_tray_->message_center();
149 bool WebNotificationTray::ShowPopups() {
150 popup_collection_.reset(new message_center::MessagePopupCollection(
151 NULL, message_center(), message_center_tray_.get(), false));
152 return true;
155 void WebNotificationTray::HidePopups() {
156 DCHECK(popup_collection_.get());
158 popup_collection_->MarkAllPopupsShown();
159 popup_collection_.reset();
162 bool WebNotificationTray::ShowMessageCenter() {
163 message_center_delegate_ =
164 new MessageCenterWidgetDelegate(this,
165 message_center_tray_.get(),
166 false, // settings initally invisible
167 GetPositionInfo());
169 return true;
172 void WebNotificationTray::HideMessageCenter() {
173 if (message_center_delegate_) {
174 views::Widget* widget = message_center_delegate_->GetWidget();
175 if (widget)
176 widget->Close();
180 bool WebNotificationTray::ShowNotifierSettings() {
181 if (message_center_delegate_) {
182 message_center_delegate_->SetSettingsVisible(true);
183 return true;
185 message_center_delegate_ =
186 new MessageCenterWidgetDelegate(this,
187 message_center_tray_.get(),
188 true, // settings initally visible
189 GetPositionInfo());
191 return true;
194 bool WebNotificationTray::IsContextMenuEnabled() const {
195 // It can always return true because the notifications are invisible if
196 // the context menu shouldn't be enabled, such as in the lock screen.
197 return true;
200 void WebNotificationTray::OnMessageCenterTrayChanged() {
201 if (status_icon_) {
202 bool quiet_mode_state = message_center()->IsQuietMode();
203 if (last_quiet_mode_state_ != quiet_mode_state) {
204 last_quiet_mode_state_ = quiet_mode_state;
206 // Quiet mode has changed, update the quiet mode menu.
207 status_icon_menu_->SetCommandIdChecked(kToggleQuietMode,
208 quiet_mode_state);
212 // See the comments in ash/system/web_notification/web_notification_tray.cc
213 // for why PostTask.
214 should_update_tray_content_ = true;
215 base::MessageLoop::current()->PostTask(
216 FROM_HERE,
217 base::Bind(&WebNotificationTray::UpdateStatusIcon, AsWeakPtr()));
220 void WebNotificationTray::OnStatusIconClicked() {
221 // TODO(dewittj): It's possible GetNativeScreen is wrong for win-aura.
222 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
223 mouse_click_point_ = screen->GetCursorScreenPoint();
224 message_center_tray_->ToggleMessageCenterBubble();
227 void WebNotificationTray::ExecuteCommand(int command_id, int event_flags) {
228 if (command_id == kToggleQuietMode) {
229 bool in_quiet_mode = message_center()->IsQuietMode();
230 message_center()->SetQuietMode(!in_quiet_mode);
231 return;
233 base::TimeDelta expires_in = command_id == kEnableQuietModeDay
234 ? base::TimeDelta::FromDays(1)
235 : base::TimeDelta::FromHours(1);
236 message_center()->EnterQuietModeWithExpire(expires_in);
239 void WebNotificationTray::UpdateStatusIcon() {
240 if (!should_update_tray_content_)
241 return;
242 should_update_tray_content_ = false;
244 int unread_notifications = message_center()->UnreadNotificationCount();
246 base::string16 tool_tip;
247 if (unread_notifications > 0) {
248 base::string16 str_unread_count = base::FormatNumber(unread_notifications);
249 tool_tip = l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_TOOLTIP_UNREAD,
250 str_unread_count);
251 } else {
252 tool_tip = l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_TOOLTIP);
255 gfx::ImageSkia* icon_image = GetIcon(
256 unread_notifications,
257 message_center()->IsQuietMode());
259 if (status_icon_) {
260 status_icon_->SetImage(*icon_image);
261 status_icon_->SetToolTip(tool_tip);
262 return;
265 CreateStatusIcon(*icon_image, tool_tip);
268 void WebNotificationTray::SendHideMessageCenter() {
269 message_center_tray_->HideMessageCenterBubble();
272 void WebNotificationTray::MarkMessageCenterHidden() {
273 if (message_center_delegate_) {
274 message_center_tray_->MarkMessageCenterHidden();
275 message_center_delegate_ = NULL;
279 PositionInfo WebNotificationTray::GetPositionInfo() {
280 PositionInfo pos_info;
282 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
283 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
284 work_area.Inset(kScreenEdgePadding, kScreenEdgePadding);
286 gfx::Point corner = internal::GetClosestCorner(work_area, mouse_click_point_);
288 pos_info.taskbar_alignment = internal::GetTaskbarAlignment();
290 // We assume the taskbar is either at the top or at the bottom if we are not
291 // able to find it.
292 if (pos_info.taskbar_alignment == ALIGNMENT_NONE) {
293 if (mouse_click_point_.y() > corner.y())
294 pos_info.taskbar_alignment = ALIGNMENT_TOP;
295 else
296 pos_info.taskbar_alignment = ALIGNMENT_BOTTOM;
299 pos_info.message_center_alignment =
300 internal::GetAnchorAlignment(work_area, corner);
302 pos_info.inital_anchor_point = corner;
303 pos_info.max_height = work_area.height();
305 if (work_area.Contains(mouse_click_point_)) {
306 // Message center is in the work area. So position it few pixels above the
307 // mouse click point if alignemnt is towards bottom and few pixels below if
308 // alignment is towards top.
309 pos_info.inital_anchor_point.set_y(
310 mouse_click_point_.y() +
311 (pos_info.message_center_alignment & ALIGNMENT_BOTTOM ? -kMouseOffset
312 : kMouseOffset));
314 // Subtract the distance between mouse click point and the closest
315 // (insetted) edge from the max height to show the message center within the
316 // (insetted) work area bounds. Also subtract the offset from the mouse
317 // click point we added earlier.
318 pos_info.max_height -=
319 std::abs(mouse_click_point_.y() - corner.y()) + kMouseOffset;
321 return pos_info;
324 MessageCenterTray* WebNotificationTray::GetMessageCenterTray() {
325 return message_center_tray_.get();
328 void WebNotificationTray::CreateStatusIcon(const gfx::ImageSkia& image,
329 const base::string16& tool_tip) {
330 if (status_icon_)
331 return;
333 StatusTray* status_tray = g_browser_process->status_tray();
334 if (!status_tray)
335 return;
337 status_icon_ = status_tray->CreateStatusIcon(
338 StatusTray::NOTIFICATION_TRAY_ICON, image, tool_tip);
339 if (!status_icon_)
340 return;
342 status_icon_->AddObserver(this);
343 AddQuietModeMenu(status_icon_);
346 void WebNotificationTray::DestroyStatusIcon() {
347 if (!status_icon_)
348 return;
350 status_icon_->RemoveObserver(this);
351 StatusTray* status_tray = g_browser_process->status_tray();
352 if (status_tray)
353 status_tray->RemoveStatusIcon(status_icon_);
354 status_icon_menu_ = NULL;
355 status_icon_ = NULL;
358 void WebNotificationTray::AddQuietModeMenu(StatusIcon* status_icon) {
359 DCHECK(status_icon);
361 scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(this));
362 menu->AddCheckItem(kToggleQuietMode,
363 l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_QUIET_MODE));
364 menu->SetCommandIdChecked(kToggleQuietMode, message_center()->IsQuietMode());
365 menu->AddItem(kEnableQuietModeHour,
366 l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_QUIET_MODE_1HOUR));
367 menu->AddItem(kEnableQuietModeDay,
368 l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_QUIET_MODE_1DAY));
370 status_icon_menu_ = menu.get();
371 status_icon->SetContextMenu(menu.Pass());
374 MessageCenterWidgetDelegate*
375 WebNotificationTray::GetMessageCenterWidgetDelegateForTest() {
376 return message_center_delegate_;
379 } // namespace message_center