Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / chromeos / input_method / input_method_manager_impl.cc
blob762aaf45c2189047b78f977b3d16842e34509a58
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.
5 #include "chrome/browser/chromeos/input_method/input_method_manager_impl.h"
7 #include <algorithm> // std::find
9 #include "base/basictypes.h"
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/chromeos/input_method/candidate_window_controller.h"
18 #include "chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.h"
19 #include "chrome/browser/chromeos/input_method/input_method_engine.h"
20 #include "chrome/browser/chromeos/language_preferences.h"
21 #include "chromeos/ime/component_extension_ime_manager.h"
22 #include "chromeos/ime/extension_ime_util.h"
23 #include "chromeos/ime/input_method_delegate.h"
24 #include "chromeos/ime/xkeyboard.h"
25 #include "third_party/icu/source/common/unicode/uloc.h"
26 #include "ui/base/accelerators/accelerator.h"
28 namespace chromeos {
29 namespace input_method {
31 namespace {
33 const char nacl_mozc_jp_id[] =
34 "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_jp";
36 bool Contains(const std::vector<std::string>& container,
37 const std::string& value) {
38 return std::find(container.begin(), container.end(), value) !=
39 container.end();
42 } // namespace
44 bool InputMethodManagerImpl::IsLoginKeyboard(
45 const std::string& layout) const {
46 const InputMethodDescriptor* ime =
47 util_.GetInputMethodDescriptorFromId(layout);
48 return ime ? ime->is_login_keyboard() : false;
51 InputMethodManagerImpl::InputMethodManagerImpl(
52 scoped_ptr<InputMethodDelegate> delegate)
53 : delegate_(delegate.Pass()),
54 state_(STATE_LOGIN_SCREEN),
55 util_(delegate_.get(), GetSupportedInputMethods()),
56 component_extension_ime_manager_(new ComponentExtensionIMEManager()),
57 weak_ptr_factory_(this) {
60 InputMethodManagerImpl::~InputMethodManagerImpl() {
61 if (candidate_window_controller_.get())
62 candidate_window_controller_->RemoveObserver(this);
65 void InputMethodManagerImpl::AddObserver(
66 InputMethodManager::Observer* observer) {
67 observers_.AddObserver(observer);
70 void InputMethodManagerImpl::AddCandidateWindowObserver(
71 InputMethodManager::CandidateWindowObserver* observer) {
72 candidate_window_observers_.AddObserver(observer);
75 void InputMethodManagerImpl::RemoveObserver(
76 InputMethodManager::Observer* observer) {
77 observers_.RemoveObserver(observer);
80 void InputMethodManagerImpl::RemoveCandidateWindowObserver(
81 InputMethodManager::CandidateWindowObserver* observer) {
82 candidate_window_observers_.RemoveObserver(observer);
85 void InputMethodManagerImpl::SetState(State new_state) {
86 const State old_state = state_;
87 state_ = new_state;
88 switch (state_) {
89 case STATE_LOGIN_SCREEN:
90 break;
91 case STATE_BROWSER_SCREEN:
92 if (old_state == STATE_LOCK_SCREEN)
93 OnScreenUnlocked();
94 break;
95 case STATE_LOCK_SCREEN:
96 OnScreenLocked();
97 break;
98 case STATE_TERMINATING: {
99 if (candidate_window_controller_.get())
100 candidate_window_controller_.reset();
101 break;
106 scoped_ptr<InputMethodDescriptors>
107 InputMethodManagerImpl::GetSupportedInputMethods() const {
108 return whitelist_.GetSupportedInputMethods();
111 scoped_ptr<InputMethodDescriptors>
112 InputMethodManagerImpl::GetActiveInputMethods() const {
113 scoped_ptr<InputMethodDescriptors> result(new InputMethodDescriptors);
114 // Build the active input method descriptors from the active input
115 // methods cache |active_input_method_ids_|.
116 for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
117 const std::string& input_method_id = active_input_method_ids_[i];
118 const InputMethodDescriptor* descriptor =
119 util_.GetInputMethodDescriptorFromId(input_method_id);
120 if (descriptor) {
121 result->push_back(*descriptor);
122 } else {
123 std::map<std::string, InputMethodDescriptor>::const_iterator ix =
124 extra_input_methods_.find(input_method_id);
125 if (ix != extra_input_methods_.end())
126 result->push_back(ix->second);
127 else
128 DVLOG(1) << "Descriptor is not found for: " << input_method_id;
131 if (result->empty()) {
132 // Initially |active_input_method_ids_| is empty. browser_tests might take
133 // this path.
134 result->push_back(
135 InputMethodUtil::GetFallbackInputMethodDescriptor());
137 return result.Pass();
140 const std::vector<std::string>&
141 InputMethodManagerImpl::GetActiveInputMethodIds() const {
142 return active_input_method_ids_;
145 size_t InputMethodManagerImpl::GetNumActiveInputMethods() const {
146 return active_input_method_ids_.size();
149 const InputMethodDescriptor* InputMethodManagerImpl::GetInputMethodFromId(
150 const std::string& input_method_id) const {
151 const InputMethodDescriptor* ime = util_.GetInputMethodDescriptorFromId(
152 input_method_id);
153 if (!ime) {
154 std::map<std::string, InputMethodDescriptor>::const_iterator ix =
155 extra_input_methods_.find(input_method_id);
156 if (ix != extra_input_methods_.end())
157 ime = &ix->second;
159 return ime;
162 void InputMethodManagerImpl::EnableLayouts(const std::string& language_code,
163 const std::string& initial_layout) {
164 if (state_ == STATE_TERMINATING)
165 return;
167 std::vector<std::string> candidates;
168 // Add input methods associated with the language.
169 util_.GetInputMethodIdsFromLanguageCode(language_code,
170 kKeyboardLayoutsOnly,
171 &candidates);
172 // Add the hardware keyboard as well. We should always add this so users
173 // can use the hardware keyboard on the login screen and the screen locker.
174 candidates.push_back(util_.GetHardwareInputMethodId());
176 std::vector<std::string> layouts;
177 // First, add the initial input method ID, if it's requested, to
178 // layouts, so it appears first on the list of active input
179 // methods at the input language status menu.
180 if (util_.IsValidInputMethodId(initial_layout) &&
181 IsLoginKeyboard(initial_layout)) {
182 layouts.push_back(initial_layout);
183 } else if (!initial_layout.empty()) {
184 DVLOG(1) << "EnableLayouts: ignoring non-keyboard or invalid ID: "
185 << initial_layout;
188 // Add candidates to layouts, while skipping duplicates.
189 for (size_t i = 0; i < candidates.size(); ++i) {
190 const std::string& candidate = candidates[i];
191 // Not efficient, but should be fine, as the two vectors are very
192 // short (2-5 items).
193 if (!Contains(layouts, candidate) && IsLoginKeyboard(candidate))
194 layouts.push_back(candidate);
197 active_input_method_ids_.swap(layouts);
199 // Initialize candidate window controller and widgets such as
200 // candidate window, infolist and mode indicator. Note, mode
201 // indicator is used by only keyboard layout input methods.
202 if (active_input_method_ids_.size() > 1)
203 MaybeInitializeCandidateWindowController();
205 ChangeInputMethod(initial_layout); // you can pass empty |initial_layout|.
208 // Adds new input method to given list.
209 bool InputMethodManagerImpl::EnableInputMethodImpl(
210 const std::string& input_method_id,
211 std::vector<std::string>* new_active_input_method_ids) const {
212 DCHECK(new_active_input_method_ids);
213 if (!util_.IsValidInputMethodId(input_method_id)) {
214 DVLOG(1) << "EnableInputMethod: Invalid ID: " << input_method_id;
215 return false;
218 if (!Contains(*new_active_input_method_ids, input_method_id))
219 new_active_input_method_ids->push_back(input_method_id);
221 return true;
224 // Starts or stops the system input method framework as needed.
225 void InputMethodManagerImpl::ReconfigureIMFramework() {
226 if (component_extension_ime_manager_->IsInitialized())
227 LoadNecessaryComponentExtensions();
229 const bool need_engine =
230 !ContainsOnlyKeyboardLayout(active_input_method_ids_);
232 // Initialize candidate window controller and widgets such as
233 // candidate window, infolist and mode indicator. Note, mode
234 // indicator is used by only keyboard layout input methods.
235 if (need_engine || active_input_method_ids_.size() > 1)
236 MaybeInitializeCandidateWindowController();
239 bool InputMethodManagerImpl::EnableInputMethod(
240 const std::string& input_method_id) {
241 if (!EnableInputMethodImpl(input_method_id, &active_input_method_ids_))
242 return false;
244 ReconfigureIMFramework();
245 return true;
248 bool InputMethodManagerImpl::EnableInputMethods(
249 const std::vector<std::string>& new_active_input_method_ids) {
250 if (state_ == STATE_TERMINATING)
251 return false;
253 // Filter unknown or obsolete IDs.
254 std::vector<std::string> new_active_input_method_ids_filtered;
256 for (size_t i = 0; i < new_active_input_method_ids.size(); ++i)
257 EnableInputMethodImpl(new_active_input_method_ids[i],
258 &new_active_input_method_ids_filtered);
260 if (new_active_input_method_ids_filtered.empty()) {
261 DVLOG(1) << "EnableInputMethods: No valid input method ID";
262 return false;
265 // Copy extension IDs to |new_active_input_method_ids_filtered|. We have to
266 // keep relative order of the extension input method IDs.
267 for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
268 const std::string& input_method_id = active_input_method_ids_[i];
269 if (extension_ime_util::IsExtensionIME(input_method_id))
270 new_active_input_method_ids_filtered.push_back(input_method_id);
272 active_input_method_ids_.swap(new_active_input_method_ids_filtered);
274 ReconfigureIMFramework();
276 // If |current_input_method| is no longer in |active_input_method_ids_|,
277 // ChangeInputMethod() picks the first one in |active_input_method_ids_|.
278 ChangeInputMethod(current_input_method_.id());
279 return true;
282 void InputMethodManagerImpl::ChangeInputMethod(
283 const std::string& input_method_id) {
284 ChangeInputMethodInternal(input_method_id, false);
287 bool InputMethodManagerImpl::ChangeInputMethodInternal(
288 const std::string& input_method_id,
289 bool show_message) {
290 if (state_ == STATE_TERMINATING)
291 return false;
293 std::string input_method_id_to_switch = input_method_id;
295 // Sanity check.
296 if (!InputMethodIsActivated(input_method_id)) {
297 scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods());
298 DCHECK(!input_methods->empty());
299 input_method_id_to_switch = input_methods->at(0).id();
300 if (!input_method_id.empty()) {
301 DVLOG(1) << "Can't change the current input method to "
302 << input_method_id << " since the engine is not enabled. "
303 << "Switch to " << input_method_id_to_switch << " instead.";
307 if (!component_extension_ime_manager_->IsInitialized() &&
308 !InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch)) {
309 // We can't change input method before the initialization of
310 // component extension ime manager. ChangeInputMethod will be
311 // called with |pending_input_method_| when the initialization is
312 // done.
313 pending_input_method_ = input_method_id_to_switch;
314 return false;
316 pending_input_method_.clear();
318 // Hide candidate window and info list.
319 if (candidate_window_controller_.get())
320 candidate_window_controller_->Hide();
322 // Disable the current engine handler.
323 IBusEngineHandlerInterface* engine =
324 IBusBridge::Get()->GetCurrentEngineHandler();
325 if (engine)
326 engine->Disable();
328 // Configure the next engine handler.
329 if (InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch)) {
330 IBusBridge::Get()->SetCurrentEngineHandler(NULL);
331 } else {
332 IBusEngineHandlerInterface* next_engine =
333 IBusBridge::Get()->SetCurrentEngineHandlerById(
334 input_method_id_to_switch);
336 if (next_engine)
337 next_engine->Enable();
340 // TODO(komatsu): Check if it is necessary to perform the above routine
341 // when the current input method is equal to |input_method_id_to_swich|.
342 if (current_input_method_.id() != input_method_id_to_switch) {
343 // Clear property list. Property list would be updated by
344 // extension IMEs via InputMethodEngine::(Set|Update)MenuItems.
345 // If the current input method is a keyboard layout, empty
346 // properties are sufficient.
347 const InputMethodPropertyList empty_property_list;
348 SetCurrentInputMethodProperties(empty_property_list);
350 const InputMethodDescriptor* descriptor = NULL;
351 if (extension_ime_util::IsExtensionIME(input_method_id_to_switch)) {
352 DCHECK(extra_input_methods_.find(input_method_id_to_switch) !=
353 extra_input_methods_.end());
354 descriptor = &(extra_input_methods_[input_method_id_to_switch]);
355 } else {
356 descriptor =
357 util_.GetInputMethodDescriptorFromId(input_method_id_to_switch);
359 DCHECK(descriptor);
361 previous_input_method_ = current_input_method_;
362 current_input_method_ = *descriptor;
365 // Change the keyboard layout to a preferred layout for the input method.
366 if (!xkeyboard_->SetCurrentKeyboardLayoutByName(
367 current_input_method_.GetPreferredKeyboardLayout())) {
368 LOG(ERROR) << "Failed to change keyboard layout to "
369 << current_input_method_.GetPreferredKeyboardLayout();
372 // Update input method indicators (e.g. "US", "DV") in Chrome windows.
373 FOR_EACH_OBSERVER(InputMethodManager::Observer,
374 observers_,
375 InputMethodChanged(this, show_message));
376 return true;
379 void InputMethodManagerImpl::OnComponentExtensionInitialized(
380 scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) {
381 DCHECK(thread_checker_.CalledOnValidThread());
382 component_extension_ime_manager_->Initialize(delegate.Pass());
383 util_.SetComponentExtensions(
384 component_extension_ime_manager_->GetAllIMEAsInputMethodDescriptor());
386 LoadNecessaryComponentExtensions();
388 if (!pending_input_method_.empty())
389 ChangeInputMethodInternal(pending_input_method_, false);
393 void InputMethodManagerImpl::LoadNecessaryComponentExtensions() {
394 if (!component_extension_ime_manager_->IsInitialized())
395 return;
396 // Load component extensions but also update |active_input_method_ids_| as
397 // some component extension IMEs may have been removed from the Chrome OS
398 // image. If specified component extension IME no longer exists, falling back
399 // to an existing IME.
400 std::vector<std::string> unfiltered_input_method_ids =
401 active_input_method_ids_;
402 active_input_method_ids_.clear();
403 for (size_t i = 0; i < unfiltered_input_method_ids.size(); ++i) {
404 if (!extension_ime_util::IsComponentExtensionIME(
405 unfiltered_input_method_ids[i])) {
406 // Legacy IMEs or xkb layouts are alwayes active.
407 active_input_method_ids_.push_back(unfiltered_input_method_ids[i]);
408 } else if (component_extension_ime_manager_->IsWhitelisted(
409 unfiltered_input_method_ids[i])) {
410 component_extension_ime_manager_->LoadComponentExtensionIME(
411 unfiltered_input_method_ids[i]);
412 active_input_method_ids_.push_back(unfiltered_input_method_ids[i]);
417 void InputMethodManagerImpl::ActivateInputMethodProperty(
418 const std::string& key) {
419 DCHECK(!key.empty());
421 for (size_t i = 0; i < property_list_.size(); ++i) {
422 if (property_list_[i].key == key) {
423 IBusEngineHandlerInterface* engine =
424 IBusBridge::Get()->GetCurrentEngineHandler();
425 if (engine)
426 engine->PropertyActivate(key);
427 return;
431 DVLOG(1) << "ActivateInputMethodProperty: unknown key: " << key;
434 void InputMethodManagerImpl::AddInputMethodExtension(
435 const std::string& id,
436 InputMethodEngineInterface* engine) {
437 if (state_ == STATE_TERMINATING)
438 return;
440 if (!extension_ime_util::IsExtensionIME(id) &&
441 !extension_ime_util::IsComponentExtensionIME(id)) {
442 DVLOG(1) << id << " is not a valid extension input method ID.";
443 return;
446 DCHECK(engine);
448 const InputMethodDescriptor& descriptor = engine->GetDescriptor();
449 extra_input_methods_[id] = descriptor;
450 if (Contains(enabled_extension_imes_, id) &&
451 !extension_ime_util::IsComponentExtensionIME(id)) {
452 if (!Contains(active_input_method_ids_, id)) {
453 active_input_method_ids_.push_back(id);
454 } else {
455 DVLOG(1) << "AddInputMethodExtension: alread added: "
456 << id << ", " << descriptor.name();
457 // Call Start() anyway, just in case.
460 // Ensure that the input method daemon is running.
461 MaybeInitializeCandidateWindowController();
464 IBusBridge::Get()->SetEngineHandler(id, engine);
467 void InputMethodManagerImpl::RemoveInputMethodExtension(const std::string& id) {
468 if (!extension_ime_util::IsExtensionIME(id))
469 DVLOG(1) << id << " is not a valid extension input method ID.";
471 std::vector<std::string>::iterator i = std::find(
472 active_input_method_ids_.begin(), active_input_method_ids_.end(), id);
473 if (i != active_input_method_ids_.end())
474 active_input_method_ids_.erase(i);
475 extra_input_methods_.erase(id);
477 // If |current_input_method| is no longer in |active_input_method_ids_|,
478 // switch to the first one in |active_input_method_ids_|.
479 ChangeInputMethod(current_input_method_.id());
481 if (IBusBridge::Get()->GetCurrentEngineHandler() ==
482 IBusBridge::Get()->GetEngineHandler(id))
483 IBusBridge::Get()->SetCurrentEngineHandler(NULL);
486 void InputMethodManagerImpl::GetInputMethodExtensions(
487 InputMethodDescriptors* result) {
488 // Build the extension input method descriptors from the extra input
489 // methods cache |extra_input_methods_|.
490 std::map<std::string, InputMethodDescriptor>::iterator iter;
491 for (iter = extra_input_methods_.begin(); iter != extra_input_methods_.end();
492 ++iter) {
493 if (extension_ime_util::IsExtensionIME(iter->first))
494 result->push_back(iter->second);
498 void InputMethodManagerImpl::SetEnabledExtensionImes(
499 std::vector<std::string>* ids) {
500 enabled_extension_imes_.clear();
501 enabled_extension_imes_.insert(enabled_extension_imes_.end(),
502 ids->begin(),
503 ids->end());
505 bool active_imes_changed = false;
507 for (std::map<std::string, InputMethodDescriptor>::iterator extra_iter =
508 extra_input_methods_.begin(); extra_iter != extra_input_methods_.end();
509 ++extra_iter) {
510 if (extension_ime_util::IsComponentExtensionIME(
511 extra_iter->first))
512 continue; // Do not filter component extension.
513 std::vector<std::string>::iterator active_iter = std::find(
514 active_input_method_ids_.begin(), active_input_method_ids_.end(),
515 extra_iter->first);
517 bool active = active_iter != active_input_method_ids_.end();
518 bool enabled = Contains(enabled_extension_imes_, extra_iter->first);
520 if (active && !enabled)
521 active_input_method_ids_.erase(active_iter);
523 if (!active && enabled)
524 active_input_method_ids_.push_back(extra_iter->first);
526 if (active == !enabled)
527 active_imes_changed = true;
530 if (active_imes_changed) {
531 MaybeInitializeCandidateWindowController();
533 // If |current_input_method| is no longer in |active_input_method_ids_|,
534 // switch to the first one in |active_input_method_ids_|.
535 ChangeInputMethod(current_input_method_.id());
539 void InputMethodManagerImpl::SetInputMethodDefault() {
540 // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
541 // and US dvorak keyboard layouts.
542 if (g_browser_process && g_browser_process->local_state()) {
543 const std::string locale = g_browser_process->GetApplicationLocale();
544 // If the preferred keyboard for the login screen has been saved, use it.
545 PrefService* prefs = g_browser_process->local_state();
546 std::string initial_input_method_id =
547 prefs->GetString(chromeos::language_prefs::kPreferredKeyboardLayout);
548 if (initial_input_method_id.empty()) {
549 // If kPreferredKeyboardLayout is not specified, use the hardware layout.
550 initial_input_method_id =
551 GetInputMethodUtil()->GetHardwareInputMethodId();
553 EnableLayouts(locale, initial_input_method_id);
557 bool InputMethodManagerImpl::SwitchToNextInputMethod() {
558 // Sanity checks.
559 if (active_input_method_ids_.empty()) {
560 DVLOG(1) << "active input method is empty";
561 return false;
564 if (current_input_method_.id().empty()) {
565 DVLOG(1) << "current_input_method_ is unknown";
566 return false;
569 // Do not consume key event if there is only one input method is enabled.
570 // Ctrl+Space or Alt+Shift may be used by other application.
571 if (active_input_method_ids_.size() == 1)
572 return false;
574 // Find the next input method and switch to it.
575 SwitchToNextInputMethodInternal(active_input_method_ids_,
576 current_input_method_.id());
577 return true;
580 bool InputMethodManagerImpl::SwitchToPreviousInputMethod(
581 const ui::Accelerator& accelerator) {
582 // Sanity check.
583 if (active_input_method_ids_.empty()) {
584 DVLOG(1) << "active input method is empty";
585 return false;
588 // Do not consume key event if there is only one input method is enabled.
589 // Ctrl+Space or Alt+Shift may be used by other application.
590 if (active_input_method_ids_.size() == 1)
591 return false;
593 if (accelerator.type() == ui::ET_KEY_RELEASED)
594 return true;
596 if (previous_input_method_.id().empty() ||
597 previous_input_method_.id() == current_input_method_.id()) {
598 return SwitchToNextInputMethod();
601 std::vector<std::string>::const_iterator iter =
602 std::find(active_input_method_ids_.begin(),
603 active_input_method_ids_.end(),
604 previous_input_method_.id());
605 if (iter == active_input_method_ids_.end()) {
606 // previous_input_method_ is not supported.
607 return SwitchToNextInputMethod();
609 ChangeInputMethodInternal(*iter, true);
610 return true;
613 bool InputMethodManagerImpl::SwitchInputMethod(
614 const ui::Accelerator& accelerator) {
615 // Sanity check.
616 if (active_input_method_ids_.empty()) {
617 DVLOG(1) << "active input method is empty";
618 return false;
621 // Get the list of input method ids for the |accelerator|. For example, get
622 // { "mozc-hangul", "xkb:kr:kr104:kor" } for ui::VKEY_DBE_SBCSCHAR.
623 std::vector<std::string> input_method_ids_to_switch;
624 switch (accelerator.key_code()) {
625 case ui::VKEY_CONVERT: // Henkan key on JP106 keyboard
626 input_method_ids_to_switch.push_back(nacl_mozc_jp_id);
627 break;
628 case ui::VKEY_NONCONVERT: // Muhenkan key on JP106 keyboard
629 input_method_ids_to_switch.push_back("xkb:jp::jpn");
630 break;
631 case ui::VKEY_DBE_SBCSCHAR: // ZenkakuHankaku key on JP106 keyboard
632 case ui::VKEY_DBE_DBCSCHAR:
633 input_method_ids_to_switch.push_back(nacl_mozc_jp_id);
634 input_method_ids_to_switch.push_back("xkb:jp::jpn");
635 break;
636 default:
637 NOTREACHED();
638 break;
640 if (input_method_ids_to_switch.empty()) {
641 DVLOG(1) << "Unexpected VKEY: " << accelerator.key_code();
642 return false;
645 // Obtain the intersection of input_method_ids_to_switch and
646 // active_input_method_ids_. The order of IDs in active_input_method_ids_ is
647 // preserved.
648 std::vector<std::string> ids;
649 for (size_t i = 0; i < input_method_ids_to_switch.size(); ++i) {
650 const std::string& id = input_method_ids_to_switch[i];
651 if (Contains(active_input_method_ids_, id))
652 ids.push_back(id);
654 if (ids.empty()) {
655 // No input method for the accelerator is active. For example, we should
656 // just ignore VKEY_HANGUL when mozc-hangul is not active.
657 return false;
660 SwitchToNextInputMethodInternal(ids, current_input_method_.id());
661 return true; // consume the accelerator.
664 void InputMethodManagerImpl::SwitchToNextInputMethodInternal(
665 const std::vector<std::string>& input_method_ids,
666 const std::string& current_input_method_id) {
667 std::vector<std::string>::const_iterator iter =
668 std::find(input_method_ids.begin(),
669 input_method_ids.end(),
670 current_input_method_id);
671 if (iter != input_method_ids.end())
672 ++iter;
673 if (iter == input_method_ids.end())
674 iter = input_method_ids.begin();
675 ChangeInputMethodInternal(*iter, true);
678 InputMethodDescriptor InputMethodManagerImpl::GetCurrentInputMethod() const {
679 if (current_input_method_.id().empty())
680 return InputMethodUtil::GetFallbackInputMethodDescriptor();
682 return current_input_method_;
685 InputMethodPropertyList
686 InputMethodManagerImpl::GetCurrentInputMethodProperties() const {
687 // This check is necessary since an IME property (e.g. for Pinyin) might be
688 // sent from ibus-daemon AFTER the current input method is switched to XKB.
689 if (InputMethodUtil::IsKeyboardLayout(GetCurrentInputMethod().id()))
690 return InputMethodPropertyList(); // Empty list.
691 return property_list_;
694 void InputMethodManagerImpl::SetCurrentInputMethodProperties(
695 const InputMethodPropertyList& property_list) {
696 property_list_ = property_list;
697 PropertyChanged();
700 XKeyboard* InputMethodManagerImpl::GetXKeyboard() {
701 return xkeyboard_.get();
704 InputMethodUtil* InputMethodManagerImpl::GetInputMethodUtil() {
705 return &util_;
708 ComponentExtensionIMEManager*
709 InputMethodManagerImpl::GetComponentExtensionIMEManager() {
710 DCHECK(thread_checker_.CalledOnValidThread());
711 return component_extension_ime_manager_.get();
714 void InputMethodManagerImpl::InitializeComponentExtension() {
715 ComponentExtensionIMEManagerImpl* impl =
716 new ComponentExtensionIMEManagerImpl();
717 scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate(impl);
718 impl->InitializeAsync(base::Bind(
719 &InputMethodManagerImpl::OnComponentExtensionInitialized,
720 weak_ptr_factory_.GetWeakPtr(),
721 base::Passed(&delegate)));
724 void InputMethodManagerImpl::Init(base::SequencedTaskRunner* ui_task_runner) {
725 DCHECK(thread_checker_.CalledOnValidThread());
727 xkeyboard_.reset(XKeyboard::Create());
729 // We can't call impl->Initialize here, because file thread is not available
730 // at this moment.
731 ui_task_runner->PostTask(
732 FROM_HERE,
733 base::Bind(&InputMethodManagerImpl::InitializeComponentExtension,
734 weak_ptr_factory_.GetWeakPtr()));
737 void InputMethodManagerImpl::SetCandidateWindowControllerForTesting(
738 CandidateWindowController* candidate_window_controller) {
739 candidate_window_controller_.reset(candidate_window_controller);
740 candidate_window_controller_->AddObserver(this);
743 void InputMethodManagerImpl::SetXKeyboardForTesting(XKeyboard* xkeyboard) {
744 xkeyboard_.reset(xkeyboard);
747 void InputMethodManagerImpl::InitializeComponentExtensionForTesting(
748 scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) {
749 OnComponentExtensionInitialized(delegate.Pass());
752 void InputMethodManagerImpl::PropertyChanged() {
753 FOR_EACH_OBSERVER(InputMethodManager::Observer,
754 observers_,
755 InputMethodPropertyChanged(this));
758 void InputMethodManagerImpl::CandidateClicked(int index) {
759 IBusEngineHandlerInterface* engine =
760 IBusBridge::Get()->GetCurrentEngineHandler();
761 if (engine)
762 engine->CandidateClicked(index);
765 void InputMethodManagerImpl::CandidateWindowOpened() {
766 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
767 candidate_window_observers_,
768 CandidateWindowOpened(this));
771 void InputMethodManagerImpl::CandidateWindowClosed() {
772 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
773 candidate_window_observers_,
774 CandidateWindowClosed(this));
777 void InputMethodManagerImpl::OnScreenLocked() {
778 saved_previous_input_method_ = previous_input_method_;
779 saved_current_input_method_ = current_input_method_;
780 saved_active_input_method_ids_ = active_input_method_ids_;
782 const std::string hardware_keyboard_id = util_.GetHardwareInputMethodId();
783 // We'll add the hardware keyboard if it's not included in
784 // |active_input_method_list| so that the user can always use the hardware
785 // keyboard on the screen locker.
786 bool should_add_hardware_keyboard = true;
788 active_input_method_ids_.clear();
789 for (size_t i = 0; i < saved_active_input_method_ids_.size(); ++i) {
790 const std::string& input_method_id = saved_active_input_method_ids_[i];
791 // Skip if it's not a keyboard layout. Drop input methods including
792 // extension ones.
793 if (!IsLoginKeyboard(input_method_id))
794 continue;
795 active_input_method_ids_.push_back(input_method_id);
796 if (input_method_id == hardware_keyboard_id)
797 should_add_hardware_keyboard = false;
799 if (should_add_hardware_keyboard)
800 active_input_method_ids_.push_back(hardware_keyboard_id);
802 ChangeInputMethod(current_input_method_.id());
805 void InputMethodManagerImpl::OnScreenUnlocked() {
806 previous_input_method_ = saved_previous_input_method_;
807 current_input_method_ = saved_current_input_method_;
808 active_input_method_ids_ = saved_active_input_method_ids_;
810 ChangeInputMethod(current_input_method_.id());
813 bool InputMethodManagerImpl::InputMethodIsActivated(
814 const std::string& input_method_id) {
815 return Contains(active_input_method_ids_, input_method_id);
818 bool InputMethodManagerImpl::ContainsOnlyKeyboardLayout(
819 const std::vector<std::string>& value) {
820 for (size_t i = 0; i < value.size(); ++i) {
821 if (!InputMethodUtil::IsKeyboardLayout(value[i]))
822 return false;
824 return true;
827 void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() {
828 if (candidate_window_controller_.get())
829 return;
831 candidate_window_controller_.reset(
832 CandidateWindowController::CreateCandidateWindowController());
833 candidate_window_controller_->AddObserver(this);
836 } // namespace input_method
837 } // namespace chromeos