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