tray_ime.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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  return tray_label_;
272}
273
274views::View* TrayIME::CreateDefaultView(user::LoginStatus status) {
275  SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
276  IMEInfoList list;
277  IMEPropertyInfoList property_list;
278  delegate->GetAvailableIMEList(&list);
279  delegate->GetCurrentIMEProperties(&property_list);
280  if (list.size() <= 1 && property_list.size() <= 1)
281    return NULL;
282  CHECK(default_ == NULL);
283  default_ = new tray::IMEDefaultView(this);
284  return default_;
285}
286
287views::View* TrayIME::CreateDetailedView(user::LoginStatus status) {
288  CHECK(detailed_ == NULL);
289  detailed_ = new tray::IMEDetailedView(this, status);
290  return detailed_;
291}
292
293views::View* TrayIME::CreateNotificationView(user::LoginStatus status) {
294  DCHECK(notification_ == NULL);
295  notification_ = new tray::IMENotificationView(this);
296  notification_->StartAutoCloseTimer(kTrayPopupAutoCloseDelayForTextInSeconds);
297  return notification_;
298}
299
300void TrayIME::DestroyTrayView() {
301  tray_label_ = NULL;
302}
303
304void TrayIME::DestroyDefaultView() {
305  default_ = NULL;
306}
307
308void TrayIME::DestroyDetailedView() {
309  detailed_ = NULL;
310}
311
312void TrayIME::DestroyNotificationView() {
313  notification_ = NULL;
314}
315
316void TrayIME::UpdateAfterLoginStatusChange(user::LoginStatus status) {
317}
318
319void TrayIME::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
320  SetTrayLabelItemBorder(tray_label_, alignment);
321}
322
323void TrayIME::OnIMERefresh(bool show_message) {
324  SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
325  IMEInfoList list;
326  IMEInfo current;
327  IMEPropertyInfoList property_list;
328  delegate->GetCurrentIME(&current);
329  delegate->GetAvailableIMEList(&list);
330  delegate->GetCurrentIMEProperties(&property_list);
331
332  UpdateTrayLabel(current, list.size());
333
334  if (default_)
335    default_->UpdateLabel(current);
336  if (detailed_)
337    detailed_->Update(list, property_list);
338
339  if (list.size() > 1 && show_message) {
340    // If the notification is still visible, hide it and clear the flag so it is
341    // refreshed.
342    if (notification_) {
343      notification_->UpdateLabel();
344    } else if (!Shell::GetPrimaryRootWindowController()->shelf()->IsVisible() ||
345               !message_shown_) {
346      ShowNotificationView();
347      message_shown_ = true;
348    }
349  }
350}
351
352}  // namespace internal
353}  // namespace ash
354