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