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_caps_lock.h"
6
7 #include "ash/metrics/user_metrics_recorder.h"
8 #include "ash/shell.h"
9 #include "ash/system/tray/actionable_view.h"
10 #include "ash/system/tray/fixed_sized_image_view.h"
11 #include "ash/system/tray/system_tray_notifier.h"
12 #include "ash/system/tray/tray_constants.h"
13 #include "base/sys_info.h"
14 #include "chromeos/ime/ime_keyboard.h"
15 #include "chromeos/ime/input_method_manager.h"
16 #include "grit/ash_resources.h"
17 #include "grit/ash_strings.h"
18 #include "ui/accessibility/ax_view_state.h"
19 #include "ui/base/resource/resource_bundle.h"
20 #include "ui/gfx/image/image.h"
21 #include "ui/views/controls/image_view.h"
22 #include "ui/views/controls/label.h"
23 #include "ui/views/layout/box_layout.h"
24 #include "ui/views/widget/widget.h"
25
26 namespace ash {
27 namespace {
28
CapsLockIsEnabled()29 bool CapsLockIsEnabled() {
30 chromeos::input_method::InputMethodManager* ime =
31 chromeos::input_method::InputMethodManager::Get();
32 return (ime && ime->GetImeKeyboard())
33 ? ime->GetImeKeyboard()->CapsLockIsEnabled()
34 : false;
35 }
36
37 }
38
39 class CapsLockDefaultView : public ActionableView {
40 public:
CapsLockDefaultView()41 CapsLockDefaultView()
42 : text_label_(new views::Label),
43 shortcut_label_(new views::Label) {
44 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
45 kTrayPopupPaddingHorizontal,
46 0,
47 kTrayPopupPaddingBetweenItems));
48
49 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
50 FixedSizedImageView* image =
51 new FixedSizedImageView(0, kTrayPopupItemHeight);
52 image->SetImage(bundle.GetImageNamed(IDR_AURA_UBER_TRAY_CAPS_LOCK_DARK).
53 ToImageSkia());
54 AddChildView(image);
55
56 text_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
57 AddChildView(text_label_);
58
59 shortcut_label_->SetEnabled(false);
60 AddChildView(shortcut_label_);
61 }
62
~CapsLockDefaultView()63 virtual ~CapsLockDefaultView() {}
64
65 // Updates the label text and the shortcut text.
Update(bool caps_lock_enabled)66 void Update(bool caps_lock_enabled) {
67 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
68 const int text_string_id = caps_lock_enabled ?
69 IDS_ASH_STATUS_TRAY_CAPS_LOCK_ENABLED :
70 IDS_ASH_STATUS_TRAY_CAPS_LOCK_DISABLED;
71 text_label_->SetText(bundle.GetLocalizedString(text_string_id));
72
73 int shortcut_string_id = 0;
74 bool search_mapped_to_caps_lock = Shell::GetInstance()->
75 system_tray_delegate()->IsSearchKeyMappedToCapsLock();
76 if (caps_lock_enabled) {
77 shortcut_string_id = search_mapped_to_caps_lock ?
78 IDS_ASH_STATUS_TRAY_CAPS_LOCK_SHORTCUT_SEARCH_OR_SHIFT :
79 IDS_ASH_STATUS_TRAY_CAPS_LOCK_SHORTCUT_ALT_SEARCH_OR_SHIFT;
80 } else {
81 shortcut_string_id = search_mapped_to_caps_lock ?
82 IDS_ASH_STATUS_TRAY_CAPS_LOCK_SHORTCUT_SEARCH :
83 IDS_ASH_STATUS_TRAY_CAPS_LOCK_SHORTCUT_ALT_SEARCH;
84 }
85 shortcut_label_->SetText(bundle.GetLocalizedString(shortcut_string_id));
86
87 Layout();
88 }
89
90 private:
91 // Overridden from views::View:
Layout()92 virtual void Layout() OVERRIDE {
93 views::View::Layout();
94
95 // Align the shortcut text with the right end
96 const int old_x = shortcut_label_->x();
97 const int new_x =
98 width() - shortcut_label_->width() - kTrayPopupPaddingHorizontal;
99 shortcut_label_->SetX(new_x);
100 const gfx::Size text_size = text_label_->size();
101 text_label_->SetSize(gfx::Size(text_size.width() + new_x - old_x,
102 text_size.height()));
103 }
104
GetAccessibleState(ui::AXViewState * state)105 virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE {
106 state->role = ui::AX_ROLE_BUTTON;
107 state->name = text_label_->text();
108 }
109
110 // Overridden from ActionableView:
PerformAction(const ui::Event & event)111 virtual bool PerformAction(const ui::Event& event) OVERRIDE {
112 chromeos::input_method::ImeKeyboard* keyboard =
113 chromeos::input_method::InputMethodManager::Get()->GetImeKeyboard();
114 if (keyboard) {
115 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
116 keyboard->CapsLockIsEnabled() ?
117 ash::UMA_STATUS_AREA_CAPS_LOCK_DISABLED_BY_CLICK :
118 ash::UMA_STATUS_AREA_CAPS_LOCK_ENABLED_BY_CLICK);
119 keyboard->SetCapsLockEnabled(!keyboard->CapsLockIsEnabled());
120 }
121 return true;
122 }
123
124 views::Label* text_label_;
125 views::Label* shortcut_label_;
126
127 DISALLOW_COPY_AND_ASSIGN(CapsLockDefaultView);
128 };
129
TrayCapsLock(SystemTray * system_tray)130 TrayCapsLock::TrayCapsLock(SystemTray* system_tray)
131 : TrayImageItem(system_tray, IDR_AURA_UBER_TRAY_CAPS_LOCK),
132 default_(NULL),
133 detailed_(NULL),
134 caps_lock_enabled_(CapsLockIsEnabled()),
135 message_shown_(false) {
136 chromeos::input_method::InputMethodManager* ime =
137 chromeos::input_method::InputMethodManager::Get();
138 if (ime && ime->GetImeKeyboard())
139 ime->GetImeKeyboard()->AddObserver(this);
140 }
141
~TrayCapsLock()142 TrayCapsLock::~TrayCapsLock() {
143 chromeos::input_method::InputMethodManager* ime =
144 chromeos::input_method::InputMethodManager::Get();
145 if (ime && ime->GetImeKeyboard())
146 ime->GetImeKeyboard()->RemoveObserver(this);
147 }
148
OnCapsLockChanged(bool enabled)149 void TrayCapsLock::OnCapsLockChanged(bool enabled) {
150 caps_lock_enabled_ = enabled;
151
152 if (tray_view())
153 tray_view()->SetVisible(caps_lock_enabled_);
154
155 if (default_) {
156 default_->Update(caps_lock_enabled_);
157 } else {
158 if (caps_lock_enabled_) {
159 if (!message_shown_) {
160 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
161 ash::UMA_STATUS_AREA_CAPS_LOCK_POPUP);
162 PopupDetailedView(kTrayPopupAutoCloseDelayForTextInSeconds, false);
163 message_shown_ = true;
164 }
165 } else if (detailed_) {
166 detailed_->GetWidget()->Close();
167 }
168 }
169 }
170
GetInitialVisibility()171 bool TrayCapsLock::GetInitialVisibility() {
172 return CapsLockIsEnabled();
173 }
174
CreateDefaultView(user::LoginStatus status)175 views::View* TrayCapsLock::CreateDefaultView(user::LoginStatus status) {
176 if (!caps_lock_enabled_)
177 return NULL;
178 DCHECK(default_ == NULL);
179 default_ = new CapsLockDefaultView;
180 default_->Update(caps_lock_enabled_);
181 return default_;
182 }
183
CreateDetailedView(user::LoginStatus status)184 views::View* TrayCapsLock::CreateDetailedView(user::LoginStatus status) {
185 DCHECK(detailed_ == NULL);
186 detailed_ = new views::View;
187
188 detailed_->SetLayoutManager(new
189 views::BoxLayout(views::BoxLayout::kHorizontal,
190 kTrayPopupPaddingHorizontal, 10, kTrayPopupPaddingBetweenItems));
191
192 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
193 views::ImageView* image = new views::ImageView;
194 image->SetImage(bundle.GetImageNamed(IDR_AURA_UBER_TRAY_CAPS_LOCK_DARK).
195 ToImageSkia());
196
197 detailed_->AddChildView(image);
198
199 const int string_id = Shell::GetInstance()->system_tray_delegate()->
200 IsSearchKeyMappedToCapsLock() ?
201 IDS_ASH_STATUS_TRAY_CAPS_LOCK_CANCEL_BY_SEARCH :
202 IDS_ASH_STATUS_TRAY_CAPS_LOCK_CANCEL_BY_ALT_SEARCH;
203 views::Label* label = new views::Label(bundle.GetLocalizedString(string_id));
204 label->SetMultiLine(true);
205 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
206 detailed_->AddChildView(label);
207 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
208 ash::UMA_STATUS_AREA_CAPS_LOCK_DETAILED);
209
210 return detailed_;
211 }
212
DestroyDefaultView()213 void TrayCapsLock::DestroyDefaultView() {
214 default_ = NULL;
215 }
216
DestroyDetailedView()217 void TrayCapsLock::DestroyDetailedView() {
218 detailed_ = NULL;
219 }
220
221 } // namespace ash
222