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 "ash/system/tray/system_tray.h" 6 7 #include "ash/ash_switches.h" 8 #include "ash/shelf/shelf_layout_manager.h" 9 #include "ash/shell.h" 10 #include "ash/shell/panel_window.h" 11 #include "ash/shell_window_ids.h" 12 #include "ash/system/bluetooth/tray_bluetooth.h" 13 #include "ash/system/brightness/tray_brightness.h" 14 #include "ash/system/chromeos/tray_tracing.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/logout_button/tray_logout_button.h" 19 #include "ash/system/monitor/tray_monitor.h" 20 #include "ash/system/session_length_limit/tray_session_length_limit.h" 21 #include "ash/system/status_area_widget.h" 22 #include "ash/system/tray/system_tray_delegate.h" 23 #include "ash/system/tray/system_tray_item.h" 24 #include "ash/system/tray/tray_bubble_wrapper.h" 25 #include "ash/system/tray/tray_constants.h" 26 #include "ash/system/tray_accessibility.h" 27 #include "ash/system/tray_caps_lock.h" 28 #include "ash/system/tray_update.h" 29 #include "ash/system/user/login_status.h" 30 #include "ash/system/user/tray_user.h" 31 #include "ash/system/web_notification/web_notification_tray.h" 32 #include "base/command_line.h" 33 #include "base/logging.h" 34 #include "base/strings/utf_string_conversions.h" 35 #include "base/timer/timer.h" 36 #include "grit/ash_strings.h" 37 #include "ui/aura/root_window.h" 38 #include "ui/base/events/event_constants.h" 39 #include "ui/base/l10n/l10n_util.h" 40 #include "ui/compositor/layer.h" 41 #include "ui/gfx/canvas.h" 42 #include "ui/gfx/screen.h" 43 #include "ui/gfx/skia_util.h" 44 #include "ui/views/border.h" 45 #include "ui/views/controls/label.h" 46 #include "ui/views/layout/box_layout.h" 47 #include "ui/views/layout/fill_layout.h" 48 #include "ui/views/view.h" 49 50 #if defined(OS_CHROMEOS) 51 #include "ash/system/chromeos/audio/tray_audio.h" 52 #include "ash/system/chromeos/enterprise/tray_enterprise.h" 53 #include "ash/system/chromeos/managed/tray_locally_managed_user.h" 54 #include "ash/system/chromeos/network/tray_network.h" 55 #include "ash/system/chromeos/network/tray_sms.h" 56 #include "ash/system/chromeos/network/tray_vpn.h" 57 #include "ash/system/chromeos/power/tray_power.h" 58 #include "ash/system/chromeos/screen_security/screen_capture_tray_item.h" 59 #include "ash/system/chromeos/screen_security/screen_share_tray_item.h" 60 #include "ash/system/chromeos/settings/tray_settings.h" 61 #include "ash/system/chromeos/tray_display.h" 62 #include "ui/message_center/message_center.h" 63 #endif 64 65 using views::TrayBubbleView; 66 67 namespace ash { 68 69 // The minimum width of the system tray menu width. 70 const int kMinimumSystemTrayMenuWidth = 300; 71 72 namespace internal { 73 74 // Class to initialize and manage the SystemTrayBubble and TrayBubbleWrapper 75 // instances for a bubble. 76 77 class SystemBubbleWrapper { 78 public: 79 // Takes ownership of |bubble|. 80 explicit SystemBubbleWrapper(internal::SystemTrayBubble* bubble) 81 : bubble_(bubble) { 82 } 83 84 // Initializes the bubble view and creates |bubble_wrapper_|. 85 void InitView(TrayBackgroundView* tray, 86 views::View* anchor, 87 TrayBubbleView::InitParams* init_params) { 88 DCHECK(anchor); 89 user::LoginStatus login_status = 90 Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus(); 91 bubble_->InitView(anchor, login_status, init_params); 92 bubble_wrapper_.reset( 93 new internal::TrayBubbleWrapper(tray, bubble_->bubble_view())); 94 if (ash::switches::UseAlternateShelfLayout()) { 95 // The system bubble should not have an arrow. 96 bubble_->bubble_view()->SetArrowPaintType( 97 views::BubbleBorder::PAINT_NONE); 98 } 99 } 100 101 // Convenience accessors: 102 SystemTrayBubble* bubble() const { return bubble_.get(); } 103 SystemTrayBubble::BubbleType bubble_type() const { 104 return bubble_->bubble_type(); 105 } 106 TrayBubbleView* bubble_view() const { return bubble_->bubble_view(); } 107 108 private: 109 scoped_ptr<internal::SystemTrayBubble> bubble_; 110 scoped_ptr<internal::TrayBubbleWrapper> bubble_wrapper_; 111 112 DISALLOW_COPY_AND_ASSIGN(SystemBubbleWrapper); 113 }; 114 115 } // namespace internal 116 117 // SystemTray 118 119 using internal::SystemTrayBubble; 120 121 SystemTray::SystemTray(internal::StatusAreaWidget* status_area_widget) 122 : internal::TrayBackgroundView(status_area_widget), 123 items_(), 124 default_bubble_height_(0), 125 hide_notifications_(false), 126 full_system_tray_menu_(false), 127 tray_accessibility_(NULL) { 128 SetContentsBackground(); 129 } 130 131 SystemTray::~SystemTray() { 132 // Destroy any child views that might have back pointers before ~View(). 133 system_bubble_.reset(); 134 notification_bubble_.reset(); 135 for (std::vector<SystemTrayItem*>::iterator it = items_.begin(); 136 it != items_.end(); 137 ++it) { 138 (*it)->DestroyTrayView(); 139 } 140 } 141 142 void SystemTray::InitializeTrayItems(SystemTrayDelegate* delegate) { 143 internal::TrayBackgroundView::Initialize(); 144 CreateItems(delegate); 145 } 146 147 void SystemTray::CreateItems(SystemTrayDelegate* delegate) { 148 #if !defined(OS_WIN) 149 AddTrayItem(new internal::TraySessionLengthLimit(this)); 150 AddTrayItem(new internal::TrayLogoutButton(this)); 151 // In multi-profile user mode we can have multiple user tiles. 152 ash::Shell* shell = ash::Shell::GetInstance(); 153 int maximum_user_profiles = 154 shell->delegate()->IsMultiProfilesEnabled() ? 155 shell->session_state_delegate()->GetMaximumNumberOfLoggedInUsers() : 156 0; 157 // Note: We purposely use one more item then logged in users to account for 158 // the additional separator. 159 for (int i = 0; i <= maximum_user_profiles; i++) 160 AddTrayItem(new internal::TrayUser(this, i)); 161 162 #endif 163 #if defined(OS_CHROMEOS) 164 AddTrayItem(new internal::TrayEnterprise(this)); 165 AddTrayItem(new internal::TrayLocallyManagedUser(this)); 166 #endif 167 AddTrayItem(new internal::TrayIME(this)); 168 tray_accessibility_ = new internal::TrayAccessibility(this); 169 AddTrayItem(tray_accessibility_); 170 #if defined(OS_CHROMEOS) 171 AddTrayItem(new internal::TrayTracing(this)); 172 AddTrayItem( 173 new internal::TrayPower(this, message_center::MessageCenter::Get())); 174 #endif 175 #if defined(OS_CHROMEOS) 176 AddTrayItem(new internal::TrayNetwork(this)); 177 AddTrayItem(new internal::TrayVPN(this)); 178 AddTrayItem(new internal::TraySms(this)); 179 #endif 180 #if !defined(OS_WIN) 181 AddTrayItem(new internal::TrayBluetooth(this)); 182 #endif 183 AddTrayItem(new internal::TrayDrive(this)); 184 #if defined(OS_CHROMEOS) 185 AddTrayItem(new internal::TrayDisplay(this)); 186 AddTrayItem(new internal::ScreenCaptureTrayItem(this)); 187 AddTrayItem(new internal::ScreenShareTrayItem(this)); 188 AddTrayItem(new internal::TrayAudio(this)); 189 #endif 190 #if !defined(OS_WIN) 191 AddTrayItem(new internal::TrayBrightness(this)); 192 AddTrayItem(new internal::TrayCapsLock(this)); 193 #endif 194 #if defined(OS_CHROMEOS) 195 AddTrayItem(new internal::TraySettings(this)); 196 #endif 197 AddTrayItem(new internal::TrayUpdate(this)); 198 AddTrayItem(new internal::TrayDate(this)); 199 200 #if defined(OS_LINUX) 201 // Add memory monitor if enabled. 202 CommandLine* cmd = CommandLine::ForCurrentProcess(); 203 if (cmd->HasSwitch(ash::switches::kAshEnableMemoryMonitor)) 204 AddTrayItem(new internal::TrayMonitor(this)); 205 #endif 206 207 SetVisible(ash::Shell::GetInstance()->system_tray_delegate()-> 208 GetTrayVisibilityOnStartup()); 209 } 210 211 void SystemTray::AddTrayItem(SystemTrayItem* item) { 212 items_.push_back(item); 213 214 SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate(); 215 views::View* tray_item = item->CreateTrayView(delegate->GetUserLoginStatus()); 216 item->UpdateAfterShelfAlignmentChange(shelf_alignment()); 217 218 if (tray_item) { 219 tray_container()->AddChildViewAt(tray_item, 0); 220 PreferredSizeChanged(); 221 tray_item_map_[item] = tray_item; 222 } 223 } 224 225 void SystemTray::RemoveTrayItem(SystemTrayItem* item) { 226 NOTIMPLEMENTED(); 227 } 228 229 const std::vector<SystemTrayItem*>& SystemTray::GetTrayItems() const { 230 return items_.get(); 231 } 232 233 void SystemTray::ShowDefaultView(BubbleCreationType creation_type) { 234 ShowDefaultViewWithOffset(creation_type, 235 TrayBubbleView::InitParams::kArrowDefaultOffset); 236 } 237 238 void SystemTray::ShowDetailedView(SystemTrayItem* item, 239 int close_delay, 240 bool activate, 241 BubbleCreationType creation_type) { 242 std::vector<SystemTrayItem*> items; 243 items.push_back(item); 244 ShowItems(items, true, activate, creation_type, GetTrayXOffset(item)); 245 if (system_bubble_) 246 system_bubble_->bubble()->StartAutoCloseTimer(close_delay); 247 } 248 249 void SystemTray::SetDetailedViewCloseDelay(int close_delay) { 250 if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DETAILED)) 251 system_bubble_->bubble()->StartAutoCloseTimer(close_delay); 252 } 253 254 void SystemTray::HideDetailedView(SystemTrayItem* item) { 255 if (item != detailed_item_) 256 return; 257 DestroySystemBubble(); 258 UpdateNotificationBubble(); 259 } 260 261 void SystemTray::ShowNotificationView(SystemTrayItem* item) { 262 if (std::find(notification_items_.begin(), notification_items_.end(), item) 263 != notification_items_.end()) 264 return; 265 notification_items_.push_back(item); 266 UpdateNotificationBubble(); 267 } 268 269 void SystemTray::HideNotificationView(SystemTrayItem* item) { 270 std::vector<SystemTrayItem*>::iterator found_iter = 271 std::find(notification_items_.begin(), notification_items_.end(), item); 272 if (found_iter == notification_items_.end()) 273 return; 274 notification_items_.erase(found_iter); 275 // Only update the notification bubble if visible (i.e. don't create one). 276 if (notification_bubble_) 277 UpdateNotificationBubble(); 278 } 279 280 void SystemTray::UpdateAfterLoginStatusChange(user::LoginStatus login_status) { 281 DestroySystemBubble(); 282 UpdateNotificationBubble(); 283 284 for (std::vector<SystemTrayItem*>::iterator it = items_.begin(); 285 it != items_.end(); 286 ++it) { 287 (*it)->UpdateAfterLoginStatusChange(login_status); 288 } 289 290 // Items default to SHELF_ALIGNMENT_BOTTOM. Update them if the initial 291 // position of the shelf differs. 292 if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM) 293 UpdateAfterShelfAlignmentChange(shelf_alignment()); 294 295 SetVisible(true); 296 PreferredSizeChanged(); 297 } 298 299 void SystemTray::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) { 300 for (std::vector<SystemTrayItem*>::iterator it = items_.begin(); 301 it != items_.end(); 302 ++it) { 303 (*it)->UpdateAfterShelfAlignmentChange(alignment); 304 } 305 } 306 307 void SystemTray::SetHideNotifications(bool hide_notifications) { 308 if (notification_bubble_) 309 notification_bubble_->bubble()->SetVisible(!hide_notifications); 310 hide_notifications_ = hide_notifications; 311 } 312 313 bool SystemTray::ShouldShowLauncher() const { 314 return system_bubble_.get() && system_bubble_->bubble()->ShouldShowLauncher(); 315 } 316 317 bool SystemTray::HasSystemBubble() const { 318 return system_bubble_.get() != NULL; 319 } 320 321 bool SystemTray::HasNotificationBubble() const { 322 return notification_bubble_.get() != NULL; 323 } 324 325 bool SystemTray::IsPressed() { 326 // Only when a full system tray bubble gets shown true will be returned. 327 // Small bubbles (like audio modifications via keyboard) should return false. 328 // Since showing the e.g. network portion of the system tray menu will convert 329 // the |system_bubble_| from type |BUBBLE_TYPE_DEFAULT| into 330 // |BUBBLE_TYPE_DETAILED| the full tray cannot reliably be checked trhough the 331 // type. As such |full_system_tray_menu_| gets checked here. 332 return HasSystemBubble() && full_system_tray_menu_; 333 } 334 335 internal::SystemTrayBubble* SystemTray::GetSystemBubble() { 336 if (!system_bubble_) 337 return NULL; 338 return system_bubble_->bubble(); 339 } 340 341 bool SystemTray::IsAnyBubbleVisible() const { 342 return ((system_bubble_.get() && 343 system_bubble_->bubble()->IsVisible()) || 344 (notification_bubble_.get() && 345 notification_bubble_->bubble()->IsVisible())); 346 } 347 348 bool SystemTray::IsMouseInNotificationBubble() const { 349 if (!notification_bubble_) 350 return false; 351 return notification_bubble_->bubble_view()->GetBoundsInScreen().Contains( 352 Shell::GetScreen()->GetCursorScreenPoint()); 353 } 354 355 bool SystemTray::CloseSystemBubble() const { 356 if (!system_bubble_) 357 return false; 358 system_bubble_->bubble()->Close(); 359 return true; 360 } 361 362 bool SystemTray::CloseNotificationBubbleForTest() const { 363 if (!notification_bubble_) 364 return false; 365 notification_bubble_->bubble()->Close(); 366 return true; 367 } 368 369 // Private methods. 370 371 bool SystemTray::HasSystemBubbleType(SystemTrayBubble::BubbleType type) { 372 DCHECK(type != SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION); 373 return system_bubble_.get() && system_bubble_->bubble_type() == type; 374 } 375 376 void SystemTray::DestroySystemBubble() { 377 system_bubble_.reset(); 378 detailed_item_ = NULL; 379 UpdateWebNotifications(); 380 } 381 382 void SystemTray::DestroyNotificationBubble() { 383 if (notification_bubble_) { 384 notification_bubble_.reset(); 385 UpdateWebNotifications(); 386 } 387 } 388 389 int SystemTray::GetTrayXOffset(SystemTrayItem* item) const { 390 // Don't attempt to align the arrow if the shelf is on the left or right. 391 if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM && 392 shelf_alignment() != SHELF_ALIGNMENT_TOP) 393 return TrayBubbleView::InitParams::kArrowDefaultOffset; 394 395 std::map<SystemTrayItem*, views::View*>::const_iterator it = 396 tray_item_map_.find(item); 397 if (it == tray_item_map_.end()) 398 return TrayBubbleView::InitParams::kArrowDefaultOffset; 399 400 const views::View* item_view = it->second; 401 if (item_view->bounds().IsEmpty()) { 402 // The bounds of item could be still empty if it does not have a visible 403 // tray view. In that case, use the default (minimum) offset. 404 return TrayBubbleView::InitParams::kArrowDefaultOffset; 405 } 406 407 gfx::Point point(item_view->width() / 2, 0); 408 ConvertPointToWidget(item_view, &point); 409 return point.x(); 410 } 411 412 void SystemTray::ShowDefaultViewWithOffset(BubbleCreationType creation_type, 413 int arrow_offset) { 414 ShowItems(items_.get(), false, true, creation_type, arrow_offset); 415 } 416 417 void SystemTray::ShowItems(const std::vector<SystemTrayItem*>& items, 418 bool detailed, 419 bool can_activate, 420 BubbleCreationType creation_type, 421 int arrow_offset) { 422 // No system tray bubbles in kiosk mode. 423 if (Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus() == 424 ash::user::LOGGED_IN_KIOSK_APP) { 425 return; 426 } 427 428 // Destroy any existing bubble and create a new one. 429 SystemTrayBubble::BubbleType bubble_type = detailed ? 430 SystemTrayBubble::BUBBLE_TYPE_DETAILED : 431 SystemTrayBubble::BUBBLE_TYPE_DEFAULT; 432 433 // Destroy the notification bubble here so that it doesn't get rebuilt 434 // while we add items to the main bubble_ (e.g. in HideNotificationView). 435 notification_bubble_.reset(); 436 if (system_bubble_.get() && creation_type == BUBBLE_USE_EXISTING) { 437 system_bubble_->bubble()->UpdateView(items, bubble_type); 438 } else { 439 // Remember if the menu is a single property (like e.g. volume) or the 440 // full tray menu. Note that in case of the |BUBBLE_USE_EXISTING| case 441 // above, |full_system_tray_menu_| does not get changed since the fact that 442 // the menu is full (or not) doesn't change even if a "single property" 443 // (like network) replaces most of the menu. 444 full_system_tray_menu_ = items.size() > 1; 445 // The menu width is fixed, and it is a per language setting. 446 int menu_width = std::max(kMinimumSystemTrayMenuWidth, 447 Shell::GetInstance()->system_tray_delegate()->GetSystemTrayMenuWidth()); 448 449 TrayBubbleView::InitParams init_params(TrayBubbleView::ANCHOR_TYPE_TRAY, 450 GetAnchorAlignment(), 451 menu_width, 452 kTrayPopupMaxWidth); 453 init_params.can_activate = can_activate; 454 init_params.first_item_has_no_margin = 455 ash::switches::UseAlternateShelfLayout(); 456 if (detailed) { 457 // This is the case where a volume control or brightness control bubble 458 // is created. 459 init_params.max_height = default_bubble_height_; 460 init_params.arrow_color = kBackgroundColor; 461 } else { 462 init_params.arrow_color = kHeaderBackgroundColor; 463 } 464 init_params.arrow_offset = arrow_offset; 465 // For Volume and Brightness we don't want to show an arrow when 466 // they are shown in a bubble by themselves. 467 init_params.arrow_paint_type = views::BubbleBorder::PAINT_NORMAL; 468 if (items.size() == 1 && items[0]->ShouldHideArrow()) 469 init_params.arrow_paint_type = views::BubbleBorder::PAINT_TRANSPARENT; 470 SystemTrayBubble* bubble = new SystemTrayBubble(this, items, bubble_type); 471 system_bubble_.reset(new internal::SystemBubbleWrapper(bubble)); 472 system_bubble_->InitView(this, tray_container(), &init_params); 473 } 474 // Save height of default view for creating detailed views directly. 475 if (!detailed) 476 default_bubble_height_ = system_bubble_->bubble_view()->height(); 477 478 if (detailed && items.size() > 0) 479 detailed_item_ = items[0]; 480 else 481 detailed_item_ = NULL; 482 483 UpdateNotificationBubble(); // State changed, re-create notifications. 484 if (!notification_bubble_) 485 UpdateWebNotifications(); 486 GetShelfLayoutManager()->UpdateAutoHideState(); 487 } 488 489 void SystemTray::UpdateNotificationBubble() { 490 // Only show the notification bubble if we have notifications. 491 if (notification_items_.empty()) { 492 DestroyNotificationBubble(); 493 return; 494 } 495 // Destroy the existing bubble before constructing a new one. 496 notification_bubble_.reset(); 497 SystemTrayBubble* notification_bubble; 498 notification_bubble = new SystemTrayBubble( 499 this, notification_items_, SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION); 500 views::View* anchor; 501 TrayBubbleView::AnchorType anchor_type; 502 // Tray items might want to show notifications while we are creating and 503 // initializing the |system_bubble_| - but it might not be fully initialized 504 // when coming here - this would produce a crashed like crbug.com/247416. 505 // As such we check the existence of the widget here. 506 if (system_bubble_.get() && 507 system_bubble_->bubble_view() && 508 system_bubble_->bubble_view()->GetWidget()) { 509 anchor = system_bubble_->bubble_view(); 510 anchor_type = TrayBubbleView::ANCHOR_TYPE_BUBBLE; 511 } else { 512 anchor = tray_container(); 513 anchor_type = TrayBubbleView::ANCHOR_TYPE_TRAY; 514 } 515 TrayBubbleView::InitParams init_params(anchor_type, 516 GetAnchorAlignment(), 517 kTrayPopupMinWidth, 518 kTrayPopupMaxWidth); 519 init_params.first_item_has_no_margin = 520 ash::switches::UseAlternateShelfLayout(); 521 init_params.arrow_color = kBackgroundColor; 522 init_params.arrow_offset = GetTrayXOffset(notification_items_[0]); 523 notification_bubble_.reset( 524 new internal::SystemBubbleWrapper(notification_bubble)); 525 notification_bubble_->InitView(this, anchor, &init_params); 526 527 if (notification_bubble->bubble_view()->child_count() == 0) { 528 // It is possible that none of the items generated actual notifications. 529 DestroyNotificationBubble(); 530 return; 531 } 532 if (hide_notifications_) 533 notification_bubble->SetVisible(false); 534 else 535 UpdateWebNotifications(); 536 } 537 538 void SystemTray::UpdateWebNotifications() { 539 TrayBubbleView* bubble_view = NULL; 540 if (notification_bubble_) 541 bubble_view = notification_bubble_->bubble_view(); 542 else if (system_bubble_) 543 bubble_view = system_bubble_->bubble_view(); 544 545 int height = 0; 546 if (bubble_view) { 547 gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow( 548 bubble_view->GetWidget()->GetNativeView()).work_area(); 549 if (GetShelfLayoutManager()->GetAlignment() != SHELF_ALIGNMENT_TOP) { 550 height = std::max( 551 0, work_area.height() - bubble_view->GetBoundsInScreen().y()); 552 } else { 553 height = std::max( 554 0, bubble_view->GetBoundsInScreen().bottom() - work_area.y()); 555 } 556 } 557 status_area_widget()->web_notification_tray()->SetSystemTrayHeight(height); 558 } 559 560 void SystemTray::SetShelfAlignment(ShelfAlignment alignment) { 561 if (alignment == shelf_alignment()) 562 return; 563 internal::TrayBackgroundView::SetShelfAlignment(alignment); 564 UpdateAfterShelfAlignmentChange(alignment); 565 // Destroy any existing bubble so that it is rebuilt correctly. 566 system_bubble_.reset(); 567 // Rebuild any notification bubble. 568 if (notification_bubble_) { 569 notification_bubble_.reset(); 570 UpdateNotificationBubble(); 571 } 572 } 573 574 void SystemTray::AnchorUpdated() { 575 if (notification_bubble_) { 576 notification_bubble_->bubble_view()->UpdateBubble(); 577 // Ensure that the notification buble is above the launcher/status area. 578 notification_bubble_->bubble_view()->GetWidget()->StackAtTop(); 579 UpdateBubbleViewArrow(notification_bubble_->bubble_view()); 580 } 581 if (system_bubble_) { 582 system_bubble_->bubble_view()->UpdateBubble(); 583 UpdateBubbleViewArrow(system_bubble_->bubble_view()); 584 } 585 } 586 587 base::string16 SystemTray::GetAccessibleNameForTray() { 588 return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBLE_NAME); 589 } 590 591 void SystemTray::HideBubbleWithView(const TrayBubbleView* bubble_view) { 592 if (system_bubble_.get() && bubble_view == system_bubble_->bubble_view()) { 593 DestroySystemBubble(); 594 UpdateNotificationBubble(); // State changed, re-create notifications. 595 GetShelfLayoutManager()->UpdateAutoHideState(); 596 } else if (notification_bubble_.get() && 597 bubble_view == notification_bubble_->bubble_view()) { 598 DestroyNotificationBubble(); 599 } 600 } 601 602 bool SystemTray::ClickedOutsideBubble() { 603 if (!system_bubble_) 604 return false; 605 HideBubbleWithView(system_bubble_->bubble_view()); 606 return true; 607 } 608 609 void SystemTray::BubbleViewDestroyed() { 610 if (system_bubble_) { 611 system_bubble_->bubble()->DestroyItemViews(); 612 system_bubble_->bubble()->BubbleViewDestroyed(); 613 } 614 } 615 616 void SystemTray::OnMouseEnteredView() { 617 if (system_bubble_) 618 system_bubble_->bubble()->StopAutoCloseTimer(); 619 } 620 621 void SystemTray::OnMouseExitedView() { 622 if (system_bubble_) 623 system_bubble_->bubble()->RestartAutoCloseTimer(); 624 } 625 626 base::string16 SystemTray::GetAccessibleNameForBubble() { 627 return GetAccessibleNameForTray(); 628 } 629 630 gfx::Rect SystemTray::GetAnchorRect( 631 views::Widget* anchor_widget, 632 TrayBubbleView::AnchorType anchor_type, 633 TrayBubbleView::AnchorAlignment anchor_alignment) { 634 return GetBubbleAnchorRect(anchor_widget, anchor_type, anchor_alignment); 635 } 636 637 void SystemTray::HideBubble(const TrayBubbleView* bubble_view) { 638 HideBubbleWithView(bubble_view); 639 } 640 641 bool SystemTray::PerformAction(const ui::Event& event) { 642 // If we're already showing the default view, hide it; otherwise, show it 643 // (and hide any popup that's currently shown). 644 if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DEFAULT)) { 645 system_bubble_->bubble()->Close(); 646 } else { 647 int arrow_offset = TrayBubbleView::InitParams::kArrowDefaultOffset; 648 if (event.IsMouseEvent() || event.type() == ui::ET_GESTURE_TAP) { 649 const ui::LocatedEvent& located_event = 650 static_cast<const ui::LocatedEvent&>(event); 651 if (shelf_alignment() == SHELF_ALIGNMENT_BOTTOM || 652 shelf_alignment() == SHELF_ALIGNMENT_TOP) { 653 gfx::Point point(located_event.x(), 0); 654 ConvertPointToWidget(this, &point); 655 arrow_offset = point.x(); 656 } 657 } 658 ShowDefaultViewWithOffset(BUBBLE_CREATE_NEW, arrow_offset); 659 } 660 return true; 661 } 662 663 } // namespace ash 664