[content shell] implement testRunner.overridePreference
[chromium-blink-merge.git] / ash / system / tray / system_tray.cc
blobdc9c17314de70d3882363d1fff24bee7ad6dbbf0
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 "ash/system/tray/system_tray.h"
7 #include "ash/ash_switches.h"
8 #include "ash/shell.h"
9 #include "ash/shell/panel_window.h"
10 #include "ash/shell_window_ids.h"
11 #include "ash/system/audio/tray_volume.h"
12 #include "ash/system/bluetooth/tray_bluetooth.h"
13 #include "ash/system/brightness/tray_brightness.h"
14 #include "ash/system/chromeos/tray_display.h"
15 #include "ash/system/date/tray_date.h"
16 #include "ash/system/drive/tray_drive.h"
17 #include "ash/system/ime/tray_ime.h"
18 #include "ash/system/locale/tray_locale.h"
19 #include "ash/system/logout_button/tray_logout_button.h"
20 #include "ash/system/monitor/tray_monitor.h"
21 #include "ash/system/power/power_supply_status.h"
22 #include "ash/system/power/tray_power.h"
23 #include "ash/system/session_length_limit/tray_session_length_limit.h"
24 #include "ash/system/settings/tray_settings.h"
25 #include "ash/system/status_area_widget.h"
26 #include "ash/system/tray/system_tray_delegate.h"
27 #include "ash/system/tray/system_tray_item.h"
28 #include "ash/system/tray/tray_bubble_wrapper.h"
29 #include "ash/system/tray/tray_constants.h"
30 #include "ash/system/tray_accessibility.h"
31 #include "ash/system/tray_caps_lock.h"
32 #include "ash/system/tray_update.h"
33 #include "ash/system/user/login_status.h"
34 #include "ash/system/user/tray_user.h"
35 #include "ash/wm/shelf_layout_manager.h"
36 #include "base/command_line.h"
37 #include "base/logging.h"
38 #include "base/timer.h"
39 #include "base/utf_string_conversions.h"
40 #include "grit/ash_strings.h"
41 #include "ui/aura/root_window.h"
42 #include "ui/base/events/event_constants.h"
43 #include "ui/base/l10n/l10n_util.h"
44 #include "ui/compositor/layer.h"
45 #include "ui/gfx/canvas.h"
46 #include "ui/gfx/screen.h"
47 #include "ui/gfx/skia_util.h"
48 #include "ui/views/border.h"
49 #include "ui/views/controls/label.h"
50 #include "ui/views/layout/box_layout.h"
51 #include "ui/views/layout/fill_layout.h"
52 #include "ui/views/view.h"
54 #if defined(OS_CHROMEOS)
55 #include "ash/system/chromeos/network/tray_network.h"
56 #include "ash/system/chromeos/network/tray_sms.h"
57 #include "ash/system/chromeos/network/tray_vpn.h"
58 #endif
60 using views::TrayBubbleView;
62 namespace ash {
64 namespace internal {
66 // Class to initialize and manage the SystemTrayBubble and TrayBubbleWrapper
67 // instances for a bubble.
69 class SystemBubbleWrapper {
70 public:
71 // Takes ownership of |bubble|.
72 explicit SystemBubbleWrapper(internal::SystemTrayBubble* bubble)
73 : bubble_(bubble) {
76 // Initializes the bubble view and creates |bubble_wrapper_|.
77 void InitView(TrayBackgroundView* tray,
78 views::View* anchor,
79 TrayBubbleView::InitParams* init_params) {
80 user::LoginStatus login_status =
81 Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus();
82 bubble_->InitView(anchor, login_status, init_params);
83 bubble_wrapper_.reset(
84 new internal::TrayBubbleWrapper(tray, bubble_->bubble_view()));
87 // Convenience accessors:
88 SystemTrayBubble* bubble() const { return bubble_.get(); }
89 SystemTrayBubble::BubbleType bubble_type() const {
90 return bubble_->bubble_type();
92 TrayBubbleView* bubble_view() const { return bubble_->bubble_view(); }
94 private:
95 scoped_ptr<internal::SystemTrayBubble> bubble_;
96 scoped_ptr<internal::TrayBubbleWrapper> bubble_wrapper_;
98 DISALLOW_COPY_AND_ASSIGN(SystemBubbleWrapper);
101 } // namespace internal
103 // SystemTray
105 using internal::SystemTrayBubble;
107 SystemTray::SystemTray(internal::StatusAreaWidget* status_area_widget)
108 : internal::TrayBackgroundView(status_area_widget),
109 items_(),
110 default_bubble_height_(0),
111 hide_notifications_(false) {
112 SetContentsBackground();
115 SystemTray::~SystemTray() {
116 // Destroy any child views that might have back pointers before ~View().
117 system_bubble_.reset();
118 notification_bubble_.reset();
119 for (std::vector<SystemTrayItem*>::iterator it = items_.begin();
120 it != items_.end();
121 ++it) {
122 (*it)->DestroyTrayView();
126 void SystemTray::InitializeTrayItems(SystemTrayDelegate* delegate) {
127 internal::TrayBackgroundView::Initialize();
128 CreateItems(delegate);
131 void SystemTray::CreateItems(SystemTrayDelegate* delegate) {
132 AddTrayItem(new internal::TraySessionLengthLimit(this));
133 AddTrayItem(new internal::TrayLogoutButton(this));
134 AddTrayItem(new internal::TrayUser(this));
135 AddTrayItem(new internal::TrayIME(this));
136 tray_accessibility_ = new internal::TrayAccessibility(this);
137 AddTrayItem(tray_accessibility_);
138 AddTrayItem(new internal::TrayPower(this));
139 #if defined(OS_CHROMEOS)
140 AddTrayItem(new internal::TrayNetwork(this));
141 AddTrayItem(new internal::TrayVPN(this));
142 AddTrayItem(new internal::TraySms(this));
143 #endif
144 AddTrayItem(new internal::TrayBluetooth(this));
145 AddTrayItem(new internal::TrayDrive(this));
146 AddTrayItem(new internal::TrayLocale(this));
147 #if defined(OS_CHROMEOS)
148 AddTrayItem(new internal::TrayDisplay(this));
149 #endif
150 AddTrayItem(new internal::TrayVolume(this));
151 AddTrayItem(new internal::TrayBrightness(this));
152 AddTrayItem(new internal::TrayUpdate(this));
153 AddTrayItem(new internal::TrayCapsLock(this));
154 AddTrayItem(new internal::TraySettings(this));
155 AddTrayItem(new internal::TrayDate(this));
157 #if defined(OS_LINUX)
158 // Add memory monitor if enabled.
159 CommandLine* cmd = CommandLine::ForCurrentProcess();
160 if (cmd->HasSwitch(ash::switches::kAshEnableMemoryMonitor))
161 AddTrayItem(new internal::TrayMonitor(this));
162 #endif
164 SetVisible(ash::Shell::GetInstance()->system_tray_delegate()->
165 GetTrayVisibilityOnStartup());
168 void SystemTray::AddTrayItem(SystemTrayItem* item) {
169 items_.push_back(item);
171 SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
172 views::View* tray_item = item->CreateTrayView(delegate->GetUserLoginStatus());
173 item->UpdateAfterShelfAlignmentChange(shelf_alignment());
175 if (tray_item) {
176 tray_container()->AddChildViewAt(tray_item, 0);
177 PreferredSizeChanged();
178 tray_item_map_[item] = tray_item;
182 void SystemTray::RemoveTrayItem(SystemTrayItem* item) {
183 NOTIMPLEMENTED();
186 void SystemTray::ShowDefaultView(BubbleCreationType creation_type) {
187 ShowDefaultViewWithOffset(creation_type,
188 TrayBubbleView::InitParams::kArrowDefaultOffset);
191 void SystemTray::ShowDetailedView(SystemTrayItem* item,
192 int close_delay,
193 bool activate,
194 BubbleCreationType creation_type) {
195 std::vector<SystemTrayItem*> items;
196 items.push_back(item);
197 ShowItems(items, true, activate, creation_type, GetTrayXOffset(item));
198 if (system_bubble_.get())
199 system_bubble_->bubble()->StartAutoCloseTimer(close_delay);
202 void SystemTray::SetDetailedViewCloseDelay(int close_delay) {
203 if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DETAILED))
204 system_bubble_->bubble()->StartAutoCloseTimer(close_delay);
207 void SystemTray::HideDetailedView(SystemTrayItem* item) {
208 if (item != detailed_item_)
209 return;
210 DestroySystemBubble();
211 UpdateNotificationBubble();
214 void SystemTray::ShowNotificationView(SystemTrayItem* item) {
215 if (std::find(notification_items_.begin(), notification_items_.end(), item)
216 != notification_items_.end())
217 return;
218 notification_items_.push_back(item);
219 UpdateNotificationBubble();
222 void SystemTray::HideNotificationView(SystemTrayItem* item) {
223 std::vector<SystemTrayItem*>::iterator found_iter =
224 std::find(notification_items_.begin(), notification_items_.end(), item);
225 if (found_iter == notification_items_.end())
226 return;
227 notification_items_.erase(found_iter);
228 // Only update the notification bubble if visible (i.e. don't create one).
229 if (notification_bubble_.get())
230 UpdateNotificationBubble();
233 void SystemTray::UpdateAfterLoginStatusChange(user::LoginStatus login_status) {
234 DestroySystemBubble();
236 for (std::vector<SystemTrayItem*>::iterator it = items_.begin();
237 it != items_.end();
238 ++it) {
239 (*it)->UpdateAfterLoginStatusChange(login_status);
242 SetVisible(true);
243 PreferredSizeChanged();
246 void SystemTray::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
247 for (std::vector<SystemTrayItem*>::iterator it = items_.begin();
248 it != items_.end();
249 ++it) {
250 (*it)->UpdateAfterShelfAlignmentChange(alignment);
254 void SystemTray::SetHideNotifications(bool hide_notifications) {
255 if (notification_bubble_.get())
256 notification_bubble_->bubble()->SetVisible(!hide_notifications);
257 hide_notifications_ = hide_notifications;
260 bool SystemTray::ShouldShowLauncher() const {
261 return system_bubble_.get() && system_bubble_->bubble()->ShouldShowLauncher();
264 bool SystemTray::HasSystemBubble() const {
265 return system_bubble_.get() != NULL;
268 internal::SystemTrayBubble* SystemTray::GetSystemBubble() {
269 if (!system_bubble_.get())
270 return NULL;
271 return system_bubble_->bubble();
274 bool SystemTray::IsAnyBubbleVisible() const {
275 return ((system_bubble_.get() &&
276 system_bubble_->bubble()->IsVisible()) ||
277 (notification_bubble_.get() &&
278 notification_bubble_->bubble()->IsVisible()));
281 bool SystemTray::IsMouseInNotificationBubble() const {
282 if (!notification_bubble_.get())
283 return false;
284 return notification_bubble_->bubble_view()->GetBoundsInScreen().Contains(
285 Shell::GetScreen()->GetCursorScreenPoint());
288 bool SystemTray::CloseBubbleForTest() const {
289 if (!system_bubble_.get())
290 return false;
291 system_bubble_->bubble()->Close();
292 return true;
295 // Private methods.
297 bool SystemTray::HasSystemBubbleType(SystemTrayBubble::BubbleType type) {
298 DCHECK(type != SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION);
299 return system_bubble_.get() && system_bubble_->bubble_type() == type;
302 void SystemTray::DestroySystemBubble() {
303 system_bubble_.reset();
304 detailed_item_ = NULL;
307 void SystemTray::DestroyNotificationBubble() {
308 notification_bubble_.reset();
309 status_area_widget()->SetHideWebNotifications(false);
312 int SystemTray::GetTrayXOffset(SystemTrayItem* item) const {
313 // Don't attempt to align the arrow if the shelf is on the left or right.
314 if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM)
315 return TrayBubbleView::InitParams::kArrowDefaultOffset;
317 std::map<SystemTrayItem*, views::View*>::const_iterator it =
318 tray_item_map_.find(item);
319 if (it == tray_item_map_.end())
320 return TrayBubbleView::InitParams::kArrowDefaultOffset;
322 const views::View* item_view = it->second;
323 if (item_view->bounds().IsEmpty()) {
324 // The bounds of item could be still empty if it does not have a visible
325 // tray view. In that case, use the default (minimum) offset.
326 return TrayBubbleView::InitParams::kArrowDefaultOffset;
329 gfx::Point point(item_view->width() / 2, 0);
330 ConvertPointToWidget(item_view, &point);
331 return point.x();
334 void SystemTray::ShowDefaultViewWithOffset(BubbleCreationType creation_type,
335 int arrow_offset) {
336 ShowItems(items_.get(), false, true, creation_type, arrow_offset);
339 void SystemTray::ShowItems(const std::vector<SystemTrayItem*>& items,
340 bool detailed,
341 bool can_activate,
342 BubbleCreationType creation_type,
343 int arrow_offset) {
344 // Destroy any existing bubble and create a new one.
345 SystemTrayBubble::BubbleType bubble_type = detailed ?
346 SystemTrayBubble::BUBBLE_TYPE_DETAILED :
347 SystemTrayBubble::BUBBLE_TYPE_DEFAULT;
349 // Destroy the notification bubble here so that it doesn't get rebuilt
350 // while we add items to the main bubble_ (e.g. in HideNotificationView).
351 notification_bubble_.reset();
353 if (system_bubble_.get() && creation_type == BUBBLE_USE_EXISTING) {
354 system_bubble_->bubble()->UpdateView(items, bubble_type);
355 } else {
356 TrayBubbleView::InitParams init_params(TrayBubbleView::ANCHOR_TYPE_TRAY,
357 GetAnchorAlignment(),
358 kTrayPopupMinWidth,
359 kTrayPopupMaxWidth);
360 init_params.can_activate = can_activate;
361 if (detailed) {
362 // This is the case where a volume control or brightness control bubble
363 // is created.
364 init_params.max_height = default_bubble_height_;
365 init_params.arrow_color = kBackgroundColor;
366 } else {
367 init_params.arrow_color = kHeaderBackgroundColorDark;
369 init_params.arrow_offset = arrow_offset;
370 SystemTrayBubble* bubble = new SystemTrayBubble(this, items, bubble_type);
371 system_bubble_.reset(new internal::SystemBubbleWrapper(bubble));
372 system_bubble_->InitView(this, tray_container(), &init_params);
374 // Save height of default view for creating detailed views directly.
375 if (!detailed)
376 default_bubble_height_ = system_bubble_->bubble_view()->height();
378 if (detailed && items.size() > 0)
379 detailed_item_ = items[0];
380 else
381 detailed_item_ = NULL;
383 UpdateNotificationBubble(); // State changed, re-create notifications.
384 status_area_widget()->SetHideWebNotifications(true);
385 GetShelfLayoutManager()->UpdateAutoHideState();
388 void SystemTray::UpdateNotificationBubble() {
389 // Only show the notification buble if we have notifications and we are not
390 // showing the default bubble.
391 if (notification_items_.empty() ||
392 HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DEFAULT)) {
393 DestroyNotificationBubble();
394 return;
396 // Destroy the existing bubble before constructing a new one.
397 notification_bubble_.reset();
398 SystemTrayBubble* notification_bubble;
399 if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DETAILED)) {
400 // Skip notifications for any currently displayed detailed item.
401 std::vector<SystemTrayItem*> items;
402 for (std::vector<SystemTrayItem*>::iterator iter =
403 notification_items_.begin();
404 iter != notification_items_.end(); ++ iter) {
405 if (*iter != detailed_item_)
406 items.push_back(*iter);
408 if (items.empty()) {
409 DestroyNotificationBubble();
410 return;
412 notification_bubble = new SystemTrayBubble(
413 this, items, SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION);
414 } else {
415 // Show all notifications.
416 notification_bubble = new SystemTrayBubble(
417 this, notification_items_, SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION);
419 views::View* anchor;
420 TrayBubbleView::AnchorType anchor_type;
421 if (system_bubble_.get()) {
422 anchor = system_bubble_->bubble_view();
423 anchor_type = TrayBubbleView::ANCHOR_TYPE_BUBBLE;
424 } else {
425 anchor = tray_container();
426 anchor_type = TrayBubbleView::ANCHOR_TYPE_TRAY;
428 TrayBubbleView::InitParams init_params(anchor_type,
429 GetAnchorAlignment(),
430 kTrayPopupMinWidth,
431 kTrayPopupMaxWidth);
432 init_params.arrow_color = kBackgroundColor;
433 init_params.arrow_offset = GetTrayXOffset(notification_items_[0]);
434 notification_bubble_.reset(
435 new internal::SystemBubbleWrapper(notification_bubble));
436 notification_bubble_->InitView(this, anchor, &init_params);
438 if (notification_bubble->bubble_view()->child_count() == 0) {
439 // It is possible that none of the items generated actual notifications.
440 DestroyNotificationBubble();
441 return;
443 if (hide_notifications_)
444 notification_bubble->SetVisible(false);
445 else
446 status_area_widget()->SetHideWebNotifications(true);
449 void SystemTray::SetShelfAlignment(ShelfAlignment alignment) {
450 if (alignment == shelf_alignment())
451 return;
452 internal::TrayBackgroundView::SetShelfAlignment(alignment);
453 UpdateAfterShelfAlignmentChange(alignment);
454 // Destroy any existing bubble so that it is rebuilt correctly.
455 system_bubble_.reset();
456 // Rebuild any notification bubble.
457 if (notification_bubble_.get()) {
458 notification_bubble_.reset();
459 UpdateNotificationBubble();
463 void SystemTray::AnchorUpdated() {
464 if (notification_bubble_.get()) {
465 notification_bubble_->bubble_view()->UpdateBubble();
466 // Ensure that the notification buble is above the launcher/status area.
467 notification_bubble_->bubble_view()->GetWidget()->StackAtTop();
468 UpdateBubbleViewArrow(notification_bubble_->bubble_view());
470 if (system_bubble_.get()) {
471 system_bubble_->bubble_view()->UpdateBubble();
472 UpdateBubbleViewArrow(system_bubble_->bubble_view());
476 string16 SystemTray::GetAccessibleNameForTray() {
477 return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBLE_NAME);
480 void SystemTray::HideBubbleWithView(const TrayBubbleView* bubble_view) {
481 if (system_bubble_.get() && bubble_view == system_bubble_->bubble_view()) {
482 DestroySystemBubble();
483 UpdateNotificationBubble(); // State changed, re-create notifications.
484 GetShelfLayoutManager()->UpdateAutoHideState();
485 } else if (notification_bubble_.get() &&
486 bubble_view == notification_bubble_->bubble_view()) {
487 DestroyNotificationBubble();
491 bool SystemTray::ClickedOutsideBubble() {
492 if (!system_bubble_.get())
493 return false;
494 HideBubbleWithView(system_bubble_->bubble_view());
495 return true;
498 void SystemTray::BubbleViewDestroyed() {
499 if (system_bubble_.get()) {
500 system_bubble_->bubble()->DestroyItemViews();
501 system_bubble_->bubble()->BubbleViewDestroyed();
505 void SystemTray::OnMouseEnteredView() {
506 if (system_bubble_.get())
507 system_bubble_->bubble()->StopAutoCloseTimer();
510 void SystemTray::OnMouseExitedView() {
511 if (system_bubble_.get())
512 system_bubble_->bubble()->RestartAutoCloseTimer();
515 string16 SystemTray::GetAccessibleNameForBubble() {
516 return GetAccessibleNameForTray();
519 gfx::Rect SystemTray::GetAnchorRect(
520 views::Widget* anchor_widget,
521 TrayBubbleView::AnchorType anchor_type,
522 TrayBubbleView::AnchorAlignment anchor_alignment) {
523 return GetBubbleAnchorRect(anchor_widget, anchor_type, anchor_alignment);
526 void SystemTray::HideBubble(const TrayBubbleView* bubble_view) {
527 HideBubbleWithView(bubble_view);
530 bool SystemTray::PerformAction(const ui::Event& event) {
531 // If we're already showing the default view, hide it; otherwise, show it
532 // (and hide any popup that's currently shown).
533 if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DEFAULT)) {
534 system_bubble_->bubble()->Close();
535 } else {
536 int arrow_offset = TrayBubbleView::InitParams::kArrowDefaultOffset;
537 if (event.IsMouseEvent() || event.type() == ui::ET_GESTURE_TAP) {
538 const ui::LocatedEvent& located_event =
539 static_cast<const ui::LocatedEvent&>(event);
540 if (shelf_alignment() == SHELF_ALIGNMENT_BOTTOM) {
541 gfx::Point point(located_event.x(), 0);
542 ConvertPointToWidget(this, &point);
543 arrow_offset = point.x();
546 ShowDefaultViewWithOffset(BUBBLE_CREATE_NEW, arrow_offset);
548 return true;
551 } // namespace ash