Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(582)

Side by Side Diff: chrome/browser/chromeos/input_method/input_method_manager_impl.cc

Issue 9999018: chrome/browser/chromeos/input_method/ refactoring [part 6 of 6] (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase, remove |should_hide_properties_| Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 "chrome/browser/chromeos/input_method/input_method_manager_impl.h"
6
7 #include <algorithm> // std::find
8
9 #include "base/basictypes.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/string_util.h"
12 #include "base/stringprintf.h"
13 #include "chrome/browser/chromeos/input_method/browser_state_monitor.h"
14 #include "chrome/browser/chromeos/input_method/candidate_window.h"
15 #include "chrome/browser/chromeos/input_method/input_method_util.h"
16 #include "chrome/browser/chromeos/input_method/xkeyboard.h"
17 #include "chrome/browser/chromeos/language_preferences.h"
18 #include "ui/base/accelerators/accelerator.h"
19 #include "unicode/uloc.h"
20
21 namespace chromeos {
22 namespace input_method {
23
24 namespace {
25
26 template<typename T>
27 typename T::iterator Find(T& container, const typename T::value_type& value) {
Zachary Kuznia 2012/04/17 01:26:36 Why define these wrappers? It doesn't seem to add
Yusuke Sato 2012/04/17 02:23:32 I defined them just because I didn't want to type
28 return std::find(container.begin(), container.end(), value);
29 }
30
31 template<typename T>
32 typename T::const_iterator Find(const T& container,
33 const typename T::value_type& value) {
34 return std::find(container.begin(), container.end(), value);
35 }
36
37 template<typename T>
38 bool Contains(const T& container, const typename T::value_type& value) {
39 return Find(container, value) != container.end();
40 }
41
42 } // namespace
43
44 InputMethodManagerImpl::InputMethodManagerImpl()
45 : ignore_hotkeys_(false),
46 state_(STATE_LOGIN_SCREEN),
47 util_(GetSupportedInputMethods()) {
48 }
49
50 InputMethodManagerImpl::~InputMethodManagerImpl() {
51 if (ibus_controller_.get())
52 ibus_controller_->RemoveObserver(this);
53 if (candidate_window_controller_.get())
54 candidate_window_controller_->RemoveObserver(this);
55 }
56
57 void InputMethodManagerImpl::AddObserver(
58 InputMethodManager::Observer* observer) {
59 observers_.AddObserver(observer);
60 }
61
62 void InputMethodManagerImpl::AddCandidateWindowObserver(
63 InputMethodManager::CandidateWindowObserver* observer) {
64 candidate_window_observers_.AddObserver(observer);
65 }
66
67 void InputMethodManagerImpl::RemoveObserver(
68 InputMethodManager::Observer* observer) {
69 observers_.RemoveObserver(observer);
70 }
71
72 void InputMethodManagerImpl::RemoveCandidateWindowObserver(
73 InputMethodManager::CandidateWindowObserver* observer) {
74 candidate_window_observers_.RemoveObserver(observer);
75 }
76
77 void InputMethodManagerImpl::SetState(State new_state) {
78 const State old_state = state_;
79 state_ = new_state;
80 switch (state_) {
81 case STATE_LOGIN_SCREEN:
82 break;
83 case STATE_BROWSER_SCREEN:
84 if (old_state == STATE_LOCK_SCREEN)
85 OnScreenUnlocked();
86 break;
87 case STATE_LOCK_SCREEN:
88 OnScreenLocked();
89 break;
90 case STATE_TERMINATING:
91 ibus_controller_->Stop();
92 browser_state_monitor_.reset(); // For crbug.com/120183.
93 candidate_window_controller_.reset();
94 break;
95 }
96 }
97
98 InputMethodDescriptors*
99 InputMethodManagerImpl::GetSupportedInputMethods() const {
100 return whitelist_.GetSupportedInputMethods();
101 }
102
103 InputMethodDescriptors* InputMethodManagerImpl::GetActiveInputMethods() const {
104 InputMethodDescriptors* result = new InputMethodDescriptors;
105 // Build the active input method descriptors from the active input
106 // methods cache |active_input_method_ids_|.
107 for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
108 const std::string& input_method_id = active_input_method_ids_[i];
109 const InputMethodDescriptor* descriptor =
110 util_.GetInputMethodDescriptorFromId(input_method_id);
111 if (descriptor) {
112 result->push_back(*descriptor);
113 } else {
114 std::map<std::string, InputMethodDescriptor>::const_iterator ix =
115 extra_input_methods_.find(input_method_id);
116 if (ix != extra_input_methods_.end())
117 result->push_back(ix->second);
118 else
119 LOG(ERROR) << "Descriptor is not found for: " << input_method_id;
120 }
121 }
122 if (result->empty()) {
123 // Initially |active_input_method_ids_| is empty. browser_tests might take
124 // this path.
125 result->push_back(
126 InputMethodDescriptor::GetFallbackInputMethodDescriptor());
127 }
128 return result;
129 }
130
131 size_t InputMethodManagerImpl::GetNumActiveInputMethods() const {
132 return active_input_method_ids_.size();
133 }
134
135 void InputMethodManagerImpl::EnableLayouts(const std::string& language_code,
136 const std::string& initial_layout) {
137 if (state_ == STATE_TERMINATING)
138 return;
139
140 std::vector<std::string> candidates;
141 // Add input methods associated with the language.
142 util_.GetInputMethodIdsFromLanguageCode(language_code,
143 kKeyboardLayoutsOnly,
144 &candidates);
145 // Add the hardware keyboard as well. We should always add this so users
146 // can use the hardware keyboard on the login screen and the screen locker.
147 candidates.push_back(util_.GetHardwareInputMethodId());
148
149 std::vector<std::string> layouts;
150 // First, add the initial input method ID, if it's requested, to
151 // layouts, so it appears first on the list of active input
152 // methods at the input language status menu.
153 if (util_.IsValidInputMethodId(initial_layout) &&
154 InputMethodUtil::IsKeyboardLayout(initial_layout)) {
155 layouts.push_back(initial_layout);
156 } else if (!initial_layout.empty()) {
157 LOG(ERROR) << "EnableLayouts: ignoring non-keyboard or invalid ID: "
158 << initial_layout;
159 }
160
161 // Add candidates to layouts, while skipping duplicates.
162 for (size_t i = 0; i < candidates.size(); ++i) {
163 const std::string& candidate = candidates[i];
164 // Not efficient, but should be fine, as the two vectors are very
165 // short (2-5 items).
166 if (!Contains(layouts, candidate))
167 layouts.push_back(candidate);
168 }
169
170 active_input_method_ids_.swap(layouts);
171 ChangeInputMethod(initial_layout); // you can pass empty |initial_layout|.
172 }
173
174 bool InputMethodManagerImpl::EnableInputMethods(
175 const std::vector<std::string>& new_active_input_method_ids) {
176 if (state_ == STATE_TERMINATING)
177 return false;
178
179 // Filter unknown or obsolete IDs.
180 std::vector<std::string> new_active_input_method_ids_filtered;
181
182 for (size_t i = 0; i < new_active_input_method_ids.size(); ++i) {
183 const std::string& input_method_id = new_active_input_method_ids[i];
184 if (util_.IsValidInputMethodId(input_method_id))
185 new_active_input_method_ids_filtered.push_back(input_method_id);
186 else
187 LOG(ERROR) << "EnableInputMethods: Invalid ID: " << input_method_id;
188 }
189
190 if (new_active_input_method_ids_filtered.empty()) {
191 LOG(ERROR) << "EnableInputMethods: No valid input method ID";
192 return false;
193 }
194
195 // Copy extension IDs to |new_active_input_method_ids_filtered|. We have to
196 // keep relative order of the extension input method IDs.
197 for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
198 const std::string& input_method_id = active_input_method_ids_[i];
199 if (InputMethodUtil::IsExtensionInputMethod(input_method_id))
200 new_active_input_method_ids_filtered.push_back(input_method_id);
201 }
202 active_input_method_ids_.swap(new_active_input_method_ids_filtered);
203
204 if (ContainOnlyKeyboardLayout(active_input_method_ids_)) {
205 // Do NOT call ibus_controller_->Stop(); here to work around a crash issue
206 // at crosbug.com/27051.
207 // TODO(yusukes): We can safely call Stop(); here once crosbug.com/26443
208 // is implemented.
209 } else {
210 MaybeInitializeCandidateWindowController();
211 ibus_controller_->Start();
212 }
213
214 // If |current_input_method| is no longer in |active_input_method_ids_|,
215 // ChangeInputMethod() picks the first one in |active_input_method_ids_|.
216 ChangeInputMethod(current_input_method_.id());
217 return true;
218 }
219
220 bool InputMethodManagerImpl::SetInputMethodConfig(
221 const std::string& section,
222 const std::string& config_name,
223 const InputMethodConfigValue& value) {
224 DCHECK(section != language_prefs::kGeneralSectionName ||
225 config_name != language_prefs::kPreloadEnginesConfigName);
226
227 if (state_ == STATE_TERMINATING)
228 return false;
229 return ibus_controller_->SetInputMethodConfig(section, config_name, value);
230 }
231
232 void InputMethodManagerImpl::ChangeInputMethod(
233 const std::string& input_method_id) {
234 if (state_ == STATE_TERMINATING)
235 return;
236
237 std::string input_method_id_to_switch = input_method_id;
238
239 // Sanity check.
240 if (!InputMethodIsActivated(input_method_id)) {
241 scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods());
242 DCHECK(!input_methods->empty());
243 input_method_id_to_switch = input_methods->at(0).id();
244 if (!input_method_id.empty()) {
245 VLOG(1) << "Can't change the current input method to "
246 << input_method_id << " since the engine is not enabled. "
247 << "Switch to " << input_method_id_to_switch << " instead.";
248 }
249 }
250
251 if (InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch)) {
252 FOR_EACH_OBSERVER(InputMethodManager::Observer,
253 observers_,
254 InputMethodPropertyChanged(this));
255 } else {
256 ibus_controller_->ChangeInputMethod(input_method_id_to_switch);
257 }
258
259 if (current_input_method_.id() != input_method_id_to_switch) {
260 const InputMethodDescriptor* descriptor = NULL;
261 if (!InputMethodUtil::IsExtensionInputMethod(input_method_id_to_switch)) {
262 descriptor =
263 util_.GetInputMethodDescriptorFromId(input_method_id_to_switch);
264 } else {
265 std::map<std::string, InputMethodDescriptor>::const_iterator i =
266 extra_input_methods_.find(input_method_id_to_switch);
267 DCHECK(i != extra_input_methods_.end());
268 descriptor = &(i->second);
269 }
270 DCHECK(descriptor);
271
272 previous_input_method_ = current_input_method_;
273 current_input_method_ = *descriptor;
274
275 // Change the keyboard layout to a preferred layout for the input method.
276 if (!xkeyboard_->SetCurrentKeyboardLayoutByName(
277 current_input_method_.keyboard_layout())) {
278 LOG(ERROR) << "Failed to change keyboard layout to "
279 << current_input_method_.keyboard_layout();
280 }
281 }
282
283 // Update input method indicators (e.g. "US", "DV") in Chrome windows.
284 FOR_EACH_OBSERVER(InputMethodManager::Observer,
285 observers_,
286 InputMethodChanged(this));
287 }
288
289 void InputMethodManagerImpl::ActivateInputMethodProperty(
290 const std::string& key) {
291 DCHECK(!key.empty());
292 ibus_controller_->ActivateInputMethodProperty(key);
293 }
294
295 void InputMethodManagerImpl::AddInputMethodExtension(
296 const std::string& id,
297 const std::string& name,
298 const std::vector<std::string>& layouts,
299 const std::string& language) {
300 if (state_ == STATE_TERMINATING)
301 return;
302
303 if (!InputMethodUtil::IsExtensionInputMethod(id)) {
304 LOG(ERROR) << id << " is not a valid extension input method ID.";
305 return;
306 }
307
308 const std::string virtual_layouts = JoinString(layouts, ',');
309 extra_input_methods_[id] = InputMethodDescriptor(
310 whitelist_, id, name, virtual_layouts, language);
311
312 if (!Contains(active_input_method_ids_, id)) {
313 active_input_method_ids_.push_back(id);
314 } else {
315 LOG(ERROR) << "AddInputMethodExtension: alread added: "
316 << id << ", " << name;
317 // Call Start() anyway, just in case.
318 }
319
320 // Ensure that the input method daemon is running.
321 MaybeInitializeCandidateWindowController();
322 ibus_controller_->Start();
323 }
324
325 void InputMethodManagerImpl::RemoveInputMethodExtension(const std::string& id) {
326 if (!InputMethodUtil::IsExtensionInputMethod(id))
327 LOG(ERROR) << id << " is not a valid extension input method ID.";
328
329 std::vector<std::string>::iterator i = Find(active_input_method_ids_, id);
330 if (i != active_input_method_ids_.end())
331 active_input_method_ids_.erase(i);
332 extra_input_methods_.erase(id);
333
334 if (ContainOnlyKeyboardLayout(active_input_method_ids_)) {
335 // Do NOT call ibus_controller_->Stop(); here to work around a crash issue
336 // at crosbug.com/27051.
337 // TODO(yusukes): We can safely call Stop(); here once crosbug.com/26443
338 // is implemented.
339 }
340
341 // If |current_input_method| is no longer in |active_input_method_ids_|,
342 // switch to the first one in |active_input_method_ids_|.
343 ChangeInputMethod(current_input_method_.id());
344 }
345
346 void InputMethodManagerImpl::EnableHotkeys() {
347 ignore_hotkeys_ = false;
348 }
349
350 void InputMethodManagerImpl::DisableHotkeys() {
351 ignore_hotkeys_ = true;
352 }
353
354 bool InputMethodManagerImpl::SwitchToNextInputMethod() {
355 if (ignore_hotkeys_)
356 return false;
357
358 // Sanity checks.
359 if (active_input_method_ids_.empty()) {
360 LOG(ERROR) << "active input method is empty";
361 return false;
362 }
363 if (current_input_method_.id().empty()) {
364 LOG(ERROR) << "current_input_method_ is unknown";
365 return false;
366 }
367
368 // Find the next input method.
369 std::vector<std::string>::const_iterator iter =
370 Find(active_input_method_ids_, current_input_method_.id());
371 if (iter != active_input_method_ids_.end())
372 ++iter;
373 if (iter == active_input_method_ids_.end())
374 iter = active_input_method_ids_.begin();
kinaba 2012/04/16 10:22:53 Shall we factor out the pattern of above 6 lines?
Yusuke Sato 2012/04/16 11:25:43 Done.
375 ChangeInputMethod(*iter);
376 return true;
377 }
378
379 bool InputMethodManagerImpl::SwitchToPreviousInputMethod() {
380 if (ignore_hotkeys_)
381 return false;
382
383 // Sanity check.
384 if (active_input_method_ids_.empty()) {
385 LOG(ERROR) << "active input method is empty";
386 return false;
387 }
388
389 if (previous_input_method_.id().empty() ||
390 previous_input_method_.id() == current_input_method_.id()) {
391 return SwitchToNextInputMethod();
392 }
393
394 std::vector<std::string>::const_iterator iter =
395 Find(active_input_method_ids_, previous_input_method_.id());
396 if (iter == active_input_method_ids_.end()) {
397 // previous_input_method_ is not supported.
398 return SwitchToNextInputMethod();
399 }
400 ChangeInputMethod(*iter);
401 return true;
402 }
403
404 bool InputMethodManagerImpl::SwitchInputMethod(
405 const ui::Accelerator& accelerator) {
406 if (ignore_hotkeys_)
407 return false;
408
409 // Sanity check.
410 if (active_input_method_ids_.empty()) {
411 LOG(ERROR) << "active input method is empty";
412 return false;
413 }
414
415 // Get the list of input method ids for the |accelerator|. For example, get
416 // { "mozc-hangul", "xkb:kr:kr104:kor" } for ui::VKEY_DBE_SBCSCHAR.
417 std::vector<std::string> input_method_ids_to_switch;
418 switch (accelerator.key_code()) {
419 case ui::VKEY_CONVERT: // Henkan key on JP106 keyboard
420 input_method_ids_to_switch.push_back("mozc-jp");
421 break;
422 case ui::VKEY_NONCONVERT: // Muhenkan key on JP106 keyboard
423 input_method_ids_to_switch.push_back("xkb:jp::jpn");
424 break;
425 case ui::VKEY_DBE_SBCSCHAR: // ZenkakuHankaku key on JP106 keyboard
426 case ui::VKEY_DBE_DBCSCHAR:
427 input_method_ids_to_switch.push_back("mozc-jp");
428 input_method_ids_to_switch.push_back("xkb:jp::jpn");
429 break;
430 case ui::VKEY_HANGUL: // Hangul (or right Alt) key on Korean keyboard
431 case ui::VKEY_SPACE: // Shift+Space
432 input_method_ids_to_switch.push_back("mozc-hangul");
433 input_method_ids_to_switch.push_back("xkb:kr:kr104:kor");
434 break;
435 default:
436 NOTREACHED();
437 break;
438 }
439 if (input_method_ids_to_switch.empty()) {
440 LOG(ERROR) << "Unexpected VKEY: " << accelerator.key_code();
441 return false;
442 }
443
444 // Obtain the intersection of input_method_ids_to_switch and
445 // active_input_method_ids_. The order of IDs in active_input_method_ids_ is
446 // preserved.
447 std::vector<std::string> ids;
448 for (size_t i = 0; i < input_method_ids_to_switch.size(); ++i) {
449 const std::string& id = input_method_ids_to_switch[i];
450 if (Contains(active_input_method_ids_, id))
451 ids.push_back(id);
452 }
453 if (ids.empty()) {
454 // No input method for the accelerator is active. For example, we should
455 // just ignore VKEY_HANGUL when mozc-hangul is not active.
456 return false;
457 }
458
459 // If |current_input_method_| is not in ids, switch to ids[0]. If
460 // |current_input_method_| is ids[N], switch to ids[N+1].
461 std::vector<std::string>::const_iterator iter =
462 Find(ids, current_input_method_.id());
463 if (iter != ids.end())
464 ++iter;
465 if (iter == ids.end())
466 iter = ids.begin();
467 ChangeInputMethod(*iter);
468 return true; // consume the accelerator.
469 }
470
471 InputMethodDescriptor InputMethodManagerImpl::GetCurrentInputMethod() const {
472 if (current_input_method_.id().empty())
473 return InputMethodDescriptor::GetFallbackInputMethodDescriptor();
474 return current_input_method_;
475 }
476
477 InputMethodPropertyList
478 InputMethodManagerImpl::GetCurrentInputMethodProperties() const {
479 // This check is necessary since an IME property (e.g. for Pinyin) might be
480 // sent from ibus-daemon AFTER the current input method is switched to XKB.
481 if (InputMethodUtil::IsKeyboardLayout(GetCurrentInputMethod().id()))
482 return InputMethodPropertyList();
483 return ibus_controller_->GetCurrentProperties();
484 }
485
486 XKeyboard* InputMethodManagerImpl::GetXKeyboard() {
487 return xkeyboard_.get();
488 }
489
490 InputMethodUtil* InputMethodManagerImpl::GetInputMethodUtil() {
491 return &util_;
492 }
493
494 void InputMethodManagerImpl::Init() {
495 DCHECK(!ibus_controller_.get());
496
497 browser_state_monitor_.reset(new BrowserStateMonitor(this));
498 ibus_controller_.reset(IBusController::Create());
499 xkeyboard_.reset(XKeyboard::Create(util_));
500 ibus_controller_->AddObserver(this);
501 }
502
503 void InputMethodManagerImpl::SetIBusControllerForTesting(
504 IBusController* ibus_controller) {
505 ibus_controller_.reset(ibus_controller);
506 ibus_controller_->AddObserver(this);
507 }
508
509 void InputMethodManagerImpl::SetCandidateWindowControllerForTesting(
510 CandidateWindowController* candidate_window_controller) {
511 candidate_window_controller_.reset(candidate_window_controller);
512 candidate_window_controller_->Init();
513 candidate_window_controller_->AddObserver(this);
514 }
515
516 void InputMethodManagerImpl::SetXKeyboardForTesting(XKeyboard* xkeyboard) {
517 xkeyboard_.reset(xkeyboard);
518 }
519
520 void InputMethodManagerImpl::PropertyChanged() {
521 FOR_EACH_OBSERVER(InputMethodManager::Observer,
522 observers_,
523 InputMethodPropertyChanged(this));
524 }
525
526 void InputMethodManagerImpl::CandidateWindowOpened() {
527 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
528 candidate_window_observers_,
529 CandidateWindowOpened(this));
530 }
531
532 void InputMethodManagerImpl::CandidateWindowClosed() {
533 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
534 candidate_window_observers_,
535 CandidateWindowClosed(this));
536 }
537
538 void InputMethodManagerImpl::OnScreenLocked() {
539 saved_previous_input_method_ = previous_input_method_;
540 saved_current_input_method_ = current_input_method_;
541 saved_active_input_method_ids_ = active_input_method_ids_;
542
543 const std::string hardware_keyboard_id = util_.GetHardwareInputMethodId();
544 // We'll add the hardware keyboard if it's not included in
545 // |active_input_method_list| so that the user can always use the hardware
546 // keyboard on the screen locker.
547 bool should_add_hardware_keyboard = true;
548
549 active_input_method_ids_.clear();
550 for (size_t i = 0; i < saved_active_input_method_ids_.size(); ++i) {
551 const std::string& input_method_id = saved_active_input_method_ids_[i];
552 // Skip if it's not a keyboard layout. Drop input methods including
553 // extension ones.
554 if (!InputMethodUtil::IsKeyboardLayout(input_method_id))
555 continue;
556 active_input_method_ids_.push_back(input_method_id);
557 if (input_method_id == hardware_keyboard_id)
558 should_add_hardware_keyboard = false;
559 }
560 if (should_add_hardware_keyboard)
561 active_input_method_ids_.push_back(hardware_keyboard_id);
562
563 ChangeInputMethod(current_input_method_.id());
564 }
565
566 void InputMethodManagerImpl::OnScreenUnlocked() {
567 previous_input_method_ = saved_previous_input_method_;
568 current_input_method_ = saved_current_input_method_;
569 active_input_method_ids_ = saved_active_input_method_ids_;
570
571 ChangeInputMethod(current_input_method_.id());
572 }
573
574 bool InputMethodManagerImpl::InputMethodIsActivated(
575 const std::string& input_method_id) {
576 return Contains(active_input_method_ids_, input_method_id);
577 }
578
579 bool InputMethodManagerImpl::ContainOnlyKeyboardLayout(
580 const std::vector<std::string>& value) {
581 for (size_t i = 0; i < value.size(); ++i) {
582 if (!InputMethodUtil::IsKeyboardLayout(value[i]))
583 return false;
584 }
585 return true;
586 }
587
588 void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() {
589 #if !defined(USE_VIRTUAL_KEYBOARD)
590 if (candidate_window_controller_.get())
591 return;
592
593 candidate_window_controller_.reset(
594 CandidateWindowController::CreateCandidateWindowController());
595 if (candidate_window_controller_->Init())
596 candidate_window_controller_->AddObserver(this);
597 else
598 LOG(WARNING) << "Failed to initialize the candidate window controller";
599 #endif
600 }
601
602 // static
603 InputMethodManagerImpl* InputMethodManagerImpl::GetInstanceForTesting() {
604 return new InputMethodManagerImpl;
605 }
606
607 } // namespace input_method
608 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698