• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/chromeos/tray_display.h"
6 
7 #include "ash/display/display_controller.h"
8 #include "ash/display/display_manager.h"
9 #include "ash/shell.h"
10 #include "ash/system/system_notifier.h"
11 #include "ash/system/tray/actionable_view.h"
12 #include "ash/system/tray/fixed_sized_image_view.h"
13 #include "ash/system/tray/system_tray.h"
14 #include "ash/system/tray/system_tray_delegate.h"
15 #include "ash/system/tray/tray_constants.h"
16 #include "ash/system/tray/tray_notification_view.h"
17 #include "base/bind.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "grit/ash_resources.h"
21 #include "grit/ash_strings.h"
22 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/message_center/message_center.h"
25 #include "ui/message_center/notification.h"
26 #include "ui/message_center/notification_delegate.h"
27 #include "ui/views/controls/image_view.h"
28 #include "ui/views/controls/label.h"
29 #include "ui/views/layout/box_layout.h"
30 
31 using message_center::Notification;
32 
33 namespace ash {
34 namespace internal {
35 namespace {
36 
GetDisplayManager()37 DisplayManager* GetDisplayManager() {
38   return Shell::GetInstance()->display_manager();
39 }
40 
GetDisplayName(int64 display_id)41 base::string16 GetDisplayName(int64 display_id) {
42   return UTF8ToUTF16(GetDisplayManager()->GetDisplayNameForId(display_id));
43 }
44 
GetDisplaySize(int64 display_id)45 base::string16 GetDisplaySize(int64 display_id) {
46   DisplayManager* display_manager = GetDisplayManager();
47 
48   const gfx::Display* display = &display_manager->GetDisplayForId(display_id);
49 
50   // We don't show display size for mirrored display. Fallback
51   // to empty string if this happens on release build.
52   bool mirrored_display = display_manager->mirrored_display_id() == display_id;
53   DCHECK(!mirrored_display);
54   if (mirrored_display)
55     return base::string16();
56 
57   DCHECK(display->is_valid());
58   return UTF8ToUTF16(display->size().ToString());
59 }
60 
61 // Returns 1-line information for the specified display, like
62 // "InternalDisplay: 1280x750"
GetDisplayInfoLine(int64 display_id)63 base::string16 GetDisplayInfoLine(int64 display_id) {
64   const DisplayInfo& display_info =
65       GetDisplayManager()->GetDisplayInfo(display_id);
66   if (GetDisplayManager()->mirrored_display_id() == display_id)
67     return GetDisplayName(display_id);
68 
69   base::string16 size_text = GetDisplaySize(display_id);
70   base::string16 display_data;
71   if (display_info.has_overscan()) {
72     display_data = l10n_util::GetStringFUTF16(
73         IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION,
74         size_text,
75         l10n_util::GetStringUTF16(
76             IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION_OVERSCAN));
77   } else {
78     display_data = size_text;
79   }
80 
81   return l10n_util::GetStringFUTF16(
82       IDS_ASH_STATUS_TRAY_DISPLAY_SINGLE_DISPLAY,
83       GetDisplayName(display_id),
84       display_data);
85 }
86 
GetAllDisplayInfo()87 base::string16 GetAllDisplayInfo() {
88   DisplayManager* display_manager = GetDisplayManager();
89   std::vector<base::string16> lines;
90   int64 internal_id = gfx::Display::kInvalidDisplayID;
91   // Make sure to show the internal display first.
92   if (display_manager->HasInternalDisplay() &&
93       display_manager->IsInternalDisplayId(
94           display_manager->first_display_id())) {
95     internal_id = display_manager->first_display_id();
96     lines.push_back(GetDisplayInfoLine(internal_id));
97   }
98 
99   for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
100     int64 id = display_manager->GetDisplayAt(i).id();
101     if (id == internal_id)
102       continue;
103     lines.push_back(GetDisplayInfoLine(id));
104   }
105 
106   return JoinString(lines, '\n');
107 }
108 
OpenSettings()109 void OpenSettings() {
110   // switch is intentionally introduced without default, to cause an error when
111   // a new type of login status is introduced.
112   switch (Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus()) {
113     case user::LOGGED_IN_NONE:
114     case user::LOGGED_IN_LOCKED:
115       return;
116 
117     case user::LOGGED_IN_USER:
118     case user::LOGGED_IN_OWNER:
119     case user::LOGGED_IN_GUEST:
120     case user::LOGGED_IN_RETAIL_MODE:
121     case user::LOGGED_IN_PUBLIC:
122     case user::LOGGED_IN_LOCALLY_MANAGED:
123     case user::LOGGED_IN_KIOSK_APP:
124       Shell::GetInstance()->system_tray_delegate()->ShowDisplaySettings();
125   }
126 }
127 
128 }  // namespace
129 
130 const char TrayDisplay::kNotificationId[] = "chrome://settings/display";
131 
132 class DisplayView : public internal::ActionableView {
133  public:
DisplayView()134   explicit DisplayView() {
135     SetLayoutManager(new views::BoxLayout(
136         views::BoxLayout::kHorizontal,
137         kTrayPopupPaddingHorizontal, 0,
138         kTrayPopupPaddingBetweenItems));
139 
140     ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
141     image_ = new internal::FixedSizedImageView(0, kTrayPopupItemHeight);
142     image_->SetImage(
143         bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY).ToImageSkia());
144     AddChildView(image_);
145 
146     label_ = new views::Label();
147     label_->SetMultiLine(true);
148     label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
149     AddChildView(label_);
150     Update();
151   }
152 
~DisplayView()153   virtual ~DisplayView() {}
154 
Update()155   void Update() {
156     base::string16 message = GetTrayDisplayMessage(NULL);
157     if (message.empty() && ShouldShowFirstDisplayInfo())
158       message = GetDisplayInfoLine(GetDisplayManager()->first_display_id());
159     SetVisible(!message.empty());
160     label_->SetText(message);
161     Layout();
162   }
163 
label() const164   const views::Label* label() const { return label_; }
165 
166   // Overridden from views::View.
GetTooltipText(const gfx::Point & p,base::string16 * tooltip) const167   virtual bool GetTooltipText(const gfx::Point& p,
168                               base::string16* tooltip) const OVERRIDE {
169     base::string16 tray_message = GetTrayDisplayMessage(NULL);
170     base::string16 display_message = GetAllDisplayInfo();
171     if (tray_message.empty() && display_message.empty())
172       return false;
173 
174     *tooltip = tray_message + ASCIIToUTF16("\n") + display_message;
175     return true;
176   }
177 
178   // Returns the name of the currently connected external display.
179   // This should not be used when the external display is used for
180   // mirroring.
GetExternalDisplayName()181   static base::string16 GetExternalDisplayName() {
182     DisplayManager* display_manager = GetDisplayManager();
183     DCHECK(!display_manager->IsMirrored());
184 
185     int64 external_id = gfx::Display::kInvalidDisplayID;
186     for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
187       int64 id = display_manager->GetDisplayAt(i).id();
188       if (id != gfx::Display::InternalDisplayId()) {
189         external_id = id;
190         break;
191       }
192     }
193 
194     if (external_id == gfx::Display::kInvalidDisplayID) {
195       return l10n_util::GetStringUTF16(
196           IDS_ASH_STATUS_TRAY_UNKNOWN_DISPLAY_NAME);
197     }
198 
199     // The external display name may have an annotation of "(width x height)" in
200     // case that the display is rotated or its resolution is changed.
201     base::string16 name = GetDisplayName(external_id);
202     const DisplayInfo& display_info =
203         display_manager->GetDisplayInfo(external_id);
204     if (display_info.rotation() != gfx::Display::ROTATE_0 ||
205         display_info.configured_ui_scale() != 1.0f ||
206         !display_info.overscan_insets_in_dip().empty()) {
207       name = l10n_util::GetStringFUTF16(
208           IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATED_NAME,
209           name, GetDisplaySize(external_id));
210     } else if (display_info.overscan_insets_in_dip().empty() &&
211                display_info.has_overscan()) {
212       name = l10n_util::GetStringFUTF16(
213           IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATED_NAME,
214           name, l10n_util::GetStringUTF16(
215               IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION_OVERSCAN));
216     }
217 
218     return name;
219   }
220 
GetTrayDisplayMessage(base::string16 * additional_message_out)221   static base::string16 GetTrayDisplayMessage(
222       base::string16* additional_message_out) {
223     DisplayManager* display_manager = GetDisplayManager();
224     if (display_manager->GetNumDisplays() > 1) {
225       if (GetDisplayManager()->HasInternalDisplay()) {
226         return l10n_util::GetStringFUTF16(
227             IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED, GetExternalDisplayName());
228       }
229       return l10n_util::GetStringUTF16(
230           IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL);
231     }
232 
233     if (display_manager->IsMirrored()) {
234       if (GetDisplayManager()->HasInternalDisplay()) {
235         return l10n_util::GetStringFUTF16(
236             IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING,
237             GetDisplayName(display_manager->mirrored_display_id()));
238       }
239       return l10n_util::GetStringUTF16(
240           IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING_NO_INTERNAL);
241     }
242 
243     int64 primary_id = Shell::GetScreen()->GetPrimaryDisplay().id();
244     if (display_manager->HasInternalDisplay() &&
245         !display_manager->IsInternalDisplayId(primary_id)) {
246       if (additional_message_out) {
247         *additional_message_out = l10n_util::GetStringUTF16(
248             IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED_DESCRIPTION);
249       }
250       return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED);
251     }
252 
253     return base::string16();
254   }
255 
256  private:
ShouldShowFirstDisplayInfo() const257   bool ShouldShowFirstDisplayInfo() const {
258     const DisplayInfo& display_info = GetDisplayManager()->GetDisplayInfo(
259         GetDisplayManager()->first_display_id());
260     return display_info.rotation() != gfx::Display::ROTATE_0 ||
261         display_info.configured_ui_scale() != 1.0f ||
262         !display_info.overscan_insets_in_dip().empty() ||
263         display_info.has_overscan();
264   }
265 
266   // Overridden from ActionableView.
PerformAction(const ui::Event & event)267   virtual bool PerformAction(const ui::Event& event) OVERRIDE {
268     OpenSettings();
269     return true;
270   }
271 
OnBoundsChanged(const gfx::Rect & previous_bounds)272   virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE {
273     int label_max_width = bounds().width() - kTrayPopupPaddingHorizontal * 2 -
274         kTrayPopupPaddingBetweenItems - image_->GetPreferredSize().width();
275     label_->SizeToFit(label_max_width);
276   }
277 
278   views::ImageView* image_;
279   views::Label* label_;
280 
281   DISALLOW_COPY_AND_ASSIGN(DisplayView);
282 };
283 
TrayDisplay(SystemTray * system_tray)284 TrayDisplay::TrayDisplay(SystemTray* system_tray)
285     : SystemTrayItem(system_tray),
286       default_(NULL) {
287   Shell::GetInstance()->display_controller()->AddObserver(this);
288   UpdateDisplayInfo(NULL);
289 }
290 
~TrayDisplay()291 TrayDisplay::~TrayDisplay() {
292   Shell::GetInstance()->display_controller()->RemoveObserver(this);
293 }
294 
UpdateDisplayInfo(TrayDisplay::DisplayInfoMap * old_info)295 void TrayDisplay::UpdateDisplayInfo(TrayDisplay::DisplayInfoMap* old_info) {
296   if (old_info)
297     old_info->swap(display_info_);
298   display_info_.clear();
299 
300   DisplayManager* display_manager = GetDisplayManager();
301   for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
302     int64 id = display_manager->GetDisplayAt(i).id();
303     display_info_[id] = display_manager->GetDisplayInfo(id);
304   }
305 }
306 
GetDisplayMessageForNotification(const TrayDisplay::DisplayInfoMap & old_info,base::string16 * message_out,base::string16 * additional_message_out)307 bool TrayDisplay::GetDisplayMessageForNotification(
308     const TrayDisplay::DisplayInfoMap& old_info,
309     base::string16* message_out,
310     base::string16* additional_message_out) {
311   // Display is added or removed. Use the same message as the one in
312   // the system tray.
313   if (display_info_.size() != old_info.size()) {
314     *message_out = DisplayView::GetTrayDisplayMessage(additional_message_out);
315     return true;
316   }
317 
318   for (DisplayInfoMap::const_iterator iter = display_info_.begin();
319        iter != display_info_.end(); ++iter) {
320     DisplayInfoMap::const_iterator old_iter = old_info.find(iter->first);
321     // The display's number is same but different displays. This happens
322     // for the transition between docked mode and mirrored display. Falls back
323     // to GetTrayDisplayMessage().
324     if (old_iter == old_info.end()) {
325       *message_out = DisplayView::GetTrayDisplayMessage(additional_message_out);
326       return true;
327     }
328 
329     if (iter->second.configured_ui_scale() !=
330         old_iter->second.configured_ui_scale()) {
331       *message_out = l10n_util::GetStringFUTF16(
332           IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED,
333           GetDisplayName(iter->first),
334           GetDisplaySize(iter->first));
335       return true;
336     }
337     if (iter->second.rotation() != old_iter->second.rotation()) {
338       int rotation_text_id = 0;
339       switch (iter->second.rotation()) {
340         case gfx::Display::ROTATE_0:
341           rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_STANDARD_ORIENTATION;
342           break;
343         case gfx::Display::ROTATE_90:
344           rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_90;
345           break;
346         case gfx::Display::ROTATE_180:
347           rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_180;
348           break;
349         case gfx::Display::ROTATE_270:
350           rotation_text_id = IDS_ASH_STATUS_TRAY_DISPLAY_ORIENTATION_270;
351           break;
352       }
353       *message_out = l10n_util::GetStringFUTF16(
354           IDS_ASH_STATUS_TRAY_DISPLAY_ROTATED,
355           GetDisplayName(iter->first),
356           l10n_util::GetStringUTF16(rotation_text_id));
357       return true;
358     }
359   }
360 
361   // Found nothing special
362   return false;
363 }
364 
CreateOrUpdateNotification(const base::string16 & message,const base::string16 & additional_message)365 void TrayDisplay::CreateOrUpdateNotification(
366     const base::string16& message,
367     const base::string16& additional_message) {
368   // Always remove the notification to make sure the notification appears
369   // as a popup in any situation.
370   message_center::MessageCenter::Get()->RemoveNotification(
371       kNotificationId, false /* by_user */);
372 
373   if (message.empty())
374     return;
375 
376   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
377   scoped_ptr<Notification> notification(new Notification(
378       message_center::NOTIFICATION_TYPE_SIMPLE,
379       kNotificationId,
380       message,
381       additional_message,
382       bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
383       base::string16(),  // display_source
384       message_center::NotifierId(
385           message_center::NotifierId::SYSTEM_COMPONENT,
386           system_notifier::kNotifierDisplay),
387       message_center::RichNotificationData(),
388       new message_center::HandleNotificationClickedDelegate(
389           base::Bind(&OpenSettings))));
390   message_center::MessageCenter::Get()->AddNotification(notification.Pass());
391 }
392 
CreateDefaultView(user::LoginStatus status)393 views::View* TrayDisplay::CreateDefaultView(user::LoginStatus status) {
394   DCHECK(default_ == NULL);
395   default_ = new DisplayView();
396   return default_;
397 }
398 
DestroyDefaultView()399 void TrayDisplay::DestroyDefaultView() {
400   default_ = NULL;
401 }
402 
OnDisplayConfigurationChanged()403 void TrayDisplay::OnDisplayConfigurationChanged() {
404   DisplayInfoMap old_info;
405   UpdateDisplayInfo(&old_info);
406 
407   if (default_)
408     default_->Update();
409 
410   if (!Shell::GetInstance()->system_tray_delegate()->
411           ShouldShowDisplayNotification()) {
412     return;
413   }
414 
415   base::string16 message;
416   base::string16 additional_message;
417   if (GetDisplayMessageForNotification(old_info, &message, &additional_message))
418     CreateOrUpdateNotification(message, additional_message);
419 }
420 
GetDefaultViewMessage() const421 base::string16 TrayDisplay::GetDefaultViewMessage() const {
422   if (!default_ || !default_->visible())
423     return base::string16();
424 
425   return static_cast<DisplayView*>(default_)->label()->text();
426 }
427 
428 }  // namespace internal
429 }  // namespace ash
430