summaryrefslogtreecommitdiff
path: root/chromium/ash/system/chromeos/tray_display.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/ash/system/chromeos/tray_display.cc')
-rw-r--r--chromium/ash/system/chromeos/tray_display.cc453
1 files changed, 453 insertions, 0 deletions
diff --git a/chromium/ash/system/chromeos/tray_display.cc b/chromium/ash/system/chromeos/tray_display.cc
new file mode 100644
index 00000000000..1f81f77670c
--- /dev/null
+++ b/chromium/ash/system/chromeos/tray_display.cc
@@ -0,0 +1,453 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/chromeos/tray_display.h"
+
+#include "ash/display/display_controller.h"
+#include "ash/display/display_manager.h"
+#include "ash/shell.h"
+#include "ash/system/tray/actionable_view.h"
+#include "ash/system/tray/fixed_sized_image_view.h"
+#include "ash/system/tray/system_tray.h"
+#include "ash/system/tray/system_tray_delegate.h"
+#include "ash/system/tray/tray_constants.h"
+#include "ash/system/tray/tray_notification_view.h"
+#include "base/bind.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "grit/ash_resources.h"
+#include "grit/ash_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/notification.h"
+#include "ui/message_center/notification_delegate.h"
+#include "ui/message_center/notification_list.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/box_layout.h"
+
+using message_center::Notification;
+
+namespace ash {
+namespace internal {
+namespace {
+
+static const char kDisplayNotificationId[] = "chrome://settings/display";
+
+DisplayManager* GetDisplayManager() {
+ return Shell::GetInstance()->display_manager();
+}
+
+base::string16 GetDisplayName(int64 display_id) {
+ return UTF8ToUTF16(GetDisplayManager()->GetDisplayNameForId(display_id));
+}
+
+base::string16 GetDisplaySize(int64 display_id) {
+ DisplayManager* display_manager = GetDisplayManager();
+
+ const gfx::Display* display = &display_manager->GetDisplayForId(display_id);
+ if (display_manager->IsMirrored() &&
+ display_manager->mirrored_display().id() == display_id) {
+ display = &display_manager->mirrored_display();
+ }
+
+ DCHECK(display->is_valid());
+ return UTF8ToUTF16(display->size().ToString());
+}
+
+// Returns 1-line information for the specified display, like
+// "InternalDisplay: 1280x750"
+base::string16 GetDisplayInfoLine(int64 display_id) {
+ const DisplayInfo& display_info =
+ GetDisplayManager()->GetDisplayInfo(display_id);
+
+ base::string16 size_text = GetDisplaySize(display_id);
+ base::string16 display_data;
+ if (display_info.has_overscan()) {
+ display_data = l10n_util::GetStringFUTF16(
+ IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION,
+ size_text,
+ l10n_util::GetStringUTF16(
+ IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION_OVERSCAN));
+ } else {
+ display_data = size_text;
+ }
+
+ return l10n_util::GetStringFUTF16(
+ IDS_ASH_STATUS_TRAY_DISPLAY_SINGLE_DISPLAY,
+ GetDisplayName(display_id),
+ display_data);
+}
+
+base::string16 GetAllDisplayInfo() {
+ DisplayManager* display_manager = GetDisplayManager();
+ std::vector<base::string16> lines;
+ int64 internal_id = gfx::Display::kInvalidDisplayID;
+ // Make sure to show the internal display first.
+ if (display_manager->HasInternalDisplay() &&
+ display_manager->IsInternalDisplayId(
+ display_manager->first_display_id())) {
+ internal_id = display_manager->first_display_id();
+ lines.push_back(GetDisplayInfoLine(internal_id));
+ }
+
+ for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
+ int64 id = display_manager->GetDisplayAt(i).id();
+ if (id == internal_id)
+ continue;
+ lines.push_back(GetDisplayInfoLine(id));
+ }
+
+ return JoinString(lines, '\n');
+}
+
+// Returns the name of the currently connected external display.
+base::string16 GetExternalDisplayName() {
+ DisplayManager* display_manager = GetDisplayManager();
+ int64 external_id = display_manager->mirrored_display().id();
+
+ if (external_id == gfx::Display::kInvalidDisplayID) {
+ int64 internal_display_id = gfx::Display::InternalDisplayId();
+ for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
+ int64 id = display_manager->GetDisplayAt(i).id();
+ if (id != internal_display_id) {
+ external_id = id;
+ break;
+ }
+ }
+ }
+
+ if (external_id == gfx::Display::kInvalidDisplayID)
+ return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_UNKNOWN_DISPLAY_NAME);
+
+ // The external display name may have an annotation of "(width x height)" in
+ // case that the display is rotated or its resolution is changed.
+ base::string16 name = GetDisplayName(external_id);
+ const DisplayInfo& display_info =
+ display_manager->GetDisplayInfo(external_id);
+ if (display_info.rotation() != gfx::Display::ROTATE_0 ||
+ display_info.ui_scale() != 1.0f ||
+ !display_info.overscan_insets_in_dip().empty()) {
+ name = l10n_util::GetStringFUTF16(
+ IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATED_NAME,
+ name, GetDisplaySize(external_id));
+ } else if (display_info.overscan_insets_in_dip().empty() &&
+ display_info.has_overscan()) {
+ name = l10n_util::GetStringFUTF16(
+ IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATED_NAME,
+ name, l10n_util::GetStringUTF16(
+ IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION_OVERSCAN));
+ }
+
+ return name;
+}
+
+base::string16 GetTrayDisplayMessage() {
+ DisplayManager* display_manager = GetDisplayManager();
+ if (display_manager->GetNumDisplays() > 1) {
+ if (GetDisplayManager()->HasInternalDisplay()) {
+ return l10n_util::GetStringFUTF16(
+ IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED, GetExternalDisplayName());
+ }
+ return l10n_util::GetStringUTF16(
+ IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL);
+ }
+
+ if (display_manager->IsMirrored()) {
+ if (GetDisplayManager()->HasInternalDisplay()) {
+ return l10n_util::GetStringFUTF16(
+ IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, GetExternalDisplayName());
+ }
+ return l10n_util::GetStringUTF16(
+ IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING_NO_INTERNAL);
+ }
+
+ int64 first_id = display_manager->first_display_id();
+ if (display_manager->HasInternalDisplay() &&
+ !display_manager->IsInternalDisplayId(first_id)) {
+ return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED);
+ }
+
+ return base::string16();
+}
+
+void OpenSettings(user::LoginStatus login_status) {
+ if (login_status == ash::user::LOGGED_IN_USER ||
+ login_status == ash::user::LOGGED_IN_OWNER ||
+ login_status == ash::user::LOGGED_IN_GUEST) {
+ ash::Shell::GetInstance()->system_tray_delegate()->ShowDisplaySettings();
+ }
+}
+
+void UpdateDisplayNotification(const base::string16& message) {
+ // Always remove the notification to make sure the notification appears
+ // as a popup in any situation.
+ message_center::MessageCenter::Get()->RemoveNotification(
+ kDisplayNotificationId, false /* by_user */);
+
+ if (message.empty())
+ return;
+
+ ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+ scoped_ptr<Notification> notification(new Notification(
+ message_center::NOTIFICATION_TYPE_SIMPLE,
+ kDisplayNotificationId,
+ message,
+ base::string16(), // body is intentionally empty, see crbug.com/265915
+ bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
+ base::string16(), // display_source
+ "", // extension_id
+ message_center::RichNotificationData(),
+ new message_center::HandleNotificationClickedDelegate(
+ base::Bind(&OpenSettings,
+ Shell::GetInstance()->system_tray_delegate()->
+ GetUserLoginStatus()))));
+ message_center::MessageCenter::Get()->AddNotification(notification.Pass());
+}
+
+} // namespace
+
+class DisplayView : public ash::internal::ActionableView {
+ public:
+ explicit DisplayView(user::LoginStatus login_status)
+ : login_status_(login_status) {
+ SetLayoutManager(new views::BoxLayout(
+ views::BoxLayout::kHorizontal,
+ ash::kTrayPopupPaddingHorizontal, 0,
+ ash::kTrayPopupPaddingBetweenItems));
+
+ ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+ image_ =
+ new ash::internal::FixedSizedImageView(0, ash::kTrayPopupItemHeight);
+ image_->SetImage(
+ bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY).ToImageSkia());
+ AddChildView(image_);
+
+ label_ = new views::Label();
+ label_->SetMultiLine(true);
+ label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ AddChildView(label_);
+ Update();
+ }
+
+ virtual ~DisplayView() {}
+
+ void Update() {
+ base::string16 message = GetTrayDisplayMessage();
+ if (message.empty() && ShouldShowFirstDisplayInfo())
+ message = GetDisplayInfoLine(GetDisplayManager()->first_display_id());
+ SetVisible(!message.empty());
+ label_->SetText(message);
+ }
+
+ views::Label* label() { return label_; }
+
+ // Overridden from views::View.
+ virtual bool GetTooltipText(const gfx::Point& p,
+ base::string16* tooltip) const OVERRIDE {
+ base::string16 tray_message = GetTrayDisplayMessage();
+ base::string16 display_message = GetAllDisplayInfo();
+ if (tray_message.empty() && display_message.empty())
+ return false;
+
+ *tooltip = tray_message + ASCIIToUTF16("\n") + display_message;
+ return true;
+ }
+
+ private:
+ bool ShouldShowFirstDisplayInfo() const {
+ const DisplayInfo& display_info = GetDisplayManager()->GetDisplayInfo(
+ GetDisplayManager()->first_display_id());
+ return display_info.rotation() != gfx::Display::ROTATE_0 ||
+ display_info.ui_scale() != 1.0f ||
+ !display_info.overscan_insets_in_dip().empty() ||
+ display_info.has_overscan();
+ }
+
+ // Overridden from ActionableView.
+ virtual bool PerformAction(const ui::Event& event) OVERRIDE {
+ OpenSettings(login_status_);
+ return true;
+ }
+
+ virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE {
+ int label_max_width = bounds().width() - kTrayPopupPaddingHorizontal * 2 -
+ kTrayPopupPaddingBetweenItems - image_->GetPreferredSize().width();
+ label_->SizeToFit(label_max_width);
+ PreferredSizeChanged();
+ }
+
+ user::LoginStatus login_status_;
+ views::ImageView* image_;
+ views::Label* label_;
+
+ DISALLOW_COPY_AND_ASSIGN(DisplayView);
+};
+
+class DisplayNotificationView : public TrayNotificationView {
+ public:
+ DisplayNotificationView(user::LoginStatus login_status,
+ TrayDisplay* tray_item,
+ const base::string16& message)
+ : TrayNotificationView(tray_item, IDR_AURA_UBER_TRAY_DISPLAY),
+ login_status_(login_status) {
+ StartAutoCloseTimer(kTrayPopupAutoCloseDelayForTextInSeconds);
+ Update(message);
+ }
+
+ virtual ~DisplayNotificationView() {}
+
+ void Update(const base::string16& message) {
+ if (message.empty()) {
+ owner()->HideNotificationView();
+ } else {
+ views::Label* label = new views::Label(message);
+ label->SetMultiLine(true);
+ label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ UpdateView(label);
+ RestartAutoCloseTimer();
+ }
+ }
+
+ // Overridden from TrayNotificationView:
+ virtual void OnClickAction() OVERRIDE {
+ OpenSettings(login_status_);
+ }
+
+ private:
+ user::LoginStatus login_status_;
+
+ DISALLOW_COPY_AND_ASSIGN(DisplayNotificationView);
+};
+
+TrayDisplay::TrayDisplay(SystemTray* system_tray)
+ : SystemTrayItem(system_tray),
+ default_(NULL) {
+ Shell::GetInstance()->display_controller()->AddObserver(this);
+ UpdateDisplayInfo(NULL);
+}
+
+TrayDisplay::~TrayDisplay() {
+ Shell::GetInstance()->display_controller()->RemoveObserver(this);
+}
+
+void TrayDisplay::UpdateDisplayInfo(TrayDisplay::DisplayInfoMap* old_info) {
+ if (old_info)
+ old_info->swap(display_info_);
+ display_info_.clear();
+
+ DisplayManager* display_manager = GetDisplayManager();
+ for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
+ int64 id = display_manager->GetDisplayAt(i).id();
+ display_info_[id] = display_manager->GetDisplayInfo(id);
+ }
+}
+
+bool TrayDisplay::GetDisplayMessageForNotification(
+ base::string16* message,
+ const TrayDisplay::DisplayInfoMap& old_info) {
+ // Display is added or removed. Use the same message as the one in
+ // the system tray.
+ if (display_info_.size() != old_info.size()) {
+ *message = GetTrayDisplayMessage();
+ return true;
+ }
+
+ for (DisplayInfoMap::const_iterator iter = display_info_.begin();
+ iter != display_info_.end(); ++iter) {
+ DisplayInfoMap::const_iterator old_iter = old_info.find(iter->first);
+ // The display's number is same but different displays. This happens
+ // for the transition between docked mode and mirrored display. Falls back
+ // to GetTrayDisplayMessage().
+ if (old_iter == old_info.end()) {
+ *message = GetTrayDisplayMessage();
+ return true;
+ }
+
+ if (iter->second.ui_scale() != old_iter->second.ui_scale()) {
+ *message = l10n_util::GetStringFUTF16(
+ IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED,
+ GetDisplayName(iter->first),
+ GetDisplaySize(iter->first));
+ return true;
+ }
+ if (iter->second.rotation() != old_iter->second.rotation()) {
+ int rotation_text_id = 0;
+ switch (iter->second.rotation()) {
+ case gfx::Display::ROTATE_0:
+ rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_STANDARD_ORIENTATION;
+ break;
+ case gfx::Display::ROTATE_90:
+ rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90;
+ break;
+ case gfx::Display::ROTATE_180:
+ rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180;
+ break;
+ case gfx::Display::ROTATE_270:
+ rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270;
+ break;
+ }
+ *message = l10n_util::GetStringFUTF16(
+ IDS_ASH_STATUS_TRAY_DISPLAY_ROTATED,
+ GetDisplayName(iter->first),
+ l10n_util::GetStringUTF16(rotation_text_id));
+ return true;
+ }
+ }
+
+ // Found nothing special
+ return false;
+}
+
+views::View* TrayDisplay::CreateDefaultView(user::LoginStatus status) {
+ DCHECK(default_ == NULL);
+ default_ = new DisplayView(status);
+ return default_;
+}
+
+void TrayDisplay::DestroyDefaultView() {
+ default_ = NULL;
+}
+
+void TrayDisplay::OnDisplayConfigurationChanged() {
+ DisplayInfoMap old_info;
+ UpdateDisplayInfo(&old_info);
+
+ if (!Shell::GetInstance()->system_tray_delegate()->
+ ShouldShowDisplayNotification()) {
+ return;
+ }
+
+ base::string16 message;
+ if (GetDisplayMessageForNotification(&message, old_info))
+ UpdateDisplayNotification(message);
+}
+
+base::string16 TrayDisplay::GetDefaultViewMessage() {
+ if (!default_ || !default_->visible())
+ return base::string16();
+
+ return static_cast<DisplayView*>(default_)->label()->text();
+}
+
+base::string16 TrayDisplay::GetNotificationMessage() {
+ message_center::NotificationList::Notifications notifications =
+ message_center::MessageCenter::Get()->GetNotifications();
+ for (message_center::NotificationList::Notifications::const_iterator iter =
+ notifications.begin(); iter != notifications.end(); ++iter) {
+ if ((*iter)->id() == kDisplayNotificationId)
+ return (*iter)->title();
+ }
+
+ return base::string16();
+}
+
+void TrayDisplay::CloseNotificationForTest() {
+ message_center::MessageCenter::Get()->RemoveNotification(
+ kDisplayNotificationId, false);
+}
+
+} // namespace internal
+} // namespace ash