tray_ime.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/ime/tray_ime.h"
6
7#include <vector>
8
9#include "ash/root_window_controller.h"
10#include "ash/shelf/shelf_widget.h"
11#include "ash/shell.h"
12#include "ash/system/tray/hover_highlight_view.h"
13#include "ash/system/tray/system_tray.h"
14#include "ash/system/tray/system_tray_delegate.h"
15#include "ash/system/tray/system_tray_notifier.h"
16#include "ash/system/tray/tray_constants.h"
17#include "ash/system/tray/tray_details_view.h"
18#include "ash/system/tray/tray_item_more.h"
19#include "ash/system/tray/tray_item_view.h"
20#include "ash/system/tray/tray_notification_view.h"
21#include "ash/system/tray/tray_utils.h"
22#include "base/logging.h"
23#include "base/strings/utf_string_conversions.h"
24#include "grit/ash_resources.h"
25#include "grit/ash_strings.h"
26#include "ui/base/l10n/l10n_util.h"
27#include "ui/base/resource/resource_bundle.h"
28#include "ui/gfx/font.h"
29#include "ui/gfx/image/image.h"
30#include "ui/views/controls/label.h"
31#include "ui/views/layout/box_layout.h"
32#include "ui/views/widget/widget.h"
33
34namespace ash {
35namespace internal {
36
37namespace tray {
38
39class IMEDefaultView : public TrayItemMore {
40 public:
41  explicit IMEDefaultView(SystemTrayItem* owner)
42      : TrayItemMore(owner, true) {
43    ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
44
45    SetImage(bundle.GetImageNamed(
46        IDR_AURA_UBER_TRAY_IME).ToImageSkia());
47
48    IMEInfo info;
49    Shell::GetInstance()->system_tray_delegate()->GetCurrentIME(&info);
50    UpdateLabel(info);
51  }
52
53  virtual ~IMEDefaultView() {}
54
55  void UpdateLabel(const IMEInfo& info) {
56    SetLabel(info.name);
57    SetAccessibleName(info.name);
58  }
59
60 private:
61  DISALLOW_COPY_AND_ASSIGN(IMEDefaultView);
62};
63
64class IMEDetailedView : public TrayDetailsView,
65                        public ViewClickListener {
66 public:
67  IMEDetailedView(SystemTrayItem* owner, user::LoginStatus login)
68      : TrayDetailsView(owner),
69        login_(login) {
70    SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
71    IMEInfoList list;
72    delegate->GetAvailableIMEList(&list);
73    IMEPropertyInfoList property_list;
74    delegate->GetCurrentIMEProperties(&property_list);
75    Update(list, property_list);
76  }
77
78  virtual ~IMEDetailedView() {}
79
80  void Update(const IMEInfoList& list,
81              const IMEPropertyInfoList& property_list) {
82    Reset();
83
84    AppendIMEList(list);
85    if (!property_list.empty())
86      AppendIMEProperties(property_list);
87    if (login_ != user::LOGGED_IN_NONE && login_ != user::LOGGED_IN_LOCKED)
88      AppendSettings();
89    AppendHeaderEntry();
90
91    Layout();
92    SchedulePaint();
93  }
94
95 private:
96  void AppendHeaderEntry() {
97    CreateSpecialRow(IDS_ASH_STATUS_TRAY_IME, this);
98  }
99
100  void AppendIMEList(const IMEInfoList& list) {
101    ime_map_.clear();
102    CreateScrollableList();
103    for (size_t i = 0; i < list.size(); i++) {
104      HoverHighlightView* container = new HoverHighlightView(this);
105      container->AddLabel(list[i].name,
106          list[i].selected ? gfx::Font::BOLD : gfx::Font::NORMAL);
107      scroll_content()->AddChildView(container);
108      ime_map_[container] = list[i].id;
109    }
110  }
111
112  void AppendIMEProperties(const IMEPropertyInfoList& property_list) {
113    property_map_.clear();
114    for (size_t i = 0; i < property_list.size(); i++) {
115      HoverHighlightView* container = new HoverHighlightView(this);
116      container->AddLabel(
117          property_list[i].name,
118          property_list[i].selected ? gfx::Font::BOLD : gfx::Font::NORMAL);
119      if (i == 0)
120        container->set_border(views::Border::CreateSolidSidedBorder(1, 0, 0, 0,
121        kBorderLightColor));
122      scroll_content()->AddChildView(container);
123      property_map_[container] = property_list[i].key;
124    }
125  }
126
127  void AppendSettings() {
128    HoverHighlightView* container = new HoverHighlightView(this);
129    container->AddLabel(ui::ResourceBundle::GetSharedInstance().
130        GetLocalizedString(IDS_ASH_STATUS_TRAY_IME_SETTINGS),
131        gfx::Font::NORMAL);
132    AddChildView(container);
133    settings_ = container;
134  }
135
136  // Overridden from ViewClickListener.
137  virtual void OnViewClicked(views::View* sender) OVERRIDE {
138    SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
139    if (sender == footer()->content()) {
140      owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
141    } else if (sender == settings_) {
142      delegate->ShowIMESettings();
143    } else {
144      std::map<views::View*, std::string>::const_iterator ime_find;
145      ime_find = ime_map_.find(sender);
146      if (ime_find != ime_map_.end()) {
147        std::string ime_id = ime_find->second;
148        delegate->SwitchIME(ime_id);
149        GetWidget()->Close();
150      } else {
151        std::map<views::View*, std::string>::const_iterator prop_find;
152        prop_find = property_map_.find(sender);
153        if (prop_find != property_map_.end()) {
154          const std::string key = prop_find->second;
155          delegate->ActivateIMEProperty(key);
156          GetWidget()->Close();
157        }
158      }
159    }
160  }
161
162  user::LoginStatus login_;
163
164  std::map<views::View*, std::string> ime_map_;
165  std::map<views::View*, std::string> property_map_;
166  views::View* settings_;
167
168  DISALLOW_COPY_AND_ASSIGN(IMEDetailedView);
169};
170
171class IMENotificationView : public TrayNotificationView {
172 public:
173  explicit IMENotificationView(TrayIME* owner)
174      : TrayNotificationView(owner, IDR_AURA_UBER_TRAY_IME) {
175    InitView(GetLabel());
176  }
177
178  void UpdateLabel() {
179    RestartAutoCloseTimer();
180    UpdateView(GetLabel());
181  }
182
183  // Overridden from TrayNotificationView.
184  virtual void OnClickAction() OVERRIDE {
185    owner()->PopupDetailedView(0, true);
186  }
187
188 private:
189  views::Label* GetLabel() {
190    SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
191    IMEInfo current;
192    delegate->GetCurrentIME(&current);
193
194    // TODO(zork): Use IDS_ASH_STATUS_TRAY_THIRD_PARTY_IME_TURNED_ON_BUBBLE for
195    // third party IMEs
196    views::Label* label = new views::Label(
197        l10n_util::GetStringFUTF16(
198            IDS_ASH_STATUS_TRAY_IME_TURNED_ON_BUBBLE,
199            current.medium_name));
200    label->SetMultiLine(true);
201    label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
202    return label;
203  }
204
205  DISALLOW_COPY_AND_ASSIGN(IMENotificationView);
206};
207
208
209}  // namespace tray
210
211TrayIME::TrayIME(SystemTray* system_tray)
212    : SystemTrayItem(system_tray),
213      tray_label_(NULL),
214      default_(NULL),
215      detailed_(NULL),
216      notification_(NULL),
217      message_shown_(false) {
218  Shell::GetInstance()->system_tray_notifier()->AddIMEObserver(this);
219}
220
221TrayIME::~TrayIME() {
222  Shell::GetInstance()->system_tray_notifier()->RemoveIMEObserver(this);
223}
224
225void TrayIME::UpdateTrayLabel(const IMEInfo& current, size_t count) {
226  if (tray_label_) {
227    if (current.third_party) {
228      tray_label_->label()->SetText(current.short_name + UTF8ToUTF16("*"));
229    } else {
230      tray_label_->label()->SetText(current.short_name);
231    }
232    tray_label_->SetVisible(count > 1);
233    SetTrayLabelItemBorder(tray_label_, system_tray()->shelf_alignment());
234    tray_label_->Layout();
235  }
236}
237
238views::View* TrayIME::CreateTrayView(user::LoginStatus status) {
239  CHECK(tray_label_ == NULL);
240  tray_label_ = new TrayItemView(this);
241  tray_label_->CreateLabel();
242  SetupLabelForTray(tray_label_->label());
243  // Hide IME tray when it is created, it will be updated when it is notified
244  // for IME refresh event.
245  tray_label_->SetVisible(false);
246  return tray_label_;
247}
248
249views::View* TrayIME::CreateDefaultView(user::LoginStatus status) {
250  SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
251  IMEInfoList list;
252  IMEPropertyInfoList property_list;
253  delegate->GetAvailableIMEList(&list);
254  delegate->GetCurrentIMEProperties(&property_list);
255  if (list.size() <= 1 && property_list.size() <= 1)
256    return NULL;
257  CHECK(default_ == NULL);
258  default_ = new tray::IMEDefaultView(this);
259  return default_;
260}
261
262views::View* TrayIME::CreateDetailedView(user::LoginStatus status) {
263  CHECK(detailed_ == NULL);
264  detailed_ = new tray::IMEDetailedView(this, status);
265  return detailed_;
266}
267
268views::View* TrayIME::CreateNotificationView(user::LoginStatus status) {
269  DCHECK(notification_ == NULL);
270  notification_ = new tray::IMENotificationView(this);
271  notification_->StartAutoCloseTimer(kTrayPopupAutoCloseDelayForTextInSeconds);
272  return notification_;
273}
274
275void TrayIME::DestroyTrayView() {
276  tray_label_ = NULL;
277}
278
279void TrayIME::DestroyDefaultView() {
280  default_ = NULL;
281}
282
283void TrayIME::DestroyDetailedView() {
284  detailed_ = NULL;
285}
286
287void TrayIME::DestroyNotificationView() {
288  notification_ = NULL;
289}
290
291void TrayIME::UpdateAfterLoginStatusChange(user::LoginStatus status) {
292}
293
294void TrayIME::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
295  SetTrayLabelItemBorder(tray_label_, alignment);
296}
297
298void TrayIME::OnIMERefresh(bool show_message) {
299  SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
300  IMEInfoList list;
301  IMEInfo current;
302  IMEPropertyInfoList property_list;
303  delegate->GetCurrentIME(&current);
304  delegate->GetAvailableIMEList(&list);
305  delegate->GetCurrentIMEProperties(&property_list);
306
307  UpdateTrayLabel(current, list.size());
308
309  if (default_)
310    default_->UpdateLabel(current);
311  if (detailed_)
312    detailed_->Update(list, property_list);
313
314  if (list.size() > 1 && show_message) {
315    // If the notification is still visible, hide it and clear the flag so it is
316    // refreshed.
317    if (notification_) {
318      notification_->UpdateLabel();
319    } else if (!Shell::GetPrimaryRootWindowController()->shelf()->IsVisible() ||
320               !message_shown_) {
321      ShowNotificationView();
322      message_shown_ = true;
323    }
324  }
325}
326
327}  // namespace internal
328}  // namespace ash
329