tray_display.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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/tray/fixed_sized_image_view.h"
11#include "ash/system/tray/system_tray.h"
12#include "ash/system/tray/system_tray_delegate.h"
13#include "ash/system/tray/tray_constants.h"
14#include "ash/system/tray/tray_notification_view.h"
15#include "base/chromeos/chromeos_version.h"
16#include "base/strings/utf_string_conversions.h"
17#include "grit/ash_resources.h"
18#include "grit/ash_strings.h"
19#include "ui/base/l10n/l10n_util.h"
20#include "ui/base/resource/resource_bundle.h"
21#include "ui/views/controls/label.h"
22#include "ui/views/layout/box_layout.h"
23
24namespace ash {
25namespace internal {
26namespace {
27
28TrayDisplayMode GetCurrentTrayDisplayMode() {
29  DisplayManager* display_manager = Shell::GetInstance()->display_manager();
30  if (display_manager->GetNumDisplays() > 1)
31    return TRAY_DISPLAY_EXTENDED;
32
33  if (display_manager->IsMirrored())
34    return TRAY_DISPLAY_MIRRORED;
35
36  int64 first_id = display_manager->first_display_id();
37  if (display_manager->HasInternalDisplay() &&
38      !display_manager->IsInternalDisplayId(first_id)) {
39    return TRAY_DISPLAY_DOCKED;
40  }
41
42  return TRAY_DISPLAY_SINGLE;
43}
44
45// Returns the name of the currently connected external display.
46base::string16 GetExternalDisplayName() {
47  DisplayManager* display_manager = Shell::GetInstance()->display_manager();
48  int64 external_id = display_manager->mirrored_display().id();
49
50  if (external_id == gfx::Display::kInvalidDisplayID) {
51    int64 internal_display_id = gfx::Display::InternalDisplayId();
52    for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
53      int64 id = display_manager->GetDisplayAt(i)->id();
54      if (id != internal_display_id) {
55        external_id = id;
56        break;
57      }
58    }
59  }
60  if (external_id != gfx::Display::kInvalidDisplayID)
61    return UTF8ToUTF16(display_manager->GetDisplayNameForId(external_id));
62  return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_UNKNOWN_DISPLAY_NAME);
63}
64
65class DisplayViewBase {
66 public:
67  DisplayViewBase(user::LoginStatus login_status)
68      : login_status_(login_status) {
69    label_ = new views::Label();
70    label_->SetMultiLine(true);
71    label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
72  }
73
74  virtual ~DisplayViewBase() {
75  }
76
77 protected:
78  void OpenSettings() {
79    if (login_status_ == ash::user::LOGGED_IN_USER ||
80        login_status_ == ash::user::LOGGED_IN_OWNER ||
81        login_status_ == ash::user::LOGGED_IN_GUEST) {
82      ash::Shell::GetInstance()->system_tray_delegate()->ShowDisplaySettings();
83    }
84  }
85
86  bool UpdateLabelText() {
87    switch (GetCurrentTrayDisplayMode()) {
88      case TRAY_DISPLAY_SINGLE:
89        // TODO(oshima|mukai): Support single display mode for overscan
90        // alignment.
91        return false;
92      case TRAY_DISPLAY_EXTENDED:
93        if (Shell::GetInstance()->display_manager()->HasInternalDisplay()) {
94          label_->SetText(l10n_util::GetStringFUTF16(
95              IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED, GetExternalDisplayName()));
96        } else {
97          label_->SetText(l10n_util::GetStringUTF16(
98              IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL));
99        }
100        break;
101      case TRAY_DISPLAY_MIRRORED:
102        if (Shell::GetInstance()->display_manager()->HasInternalDisplay()) {
103          label_->SetText(l10n_util::GetStringFUTF16(
104              IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, GetExternalDisplayName()));
105        } else {
106          label_->SetText(l10n_util::GetStringUTF16(
107              IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING_NO_INTERNAL));
108        }
109        break;
110      case TRAY_DISPLAY_DOCKED:
111        label_->SetText(l10n_util::GetStringUTF16(
112            IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED));
113        break;
114    }
115    return true;
116  }
117
118  views::Label* label() { return label_; }
119
120 private:
121  user::LoginStatus login_status_;
122  views::Label* label_;
123
124  DISALLOW_COPY_AND_ASSIGN(DisplayViewBase);
125};
126
127}  // namespace
128
129class DisplayView : public DisplayViewBase,
130                    public ash::internal::ActionableView {
131 public:
132  explicit DisplayView(user::LoginStatus login_status)
133      : DisplayViewBase(login_status) {
134    SetLayoutManager(new
135        views::BoxLayout(views::BoxLayout::kHorizontal,
136        ash::kTrayPopupPaddingHorizontal, 0,
137        ash::kTrayPopupPaddingBetweenItems));
138
139    ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
140    image_ =
141        new ash::internal::FixedSizedImageView(0, ash::kTrayPopupItemHeight);
142    image_->SetImage(
143        bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY).ToImageSkia());
144    AddChildView(image_);
145    AddChildView(label());
146    Update();
147  }
148
149  virtual ~DisplayView() {}
150
151  void Update() {
152    SetVisible(UpdateLabelText());
153  }
154
155 private:
156  // Overridden from ActionableView.
157  virtual bool PerformAction(const ui::Event& event) OVERRIDE {
158    OpenSettings();
159    return true;
160  }
161
162  virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE {
163    int label_max_width = bounds().width() - kTrayPopupPaddingHorizontal * 2 -
164        kTrayPopupPaddingBetweenItems - image_->GetPreferredSize().width();
165    label()->SizeToFit(label_max_width);
166    PreferredSizeChanged();
167  }
168
169  views::ImageView* image_;
170
171  DISALLOW_COPY_AND_ASSIGN(DisplayView);
172};
173
174class DisplayNotificationView : public DisplayViewBase,
175                                public TrayNotificationView {
176 public:
177  DisplayNotificationView(user::LoginStatus login_status,
178                          TrayDisplay* tray_item)
179      : DisplayViewBase(login_status),
180        TrayNotificationView(tray_item, IDR_AURA_UBER_TRAY_DISPLAY) {
181    InitView(label());
182    StartAutoCloseTimer(kTrayPopupAutoCloseDelayForTextInSeconds);
183    Update();
184  }
185
186  virtual ~DisplayNotificationView() {}
187
188  void Update() {
189    if (UpdateLabelText())
190      RestartAutoCloseTimer();
191    else
192      owner()->HideNotificationView();
193  }
194
195  // Overridden from TrayNotificationView:
196  virtual void OnClickAction() OVERRIDE {
197    OpenSettings();
198  }
199
200 private:
201  DISALLOW_COPY_AND_ASSIGN(DisplayNotificationView);
202};
203
204TrayDisplay::TrayDisplay(SystemTray* system_tray)
205    : SystemTrayItem(system_tray),
206      default_(NULL),
207      notification_(NULL),
208      current_mode_(GetCurrentTrayDisplayMode()) {
209  Shell::GetInstance()->display_controller()->AddObserver(this);
210}
211
212TrayDisplay::~TrayDisplay() {
213  Shell::GetInstance()->display_controller()->RemoveObserver(this);
214}
215
216views::View* TrayDisplay::CreateDefaultView(user::LoginStatus status) {
217  DCHECK(default_ == NULL);
218  default_ = new DisplayView(status);
219  return default_;
220}
221
222views::View* TrayDisplay::CreateNotificationView(user::LoginStatus status) {
223  DCHECK(notification_ == NULL);
224  notification_ = new DisplayNotificationView(status, this);
225  return notification_;
226}
227
228void TrayDisplay::DestroyDefaultView() {
229  default_ = NULL;
230}
231
232void TrayDisplay::DestroyNotificationView() {
233  notification_ = NULL;
234}
235
236bool TrayDisplay::ShouldShowLauncher() const {
237  return false;
238}
239
240void TrayDisplay::OnDisplayConfigurationChanged() {
241  TrayDisplayMode new_mode = GetCurrentTrayDisplayMode();
242  if (current_mode_ != new_mode && new_mode != TRAY_DISPLAY_SINGLE) {
243    if (notification_)
244      notification_->Update();
245    else
246      ShowNotificationView();
247  }
248  current_mode_ = new_mode;
249}
250
251}  // namespace internal
252}  // namespace ash
253