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