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