Disable accessible touch exploration by default.
[chromium-blink-merge.git] / ash / system / tray / system_tray.cc
bloba28f78405fe7bd85b069c3185849f707b9f8ae63
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/metrics/user_metrics_recorder.h"
9 #include "ash/shelf/shelf_layout_manager.h"
10 #include "ash/shell.h"
11 #include "ash/shell/panel_window.h"
12 #include "ash/shell_window_ids.h"
13 #include "ash/system/audio/tray_audio.h"
14 #include "ash/system/bluetooth/tray_bluetooth.h"
15 #include "ash/system/date/tray_date.h"
16 #include "ash/system/ime/tray_ime.h"
17 #include "ash/system/status_area_widget.h"
18 #include "ash/system/tray/system_tray_delegate.h"
19 #include "ash/system/tray/system_tray_item.h"
20 #include "ash/system/tray/tray_bubble_wrapper.h"
21 #include "ash/system/tray/tray_constants.h"
22 #include "ash/system/tray_accessibility.h"
23 #include "ash/system/tray_update.h"
24 #include "ash/system/user/login_status.h"
25 #include "ash/system/user/tray_user.h"
26 #include "ash/system/user/tray_user_separator.h"
27 #include "ash/system/web_notification/web_notification_tray.h"
28 #include "base/logging.h"
29 #include "base/strings/utf_string_conversions.h"
30 #include "base/timer/timer.h"
31 #include "grit/ash_strings.h"
32 #include "ui/aura/window_event_dispatcher.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "ui/compositor/layer.h"
35 #include "ui/events/event_constants.h"
36 #include "ui/gfx/canvas.h"
37 #include "ui/gfx/screen.h"
38 #include "ui/gfx/skia_util.h"
39 #include "ui/views/border.h"
40 #include "ui/views/controls/label.h"
41 #include "ui/views/layout/box_layout.h"
42 #include "ui/views/layout/fill_layout.h"
43 #include "ui/views/view.h"
45 #if defined(OS_CHROMEOS)
46 #include "ash/system/chromeos/audio/tray_audio_chromeos.h"
47 #include "ash/system/chromeos/brightness/tray_brightness.h"
48 #include "ash/system/chromeos/enterprise/tray_enterprise.h"
49 #include "ash/system/chromeos/network/tray_network.h"
50 #include "ash/system/chromeos/network/tray_sms.h"
51 #include "ash/system/chromeos/network/tray_vpn.h"
52 #include "ash/system/chromeos/power/power_status.h"
53 #include "ash/system/chromeos/power/tray_power.h"
54 #include "ash/system/chromeos/rotation/tray_rotation_lock.h"
55 #include "ash/system/chromeos/screen_security/screen_capture_tray_item.h"
56 #include "ash/system/chromeos/screen_security/screen_share_tray_item.h"
57 #include "ash/system/chromeos/session/tray_session_length_limit.h"
58 #include "ash/system/chromeos/settings/tray_settings.h"
59 #include "ash/system/chromeos/supervised/tray_supervised_user.h"
60 #include "ash/system/chromeos/tray_caps_lock.h"
61 #include "ash/system/chromeos/tray_display.h"
62 #include "ash/system/chromeos/tray_tracing.h"
63 #include "ash/system/tray/media_security/multi_profile_media_tray_item.h"
64 #include "ui/message_center/message_center.h"
65 #elif defined(OS_WIN)
66 #include "ash/system/win/audio/tray_audio_win.h"
67 #include "media/audio/win/core_audio_util_win.h"
68 #endif
70 using views::TrayBubbleView;
72 namespace ash {
74 // The minimum width of the system tray menu width.
75 const int kMinimumSystemTrayMenuWidth = 300;
77 // Class to initialize and manage the SystemTrayBubble and TrayBubbleWrapper
78 // instances for a bubble.
80 class SystemBubbleWrapper {
81 public:
82 // Takes ownership of |bubble|.
83 explicit SystemBubbleWrapper(SystemTrayBubble* bubble)
84 : bubble_(bubble), is_persistent_(false) {}
86 // Initializes the bubble view and creates |bubble_wrapper_|.
87 void InitView(TrayBackgroundView* tray,
88 views::View* anchor,
89 TrayBubbleView::InitParams* init_params,
90 bool is_persistent) {
91 DCHECK(anchor);
92 user::LoginStatus login_status =
93 Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus();
94 bubble_->InitView(anchor, login_status, init_params);
95 bubble_wrapper_.reset(new TrayBubbleWrapper(tray, bubble_->bubble_view()));
96 // The system bubble should not have an arrow.
97 bubble_->bubble_view()->SetArrowPaintType(
98 views::BubbleBorder::PAINT_NONE);
99 is_persistent_ = is_persistent;
101 // If ChromeVox is enabled, focus the default item if no item is focused.
102 if (Shell::GetInstance()->accessibility_delegate()->
103 IsSpokenFeedbackEnabled()) {
104 bubble_->FocusDefaultIfNeeded();
108 // Convenience accessors:
109 SystemTrayBubble* bubble() const { return bubble_.get(); }
110 SystemTrayBubble::BubbleType bubble_type() const {
111 return bubble_->bubble_type();
113 TrayBubbleView* bubble_view() const { return bubble_->bubble_view(); }
114 bool is_persistent() const { return is_persistent_; }
116 private:
117 scoped_ptr<SystemTrayBubble> bubble_;
118 scoped_ptr<TrayBubbleWrapper> bubble_wrapper_;
119 bool is_persistent_;
121 DISALLOW_COPY_AND_ASSIGN(SystemBubbleWrapper);
125 // SystemTray
127 SystemTray::SystemTray(StatusAreaWidget* status_area_widget)
128 : TrayBackgroundView(status_area_widget),
129 items_(),
130 default_bubble_height_(0),
131 hide_notifications_(false),
132 full_system_tray_menu_(false),
133 tray_accessibility_(NULL),
134 tray_date_(NULL) {
135 SetContentsBackground();
138 SystemTray::~SystemTray() {
139 // Destroy any child views that might have back pointers before ~View().
140 system_bubble_.reset();
141 notification_bubble_.reset();
142 for (std::vector<SystemTrayItem*>::iterator it = items_.begin();
143 it != items_.end();
144 ++it) {
145 (*it)->DestroyTrayView();
149 void SystemTray::InitializeTrayItems(SystemTrayDelegate* delegate) {
150 TrayBackgroundView::Initialize();
151 CreateItems(delegate);
154 void SystemTray::CreateItems(SystemTrayDelegate* delegate) {
155 #if !defined(OS_WIN)
156 // Create user items for each possible user.
157 ash::Shell* shell = ash::Shell::GetInstance();
158 int maximum_user_profiles =
159 shell->session_state_delegate()->GetMaximumNumberOfLoggedInUsers();
160 for (int i = 0; i < maximum_user_profiles; i++)
161 AddTrayItem(new TrayUser(this, i));
163 if (maximum_user_profiles > 1) {
164 // Add a special double line separator between users and the rest of the
165 // menu if more then one user is logged in.
166 AddTrayItem(new TrayUserSeparator(this));
168 #endif
170 tray_accessibility_ = new TrayAccessibility(this);
171 tray_date_ = new TrayDate(this);
173 #if defined(OS_CHROMEOS)
174 AddTrayItem(new TraySessionLengthLimit(this));
175 AddTrayItem(new TrayEnterprise(this));
176 AddTrayItem(new TraySupervisedUser(this));
177 AddTrayItem(new TrayIME(this));
178 AddTrayItem(tray_accessibility_);
179 AddTrayItem(new TrayTracing(this));
180 AddTrayItem(new TrayPower(this, message_center::MessageCenter::Get()));
181 AddTrayItem(new TrayNetwork(this));
182 AddTrayItem(new TrayVPN(this));
183 AddTrayItem(new TraySms(this));
184 AddTrayItem(new TrayBluetooth(this));
185 AddTrayItem(new TrayDisplay(this));
186 AddTrayItem(new ScreenCaptureTrayItem(this));
187 AddTrayItem(new ScreenShareTrayItem(this));
188 AddTrayItem(new MultiProfileMediaTrayItem(this));
189 AddTrayItem(new TrayAudioChromeOs(this));
190 AddTrayItem(new TrayBrightness(this));
191 AddTrayItem(new TrayCapsLock(this));
192 AddTrayItem(new TraySettings(this));
193 AddTrayItem(new TrayUpdate(this));
194 AddTrayItem(new TrayRotationLock(this));
195 AddTrayItem(tray_date_);
196 #elif defined(OS_WIN)
197 AddTrayItem(tray_accessibility_);
198 if (media::CoreAudioUtil::IsSupported())
199 AddTrayItem(new TrayAudioWin(this));
200 AddTrayItem(new TrayUpdate(this));
201 AddTrayItem(tray_date_);
202 #elif defined(OS_LINUX)
203 AddTrayItem(new TrayIME(this));
204 AddTrayItem(tray_accessibility_);
205 AddTrayItem(new TrayBluetooth(this));
206 AddTrayItem(new TrayUpdate(this));
207 AddTrayItem(tray_date_);
208 #endif
210 SetVisible(ash::Shell::GetInstance()->system_tray_delegate()->
211 GetTrayVisibilityOnStartup());
214 void SystemTray::AddTrayItem(SystemTrayItem* item) {
215 items_.push_back(item);
217 SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
218 views::View* tray_item = item->CreateTrayView(delegate->GetUserLoginStatus());
219 item->UpdateAfterShelfAlignmentChange(shelf_alignment());
221 if (tray_item) {
222 tray_container()->AddChildViewAt(tray_item, 0);
223 PreferredSizeChanged();
224 tray_item_map_[item] = tray_item;
228 void SystemTray::RemoveTrayItem(SystemTrayItem* item) {
229 NOTIMPLEMENTED();
232 const std::vector<SystemTrayItem*>& SystemTray::GetTrayItems() const {
233 return items_.get();
236 void SystemTray::ShowDefaultView(BubbleCreationType creation_type) {
237 ShowDefaultViewWithOffset(
238 creation_type,
239 TrayBubbleView::InitParams::kArrowDefaultOffset,
240 false);
243 void SystemTray::ShowPersistentDefaultView() {
244 ShowItems(items_.get(),
245 false,
246 false,
247 BUBBLE_CREATE_NEW,
248 TrayBubbleView::InitParams::kArrowDefaultOffset,
249 true);
252 void SystemTray::ShowDetailedView(SystemTrayItem* item,
253 int close_delay,
254 bool activate,
255 BubbleCreationType creation_type) {
256 std::vector<SystemTrayItem*> items;
257 // The detailed view with timeout means a UI to show the current system state,
258 // like the audio level or brightness. Such UI should behave as persistent and
259 // keep its own logic for the appearance.
260 bool persistent = (
261 !activate && close_delay > 0 && creation_type == BUBBLE_CREATE_NEW);
262 items.push_back(item);
263 ShowItems(
264 items, true, activate, creation_type, GetTrayXOffset(item), persistent);
265 if (system_bubble_)
266 system_bubble_->bubble()->StartAutoCloseTimer(close_delay);
269 void SystemTray::SetDetailedViewCloseDelay(int close_delay) {
270 if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DETAILED))
271 system_bubble_->bubble()->StartAutoCloseTimer(close_delay);
274 void SystemTray::HideDetailedView(SystemTrayItem* item) {
275 if (item != detailed_item_)
276 return;
277 DestroySystemBubble();
278 UpdateNotificationBubble();
281 void SystemTray::ShowNotificationView(SystemTrayItem* item) {
282 if (std::find(notification_items_.begin(), notification_items_.end(), item)
283 != notification_items_.end())
284 return;
285 notification_items_.push_back(item);
286 UpdateNotificationBubble();
289 void SystemTray::HideNotificationView(SystemTrayItem* item) {
290 std::vector<SystemTrayItem*>::iterator found_iter =
291 std::find(notification_items_.begin(), notification_items_.end(), item);
292 if (found_iter == notification_items_.end())
293 return;
294 notification_items_.erase(found_iter);
295 // Only update the notification bubble if visible (i.e. don't create one).
296 if (notification_bubble_)
297 UpdateNotificationBubble();
300 void SystemTray::UpdateAfterLoginStatusChange(user::LoginStatus login_status) {
301 DestroySystemBubble();
302 UpdateNotificationBubble();
304 for (std::vector<SystemTrayItem*>::iterator it = items_.begin();
305 it != items_.end();
306 ++it) {
307 (*it)->UpdateAfterLoginStatusChange(login_status);
310 // Items default to SHELF_ALIGNMENT_BOTTOM. Update them if the initial
311 // position of the shelf differs.
312 if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM)
313 UpdateAfterShelfAlignmentChange(shelf_alignment());
315 SetVisible(true);
316 PreferredSizeChanged();
319 void SystemTray::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
320 for (std::vector<SystemTrayItem*>::iterator it = items_.begin();
321 it != items_.end();
322 ++it) {
323 (*it)->UpdateAfterShelfAlignmentChange(alignment);
327 void SystemTray::SetHideNotifications(bool hide_notifications) {
328 if (notification_bubble_)
329 notification_bubble_->bubble()->SetVisible(!hide_notifications);
330 hide_notifications_ = hide_notifications;
333 bool SystemTray::ShouldShowShelf() const {
334 return system_bubble_.get() && system_bubble_->bubble()->ShouldShowShelf();
337 bool SystemTray::HasSystemBubble() const {
338 return system_bubble_.get() != NULL;
341 bool SystemTray::HasNotificationBubble() const {
342 return notification_bubble_.get() != NULL;
345 SystemTrayBubble* SystemTray::GetSystemBubble() {
346 if (!system_bubble_)
347 return NULL;
348 return system_bubble_->bubble();
351 bool SystemTray::IsAnyBubbleVisible() const {
352 return ((system_bubble_.get() &&
353 system_bubble_->bubble()->IsVisible()) ||
354 (notification_bubble_.get() &&
355 notification_bubble_->bubble()->IsVisible()));
358 bool SystemTray::IsMouseInNotificationBubble() const {
359 if (!notification_bubble_)
360 return false;
361 return notification_bubble_->bubble_view()->GetBoundsInScreen().Contains(
362 Shell::GetScreen()->GetCursorScreenPoint());
365 bool SystemTray::CloseSystemBubble() const {
366 if (!system_bubble_)
367 return false;
368 system_bubble_->bubble()->Close();
369 return true;
372 views::View* SystemTray::GetHelpButtonView() const {
373 return tray_date_->GetHelpButtonView();
376 bool SystemTray::CloseNotificationBubbleForTest() const {
377 if (!notification_bubble_)
378 return false;
379 notification_bubble_->bubble()->Close();
380 return true;
383 // Private methods.
385 bool SystemTray::HasSystemBubbleType(SystemTrayBubble::BubbleType type) {
386 DCHECK(type != SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION);
387 return system_bubble_.get() && system_bubble_->bubble_type() == type;
390 void SystemTray::DestroySystemBubble() {
391 CloseSystemBubbleAndDeactivateSystemTray();
392 detailed_item_ = NULL;
393 UpdateWebNotifications();
396 void SystemTray::DestroyNotificationBubble() {
397 if (notification_bubble_) {
398 notification_bubble_.reset();
399 UpdateWebNotifications();
403 base::string16 SystemTray::GetAccessibleNameForTray() {
404 base::string16 time = GetAccessibleTimeString(base::Time::Now());
405 base::string16 battery = base::ASCIIToUTF16("");
406 #if defined(OS_CHROMEOS)
407 battery = PowerStatus::Get()->GetAccessibleNameString(false);
408 #endif
409 return l10n_util::GetStringFUTF16(
410 IDS_ASH_STATUS_TRAY_ACCESSIBLE_DESCRIPTION, time, battery);
413 int SystemTray::GetTrayXOffset(SystemTrayItem* item) const {
414 // Don't attempt to align the arrow if the shelf is on the left or right.
415 if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM &&
416 shelf_alignment() != SHELF_ALIGNMENT_TOP)
417 return TrayBubbleView::InitParams::kArrowDefaultOffset;
419 std::map<SystemTrayItem*, views::View*>::const_iterator it =
420 tray_item_map_.find(item);
421 if (it == tray_item_map_.end())
422 return TrayBubbleView::InitParams::kArrowDefaultOffset;
424 const views::View* item_view = it->second;
425 if (item_view->bounds().IsEmpty()) {
426 // The bounds of item could be still empty if it does not have a visible
427 // tray view. In that case, use the default (minimum) offset.
428 return TrayBubbleView::InitParams::kArrowDefaultOffset;
431 gfx::Point point(item_view->width() / 2, 0);
432 ConvertPointToWidget(item_view, &point);
433 return point.x();
436 void SystemTray::ShowDefaultViewWithOffset(BubbleCreationType creation_type,
437 int arrow_offset,
438 bool persistent) {
439 if (creation_type != BUBBLE_USE_EXISTING) {
440 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
441 ash::UMA_STATUS_AREA_MENU_OPENED);
443 ShowItems(items_.get(), false, true, creation_type, arrow_offset, persistent);
446 void SystemTray::ShowItems(const std::vector<SystemTrayItem*>& items,
447 bool detailed,
448 bool can_activate,
449 BubbleCreationType creation_type,
450 int arrow_offset,
451 bool persistent) {
452 // No system tray bubbles in kiosk mode.
453 if (Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus() ==
454 ash::user::LOGGED_IN_KIOSK_APP) {
455 return;
458 // Destroy any existing bubble and create a new one.
459 SystemTrayBubble::BubbleType bubble_type = detailed ?
460 SystemTrayBubble::BUBBLE_TYPE_DETAILED :
461 SystemTrayBubble::BUBBLE_TYPE_DEFAULT;
463 // Destroy the notification bubble here so that it doesn't get rebuilt
464 // while we add items to the main bubble_ (e.g. in HideNotificationView).
465 notification_bubble_.reset();
466 if (system_bubble_.get() && creation_type == BUBBLE_USE_EXISTING) {
467 system_bubble_->bubble()->UpdateView(items, bubble_type);
468 // If ChromeVox is enabled, focus the default item if no item is focused.
469 if (Shell::GetInstance()->accessibility_delegate()->
470 IsSpokenFeedbackEnabled()) {
471 system_bubble_->bubble()->FocusDefaultIfNeeded();
473 } else {
474 // Remember if the menu is a single property (like e.g. volume) or the
475 // full tray menu. Note that in case of the |BUBBLE_USE_EXISTING| case
476 // above, |full_system_tray_menu_| does not get changed since the fact that
477 // the menu is full (or not) doesn't change even if a "single property"
478 // (like network) replaces most of the menu.
479 full_system_tray_menu_ = items.size() > 1;
480 // The menu width is fixed, and it is a per language setting.
481 int menu_width = std::max(kMinimumSystemTrayMenuWidth,
482 Shell::GetInstance()->system_tray_delegate()->GetSystemTrayMenuWidth());
484 TrayBubbleView::InitParams init_params(TrayBubbleView::ANCHOR_TYPE_TRAY,
485 GetAnchorAlignment(),
486 menu_width,
487 kTrayPopupMaxWidth);
488 init_params.can_activate = can_activate;
489 init_params.first_item_has_no_margin = true;
490 if (detailed) {
491 // This is the case where a volume control or brightness control bubble
492 // is created.
493 init_params.max_height = default_bubble_height_;
494 init_params.arrow_color = kBackgroundColor;
495 } else {
496 init_params.arrow_color = kHeaderBackgroundColor;
498 init_params.arrow_offset = arrow_offset;
499 if (bubble_type == SystemTrayBubble::BUBBLE_TYPE_DEFAULT)
500 init_params.close_on_deactivate = !persistent;
501 // For Volume and Brightness we don't want to show an arrow when
502 // they are shown in a bubble by themselves.
503 init_params.arrow_paint_type = views::BubbleBorder::PAINT_NORMAL;
504 if (items.size() == 1 && items[0]->ShouldHideArrow())
505 init_params.arrow_paint_type = views::BubbleBorder::PAINT_TRANSPARENT;
506 SystemTrayBubble* bubble = new SystemTrayBubble(this, items, bubble_type);
507 system_bubble_.reset(new SystemBubbleWrapper(bubble));
508 system_bubble_->InitView(this, tray_container(), &init_params, persistent);
510 // Save height of default view for creating detailed views directly.
511 if (!detailed)
512 default_bubble_height_ = system_bubble_->bubble_view()->height();
514 if (detailed && items.size() > 0)
515 detailed_item_ = items[0];
516 else
517 detailed_item_ = NULL;
519 UpdateNotificationBubble(); // State changed, re-create notifications.
520 if (!notification_bubble_)
521 UpdateWebNotifications();
522 GetShelfLayoutManager()->UpdateAutoHideState();
524 // When we show the system menu in our alternate shelf layout, we need to
525 // tint the background.
526 if (full_system_tray_menu_)
527 SetDrawBackgroundAsActive(true);
530 void SystemTray::UpdateNotificationBubble() {
531 // Only show the notification bubble if we have notifications.
532 if (notification_items_.empty()) {
533 DestroyNotificationBubble();
534 return;
536 // Destroy the existing bubble before constructing a new one.
537 notification_bubble_.reset();
538 SystemTrayBubble* notification_bubble;
539 notification_bubble = new SystemTrayBubble(
540 this, notification_items_, SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION);
541 views::View* anchor;
542 TrayBubbleView::AnchorType anchor_type;
543 // Tray items might want to show notifications while we are creating and
544 // initializing the |system_bubble_| - but it might not be fully initialized
545 // when coming here - this would produce a crashed like crbug.com/247416.
546 // As such we check the existence of the widget here.
547 if (system_bubble_.get() &&
548 system_bubble_->bubble_view() &&
549 system_bubble_->bubble_view()->GetWidget()) {
550 anchor = system_bubble_->bubble_view();
551 anchor_type = TrayBubbleView::ANCHOR_TYPE_BUBBLE;
552 } else {
553 anchor = tray_container();
554 anchor_type = TrayBubbleView::ANCHOR_TYPE_TRAY;
556 TrayBubbleView::InitParams init_params(anchor_type,
557 GetAnchorAlignment(),
558 kTrayPopupMinWidth,
559 kTrayPopupMaxWidth);
560 init_params.first_item_has_no_margin = true;
561 init_params.arrow_color = kBackgroundColor;
562 init_params.arrow_offset = GetTrayXOffset(notification_items_[0]);
563 notification_bubble_.reset(new SystemBubbleWrapper(notification_bubble));
564 notification_bubble_->InitView(this, anchor, &init_params, false);
566 if (notification_bubble->bubble_view()->child_count() == 0) {
567 // It is possible that none of the items generated actual notifications.
568 DestroyNotificationBubble();
569 return;
571 if (hide_notifications_)
572 notification_bubble->SetVisible(false);
573 else
574 UpdateWebNotifications();
577 void SystemTray::UpdateWebNotifications() {
578 TrayBubbleView* bubble_view = NULL;
579 if (notification_bubble_)
580 bubble_view = notification_bubble_->bubble_view();
581 else if (system_bubble_)
582 bubble_view = system_bubble_->bubble_view();
584 int height = 0;
585 if (bubble_view) {
586 gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow(
587 bubble_view->GetWidget()->GetNativeView()).work_area();
588 if (GetShelfLayoutManager()->GetAlignment() != SHELF_ALIGNMENT_TOP) {
589 height = std::max(
590 0, work_area.height() - bubble_view->GetBoundsInScreen().y());
591 } else {
592 height = std::max(
593 0, bubble_view->GetBoundsInScreen().bottom() - work_area.y());
596 status_area_widget()->web_notification_tray()->SetSystemTrayHeight(height);
599 base::string16 SystemTray::GetAccessibleTimeString(
600 const base::Time& now) const {
601 base::HourClockType hour_type =
602 ash::Shell::GetInstance()->system_tray_delegate()->GetHourClockType();
603 return base::TimeFormatTimeOfDayWithHourClockType(
604 now, hour_type, base::kKeepAmPm);
607 void SystemTray::SetShelfAlignment(ShelfAlignment alignment) {
608 if (alignment == shelf_alignment())
609 return;
610 TrayBackgroundView::SetShelfAlignment(alignment);
611 UpdateAfterShelfAlignmentChange(alignment);
612 // Destroy any existing bubble so that it is rebuilt correctly.
613 CloseSystemBubbleAndDeactivateSystemTray();
614 // Rebuild any notification bubble.
615 if (notification_bubble_) {
616 notification_bubble_.reset();
617 UpdateNotificationBubble();
621 void SystemTray::AnchorUpdated() {
622 if (notification_bubble_) {
623 notification_bubble_->bubble_view()->UpdateBubble();
624 // Ensure that the notification buble is above the shelf/status area.
625 notification_bubble_->bubble_view()->GetWidget()->StackAtTop();
626 UpdateBubbleViewArrow(notification_bubble_->bubble_view());
628 if (system_bubble_) {
629 system_bubble_->bubble_view()->UpdateBubble();
630 UpdateBubbleViewArrow(system_bubble_->bubble_view());
634 void SystemTray::BubbleResized(const TrayBubbleView* bubble_view) {
635 UpdateWebNotifications();
638 void SystemTray::HideBubbleWithView(const TrayBubbleView* bubble_view) {
639 if (system_bubble_.get() && bubble_view == system_bubble_->bubble_view()) {
640 DestroySystemBubble();
641 UpdateNotificationBubble(); // State changed, re-create notifications.
642 GetShelfLayoutManager()->UpdateAutoHideState();
643 } else if (notification_bubble_.get() &&
644 bubble_view == notification_bubble_->bubble_view()) {
645 DestroyNotificationBubble();
649 bool SystemTray::ClickedOutsideBubble() {
650 if (!system_bubble_ || system_bubble_->is_persistent())
651 return false;
652 HideBubbleWithView(system_bubble_->bubble_view());
653 return true;
656 void SystemTray::BubbleViewDestroyed() {
657 if (system_bubble_) {
658 system_bubble_->bubble()->DestroyItemViews();
659 system_bubble_->bubble()->BubbleViewDestroyed();
663 void SystemTray::OnMouseEnteredView() {
664 if (system_bubble_)
665 system_bubble_->bubble()->StopAutoCloseTimer();
668 void SystemTray::OnMouseExitedView() {
669 if (system_bubble_)
670 system_bubble_->bubble()->RestartAutoCloseTimer();
673 base::string16 SystemTray::GetAccessibleNameForBubble() {
674 return GetAccessibleNameForTray();
677 gfx::Rect SystemTray::GetAnchorRect(
678 views::Widget* anchor_widget,
679 TrayBubbleView::AnchorType anchor_type,
680 TrayBubbleView::AnchorAlignment anchor_alignment) const {
681 return GetBubbleAnchorRect(anchor_widget, anchor_type, anchor_alignment);
684 void SystemTray::HideBubble(const TrayBubbleView* bubble_view) {
685 HideBubbleWithView(bubble_view);
688 views::View* SystemTray::GetTrayItemViewForTest(SystemTrayItem* item) {
689 std::map<SystemTrayItem*, views::View*>::iterator it =
690 tray_item_map_.find(item);
691 return it == tray_item_map_.end() ? NULL : it->second;
694 TrayDate* SystemTray::GetTrayDateForTesting() const { return tray_date_; }
696 bool SystemTray::PerformAction(const ui::Event& event) {
697 // If we're already showing the default view, hide it; otherwise, show it
698 // (and hide any popup that's currently shown).
699 if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DEFAULT)) {
700 system_bubble_->bubble()->Close();
701 } else {
702 int arrow_offset = TrayBubbleView::InitParams::kArrowDefaultOffset;
703 if (event.IsMouseEvent() || event.type() == ui::ET_GESTURE_TAP) {
704 const ui::LocatedEvent& located_event =
705 static_cast<const ui::LocatedEvent&>(event);
706 if (shelf_alignment() == SHELF_ALIGNMENT_BOTTOM ||
707 shelf_alignment() == SHELF_ALIGNMENT_TOP) {
708 gfx::Point point(located_event.x(), 0);
709 ConvertPointToWidget(this, &point);
710 arrow_offset = point.x();
713 ShowDefaultViewWithOffset(BUBBLE_CREATE_NEW, arrow_offset, false);
715 return true;
718 void SystemTray::CloseSystemBubbleAndDeactivateSystemTray() {
719 system_bubble_.reset();
720 // When closing a system bubble with the alternate shelf layout, we need to
721 // turn off the active tinting of the shelf.
722 if (full_system_tray_menu_) {
723 SetDrawBackgroundAsActive(false);
724 full_system_tray_menu_ = false;
728 } // namespace ash