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