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_ibus.h"
20 #include "chrome/browser/chromeos/language_preferences.h"
21 #include "chromeos/dbus/dbus_thread_manager.h"
22 #include "chromeos/dbus/ibus/ibus_client.h"
23 #include "chromeos/ime/component_extension_ime_manager.h"
24 #include "chromeos/ime/extension_ime_util.h"
25 #include "chromeos/ime/input_method_delegate.h"
26 #include "chromeos/ime/xkeyboard.h"
27 #include "third_party/icu/source/common/unicode/uloc.h"
28 #include "ui/base/accelerators/accelerator.h"
31 namespace input_method {
35 const char nacl_mozc_jp_id[] =
36 "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_jp";
38 bool Contains(const std::vector<std::string>& container,
39 const std::string& value) {
40 return std::find(container.begin(), container.end(), value) !=
46 bool InputMethodManagerImpl::IsLoginKeyboard(
47 const std::string& layout) const {
48 const InputMethodDescriptor* ime =
49 util_.GetInputMethodDescriptorFromId(layout);
50 return ime ? ime->is_login_keyboard() : false;
53 InputMethodManagerImpl::InputMethodManagerImpl(
54 scoped_ptr<InputMethodDelegate> delegate)
55 : delegate_(delegate.Pass()),
56 state_(STATE_LOGIN_SCREEN),
57 util_(delegate_.get(), GetSupportedInputMethods()),
58 component_extension_ime_manager_(new ComponentExtensionIMEManager()),
59 weak_ptr_factory_(this) {
60 IBusDaemonController::GetInstance()->AddObserver(this);
63 InputMethodManagerImpl::~InputMethodManagerImpl() {
64 if (ibus_controller_.get())
65 ibus_controller_->RemoveObserver(this);
66 IBusDaemonController::GetInstance()->RemoveObserver(this);
67 if (candidate_window_controller_.get()) {
68 candidate_window_controller_->RemoveObserver(this);
69 candidate_window_controller_->Shutdown();
73 void InputMethodManagerImpl::AddObserver(
74 InputMethodManager::Observer* observer) {
75 observers_.AddObserver(observer);
78 void InputMethodManagerImpl::AddCandidateWindowObserver(
79 InputMethodManager::CandidateWindowObserver* observer) {
80 candidate_window_observers_.AddObserver(observer);
83 void InputMethodManagerImpl::RemoveObserver(
84 InputMethodManager::Observer* observer) {
85 observers_.RemoveObserver(observer);
88 void InputMethodManagerImpl::RemoveCandidateWindowObserver(
89 InputMethodManager::CandidateWindowObserver* observer) {
90 candidate_window_observers_.RemoveObserver(observer);
93 void InputMethodManagerImpl::SetState(State new_state) {
94 const State old_state = state_;
97 case STATE_LOGIN_SCREEN:
99 case STATE_BROWSER_SCREEN:
100 if (old_state == STATE_LOCK_SCREEN)
103 case STATE_LOCK_SCREEN:
106 case STATE_TERMINATING: {
107 if (candidate_window_controller_.get()) {
108 candidate_window_controller_->Shutdown();
109 candidate_window_controller_.reset();
116 scoped_ptr<InputMethodDescriptors>
117 InputMethodManagerImpl::GetSupportedInputMethods() const {
118 return whitelist_.GetSupportedInputMethods();
121 scoped_ptr<InputMethodDescriptors>
122 InputMethodManagerImpl::GetActiveInputMethods() const {
123 scoped_ptr<InputMethodDescriptors> result(new InputMethodDescriptors);
124 // Build the active input method descriptors from the active input
125 // methods cache |active_input_method_ids_|.
126 for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
127 const std::string& input_method_id = active_input_method_ids_[i];
128 const InputMethodDescriptor* descriptor =
129 util_.GetInputMethodDescriptorFromId(input_method_id);
131 result->push_back(*descriptor);
133 std::map<std::string, InputMethodDescriptor>::const_iterator ix =
134 extra_input_methods_.find(input_method_id);
135 if (ix != extra_input_methods_.end())
136 result->push_back(ix->second);
138 DVLOG(1) << "Descriptor is not found for: " << input_method_id;
141 if (result->empty()) {
142 // Initially |active_input_method_ids_| is empty. browser_tests might take
145 InputMethodUtil::GetFallbackInputMethodDescriptor());
147 return result.Pass();
150 const std::vector<std::string>&
151 InputMethodManagerImpl::GetActiveInputMethodIds() const {
152 return active_input_method_ids_;
155 size_t InputMethodManagerImpl::GetNumActiveInputMethods() const {
156 return active_input_method_ids_.size();
159 void InputMethodManagerImpl::EnableLayouts(const std::string& language_code,
160 const std::string& initial_layout) {
161 if (state_ == STATE_TERMINATING)
164 std::vector<std::string> candidates;
165 // Add input methods associated with the language.
166 util_.GetInputMethodIdsFromLanguageCode(language_code,
167 kKeyboardLayoutsOnly,
169 // Add the hardware keyboard as well. We should always add this so users
170 // can use the hardware keyboard on the login screen and the screen locker.
171 candidates.push_back(util_.GetHardwareInputMethodId());
173 std::vector<std::string> layouts;
174 // First, add the initial input method ID, if it's requested, to
175 // layouts, so it appears first on the list of active input
176 // methods at the input language status menu.
177 if (util_.IsValidInputMethodId(initial_layout) &&
178 IsLoginKeyboard(initial_layout)) {
179 layouts.push_back(initial_layout);
180 } else if (!initial_layout.empty()) {
181 DVLOG(1) << "EnableLayouts: ignoring non-keyboard or invalid ID: "
185 // Add candidates to layouts, while skipping duplicates.
186 for (size_t i = 0; i < candidates.size(); ++i) {
187 const std::string& candidate = candidates[i];
188 // Not efficient, but should be fine, as the two vectors are very
189 // short (2-5 items).
190 if (!Contains(layouts, candidate) && IsLoginKeyboard(candidate))
191 layouts.push_back(candidate);
194 active_input_method_ids_.swap(layouts);
195 ChangeInputMethod(initial_layout); // you can pass empty |initial_layout|.
198 // Adds new input method to given list.
199 bool InputMethodManagerImpl::EnableInputMethodImpl(
200 const std::string& input_method_id,
201 std::vector<std::string>& new_active_input_method_ids) const {
202 if (!util_.IsValidInputMethodId(input_method_id)) {
203 DVLOG(1) << "EnableInputMethod: Invalid ID: " << input_method_id;
207 if (!Contains(new_active_input_method_ids, input_method_id))
208 new_active_input_method_ids.push_back(input_method_id);
213 // Starts or stops the system input method framework as needed.
214 void InputMethodManagerImpl::ReconfigureIMFramework() {
215 if (component_extension_ime_manager_->IsInitialized())
216 LoadNecessaryComponentExtensions();
218 if (ContainsOnlyKeyboardLayout(active_input_method_ids_)) {
219 // Do NOT call ibus_controller_->Stop(); here to work around a crash issue
220 // at crbug.com/27051.
221 // TODO(yusukes): We can safely call Stop(); here once crbug.com/26443
224 MaybeInitializeCandidateWindowController();
225 IBusDaemonController::GetInstance()->Start();
229 bool InputMethodManagerImpl::EnableInputMethod(
230 const std::string& input_method_id) {
231 if (!EnableInputMethodImpl(input_method_id, active_input_method_ids_))
234 ReconfigureIMFramework();
238 bool InputMethodManagerImpl::EnableInputMethods(
239 const std::vector<std::string>& new_active_input_method_ids) {
240 if (state_ == STATE_TERMINATING)
243 // Filter unknown or obsolete IDs.
244 std::vector<std::string> new_active_input_method_ids_filtered;
246 for (size_t i = 0; i < new_active_input_method_ids.size(); ++i)
247 EnableInputMethodImpl(new_active_input_method_ids[i],
248 new_active_input_method_ids_filtered);
250 if (new_active_input_method_ids_filtered.empty()) {
251 DVLOG(1) << "EnableInputMethods: No valid input method ID";
255 // Copy extension IDs to |new_active_input_method_ids_filtered|. We have to
256 // keep relative order of the extension input method IDs.
257 for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
258 const std::string& input_method_id = active_input_method_ids_[i];
259 if (extension_ime_util::IsExtensionIME(input_method_id))
260 new_active_input_method_ids_filtered.push_back(input_method_id);
262 active_input_method_ids_.swap(new_active_input_method_ids_filtered);
264 ReconfigureIMFramework();
266 // If |current_input_method| is no longer in |active_input_method_ids_|,
267 // ChangeInputMethod() picks the first one in |active_input_method_ids_|.
268 ChangeInputMethod(current_input_method_.id());
272 void InputMethodManagerImpl::ChangeInputMethod(
273 const std::string& input_method_id) {
274 ChangeInputMethodInternal(input_method_id, false);
277 bool InputMethodManagerImpl::ChangeInputMethodInternal(
278 const std::string& input_method_id,
280 if (state_ == STATE_TERMINATING)
283 std::string input_method_id_to_switch = input_method_id;
286 if (!InputMethodIsActivated(input_method_id)) {
287 scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods());
288 DCHECK(!input_methods->empty());
289 input_method_id_to_switch = input_methods->at(0).id();
290 if (!input_method_id.empty()) {
291 DVLOG(1) << "Can't change the current input method to "
292 << input_method_id << " since the engine is not enabled. "
293 << "Switch to " << input_method_id_to_switch << " instead.";
297 if (!component_extension_ime_manager_->IsInitialized() ||
298 (!InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch) &&
299 !IsIBusConnectionAlive())) {
300 // We can't change input method before the initialization of component
301 // extension ime manager or before connection to ibus-daemon is not
302 // established. ChangeInputMethod will be called with
303 // |pending_input_method_| when the both initialization is done.
304 pending_input_method_ = input_method_id_to_switch;
308 pending_input_method_.clear();
309 IBusEngineHandlerInterface* engine = IBusBridge::Get()->GetEngineHandler();
310 const std::string current_input_method_id = current_input_method_.id();
311 IBusClient* client = DBusThreadManager::Get()->GetIBusClient();
312 if (InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch)) {
313 FOR_EACH_OBSERVER(InputMethodManager::Observer,
315 InputMethodPropertyChanged(this));
318 IBusBridge::Get()->SetEngineHandler(NULL);
322 client->SetGlobalEngine(input_method_id_to_switch,
323 base::Bind(&base::DoNothing));
326 if (current_input_method_id != input_method_id_to_switch) {
327 // Clear input method properties unconditionally if
328 // |input_method_id_to_switch| is not equal to |current_input_method_id|.
330 // When switching to another input method and no text area is focused,
331 // RegisterProperties signal for the new input method will NOT be sent
332 // until a text area is focused. Therefore, we have to clear the old input
333 // method properties here to keep the input method switcher status
336 // When |input_method_id_to_switch| and |current_input_method_id| are the
337 // same, the properties shouldn't be cleared. If we do that, something
338 // wrong happens in step #4 below:
339 // 1. Enable "xkb:us::eng" and "mozc". Switch to "mozc".
340 // 2. Focus Omnibox. IME properties for mozc are sent to Chrome.
341 // 3. Switch to "xkb:us::eng". No function in this file is called.
342 // 4. Switch back to "mozc". ChangeInputMethod("mozc") is called, but it's
343 // basically NOP since ibus-daemon's current IME is already "mozc".
344 // IME properties are not sent to Chrome for the same reason.
345 // TODO(nona): Revisit above comment once ibus-daemon is gone.
346 ibus_controller_->ClearProperties();
348 const InputMethodDescriptor* descriptor = NULL;
349 if (!extension_ime_util::IsExtensionIME(input_method_id_to_switch)) {
351 util_.GetInputMethodDescriptorFromId(input_method_id_to_switch);
353 std::map<std::string, InputMethodDescriptor>::const_iterator i =
354 extra_input_methods_.find(input_method_id_to_switch);
355 DCHECK(i != extra_input_methods_.end());
356 descriptor = &(i->second);
360 previous_input_method_ = current_input_method_;
361 current_input_method_ = *descriptor;
364 // Change the keyboard layout to a preferred layout for the input method.
365 if (!xkeyboard_->SetCurrentKeyboardLayoutByName(
366 current_input_method_.GetPreferredKeyboardLayout())) {
367 LOG(ERROR) << "Failed to change keyboard layout to "
368 << current_input_method_.GetPreferredKeyboardLayout();
371 // Update input method indicators (e.g. "US", "DV") in Chrome windows.
372 FOR_EACH_OBSERVER(InputMethodManager::Observer,
374 InputMethodChanged(this, show_message));
378 void InputMethodManagerImpl::OnComponentExtensionInitialized(
379 scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) {
380 DCHECK(thread_checker_.CalledOnValidThread());
381 component_extension_ime_manager_->Initialize(delegate.Pass());
382 util_.SetComponentExtensions(
383 component_extension_ime_manager_->GetAllIMEAsInputMethodDescriptor());
385 LoadNecessaryComponentExtensions();
387 if (!pending_input_method_.empty())
388 ChangeInputMethodInternal(pending_input_method_, false);
392 void InputMethodManagerImpl::LoadNecessaryComponentExtensions() {
393 if (!component_extension_ime_manager_->IsInitialized())
395 // Load component extensions but also update |active_input_method_ids_| as
396 // some component extension IMEs may have been removed from the Chrome OS
397 // image. If specified component extension IME no longer exists, falling back
398 // to an existing IME.
399 std::vector<std::string> unfiltered_input_method_ids =
400 active_input_method_ids_;
401 active_input_method_ids_.clear();
402 for (size_t i = 0; i < unfiltered_input_method_ids.size(); ++i) {
403 if (!component_extension_ime_manager_->IsComponentExtensionIMEId(
404 unfiltered_input_method_ids[i])) {
405 // Legacy IMEs or xkb layouts are alwayes active.
406 active_input_method_ids_.push_back(unfiltered_input_method_ids[i]);
407 } else if (component_extension_ime_manager_->IsWhitelisted(
408 unfiltered_input_method_ids[i])) {
409 component_extension_ime_manager_->LoadComponentExtensionIME(
410 unfiltered_input_method_ids[i]);
411 active_input_method_ids_.push_back(unfiltered_input_method_ids[i]);
416 void InputMethodManagerImpl::ActivateInputMethodProperty(
417 const std::string& key) {
418 DCHECK(!key.empty());
419 ibus_controller_->ActivateInputMethodProperty(key);
422 void InputMethodManagerImpl::AddInputMethodExtension(
423 const std::string& id,
424 const std::string& name,
425 const std::vector<std::string>& layouts,
426 const std::vector<std::string>& languages,
427 const GURL& options_url,
428 InputMethodEngine* engine) {
429 if (state_ == STATE_TERMINATING)
432 if (!extension_ime_util::IsExtensionIME(id) &&
433 !ComponentExtensionIMEManager::IsComponentExtensionIMEId(id)) {
434 DVLOG(1) << id << " is not a valid extension input method ID.";
438 extra_input_methods_[id] =
439 InputMethodDescriptor(id, name, layouts, languages, false, options_url);
440 if (Contains(enabled_extension_imes_, id) &&
441 !ComponentExtensionIMEManager::IsComponentExtensionIMEId(id)) {
442 if (!Contains(active_input_method_ids_, id)) {
443 active_input_method_ids_.push_back(id);
445 DVLOG(1) << "AddInputMethodExtension: alread added: "
446 << id << ", " << name;
447 // Call Start() anyway, just in case.
450 // Ensure that the input method daemon is running.
451 MaybeInitializeCandidateWindowController();
452 IBusDaemonController::GetInstance()->Start();
455 extra_input_method_instances_[id] =
456 static_cast<InputMethodEngineIBus*>(engine);
459 void InputMethodManagerImpl::RemoveInputMethodExtension(const std::string& id) {
460 if (!extension_ime_util::IsExtensionIME(id))
461 DVLOG(1) << id << " is not a valid extension input method ID.";
463 std::vector<std::string>::iterator i = std::find(
464 active_input_method_ids_.begin(), active_input_method_ids_.end(), id);
465 if (i != active_input_method_ids_.end())
466 active_input_method_ids_.erase(i);
467 extra_input_methods_.erase(id);
469 if (ContainsOnlyKeyboardLayout(active_input_method_ids_)) {
470 // Do NOT call ibus_controller_->Stop(); here to work around a crash issue
471 // at crosbug.com/27051.
472 // TODO(yusukes): We can safely call Stop(); here once crosbug.com/26443
476 // If |current_input_method| is no longer in |active_input_method_ids_|,
477 // switch to the first one in |active_input_method_ids_|.
478 ChangeInputMethod(current_input_method_.id());
480 std::map<std::string, InputMethodEngineIBus*>::iterator ite =
481 extra_input_method_instances_.find(id);
482 if (ite == extra_input_method_instances_.end()) {
483 DVLOG(1) << "The engine instance of " << id << " has already gone.";
485 // Do NOT release the actual instance here. This class does not take an
486 // onwership of engine instance.
487 extra_input_method_instances_.erase(ite);
491 void InputMethodManagerImpl::GetInputMethodExtensions(
492 InputMethodDescriptors* result) {
493 // Build the extension input method descriptors from the extra input
494 // methods cache |extra_input_methods_|.
495 std::map<std::string, InputMethodDescriptor>::iterator iter;
496 for (iter = extra_input_methods_.begin(); iter != extra_input_methods_.end();
498 if (extension_ime_util::IsExtensionIME(iter->first))
499 result->push_back(iter->second);
503 void InputMethodManagerImpl::SetEnabledExtensionImes(
504 std::vector<std::string>* ids) {
505 enabled_extension_imes_.clear();
506 enabled_extension_imes_.insert(enabled_extension_imes_.end(),
510 bool active_imes_changed = false;
512 for (std::map<std::string, InputMethodDescriptor>::iterator extra_iter =
513 extra_input_methods_.begin(); extra_iter != extra_input_methods_.end();
515 if (ComponentExtensionIMEManager::IsComponentExtensionIMEId(
517 continue; // Do not filter component extension.
518 std::vector<std::string>::iterator active_iter = std::find(
519 active_input_method_ids_.begin(), active_input_method_ids_.end(),
522 bool active = active_iter != active_input_method_ids_.end();
523 bool enabled = Contains(enabled_extension_imes_, extra_iter->first);
525 if (active && !enabled)
526 active_input_method_ids_.erase(active_iter);
528 if (!active && enabled)
529 active_input_method_ids_.push_back(extra_iter->first);
531 if (active == !enabled)
532 active_imes_changed = true;
535 if (active_imes_changed) {
536 MaybeInitializeCandidateWindowController();
537 IBusDaemonController::GetInstance()->Start();
539 // If |current_input_method| is no longer in |active_input_method_ids_|,
540 // switch to the first one in |active_input_method_ids_|.
541 ChangeInputMethod(current_input_method_.id());
545 void InputMethodManagerImpl::SetInputMethodDefault() {
546 // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
547 // and US dvorak keyboard layouts.
548 if (g_browser_process && g_browser_process->local_state()) {
549 const std::string locale = g_browser_process->GetApplicationLocale();
550 // If the preferred keyboard for the login screen has been saved, use it.
551 PrefService* prefs = g_browser_process->local_state();
552 std::string initial_input_method_id =
553 prefs->GetString(chromeos::language_prefs::kPreferredKeyboardLayout);
554 if (initial_input_method_id.empty()) {
555 // If kPreferredKeyboardLayout is not specified, use the hardware layout.
556 initial_input_method_id =
557 GetInputMethodUtil()->GetHardwareInputMethodId();
559 EnableLayouts(locale, initial_input_method_id);
563 bool InputMethodManagerImpl::SwitchToNextInputMethod() {
565 if (active_input_method_ids_.empty()) {
566 DVLOG(1) << "active input method is empty";
569 if (current_input_method_.id().empty()) {
570 DVLOG(1) << "current_input_method_ is unknown";
574 // Do not consume key event if there is only one input method is enabled.
575 // Ctrl+Space or Alt+Shift may be used by other application.
576 if (active_input_method_ids_.size() == 1)
579 // Find the next input method and switch to it.
580 SwitchToNextInputMethodInternal(active_input_method_ids_,
581 current_input_method_.id());
585 bool InputMethodManagerImpl::SwitchToPreviousInputMethod(
586 const ui::Accelerator& accelerator) {
588 if (active_input_method_ids_.empty()) {
589 DVLOG(1) << "active input method is empty";
593 // Do not consume key event if there is only one input method is enabled.
594 // Ctrl+Space or Alt+Shift may be used by other application.
595 if (active_input_method_ids_.size() == 1)
598 if (accelerator.type() == ui::ET_KEY_RELEASED)
601 if (previous_input_method_.id().empty() ||
602 previous_input_method_.id() == current_input_method_.id()) {
603 return SwitchToNextInputMethod();
606 std::vector<std::string>::const_iterator iter =
607 std::find(active_input_method_ids_.begin(),
608 active_input_method_ids_.end(),
609 previous_input_method_.id());
610 if (iter == active_input_method_ids_.end()) {
611 // previous_input_method_ is not supported.
612 return SwitchToNextInputMethod();
614 ChangeInputMethodInternal(*iter, true);
618 bool InputMethodManagerImpl::SwitchInputMethod(
619 const ui::Accelerator& accelerator) {
621 if (active_input_method_ids_.empty()) {
622 DVLOG(1) << "active input method is empty";
626 // Get the list of input method ids for the |accelerator|. For example, get
627 // { "mozc-hangul", "xkb:kr:kr104:kor" } for ui::VKEY_DBE_SBCSCHAR.
628 std::vector<std::string> input_method_ids_to_switch;
629 switch (accelerator.key_code()) {
630 case ui::VKEY_CONVERT: // Henkan key on JP106 keyboard
631 input_method_ids_to_switch.push_back(nacl_mozc_jp_id);
633 case ui::VKEY_NONCONVERT: // Muhenkan key on JP106 keyboard
634 input_method_ids_to_switch.push_back("xkb:jp::jpn");
636 case ui::VKEY_DBE_SBCSCHAR: // ZenkakuHankaku key on JP106 keyboard
637 case ui::VKEY_DBE_DBCSCHAR:
638 input_method_ids_to_switch.push_back(nacl_mozc_jp_id);
639 input_method_ids_to_switch.push_back("xkb:jp::jpn");
645 if (input_method_ids_to_switch.empty()) {
646 DVLOG(1) << "Unexpected VKEY: " << accelerator.key_code();
650 // Obtain the intersection of input_method_ids_to_switch and
651 // active_input_method_ids_. The order of IDs in active_input_method_ids_ is
653 std::vector<std::string> ids;
654 for (size_t i = 0; i < input_method_ids_to_switch.size(); ++i) {
655 const std::string& id = input_method_ids_to_switch[i];
656 if (Contains(active_input_method_ids_, id))
660 // No input method for the accelerator is active. For example, we should
661 // just ignore VKEY_HANGUL when mozc-hangul is not active.
665 SwitchToNextInputMethodInternal(ids, current_input_method_.id());
666 return true; // consume the accelerator.
669 void InputMethodManagerImpl::SwitchToNextInputMethodInternal(
670 const std::vector<std::string>& input_method_ids,
671 const std::string& current_input_method_id) {
672 std::vector<std::string>::const_iterator iter =
673 std::find(input_method_ids.begin(),
674 input_method_ids.end(),
675 current_input_method_id);
676 if (iter != input_method_ids.end())
678 if (iter == input_method_ids.end())
679 iter = input_method_ids.begin();
680 ChangeInputMethodInternal(*iter, true);
683 InputMethodDescriptor InputMethodManagerImpl::GetCurrentInputMethod() const {
684 if (current_input_method_.id().empty())
685 return InputMethodUtil::GetFallbackInputMethodDescriptor();
686 return current_input_method_;
689 InputMethodPropertyList
690 InputMethodManagerImpl::GetCurrentInputMethodProperties() const {
691 // This check is necessary since an IME property (e.g. for Pinyin) might be
692 // sent from ibus-daemon AFTER the current input method is switched to XKB.
693 if (InputMethodUtil::IsKeyboardLayout(GetCurrentInputMethod().id()))
694 return InputMethodPropertyList();
695 return ibus_controller_->GetCurrentProperties();
698 XKeyboard* InputMethodManagerImpl::GetXKeyboard() {
699 return xkeyboard_.get();
702 InputMethodUtil* InputMethodManagerImpl::GetInputMethodUtil() {
706 ComponentExtensionIMEManager*
707 InputMethodManagerImpl::GetComponentExtensionIMEManager() {
708 DCHECK(thread_checker_.CalledOnValidThread());
709 return component_extension_ime_manager_.get();
712 void InputMethodManagerImpl::OnConnected() {
713 for (std::map<std::string, InputMethodEngineIBus*>::iterator ite =
714 extra_input_method_instances_.begin();
715 ite != extra_input_method_instances_.end();
717 if (Contains(enabled_extension_imes_, ite->first) ||
718 (component_extension_ime_manager_->IsInitialized() &&
719 component_extension_ime_manager_->IsWhitelisted(ite->first))) {
720 ite->second->OnConnected();
724 if (!pending_input_method_.empty())
725 ChangeInputMethodInternal(pending_input_method_, false);
728 void InputMethodManagerImpl::OnDisconnected() {
729 for (std::map<std::string, InputMethodEngineIBus*>::iterator ite =
730 extra_input_method_instances_.begin();
731 ite != extra_input_method_instances_.end();
733 if (Contains(enabled_extension_imes_, ite->first))
734 ite->second->OnDisconnected();
738 void InputMethodManagerImpl::InitializeComponentExtension() {
739 ComponentExtensionIMEManagerImpl* impl =
740 new ComponentExtensionIMEManagerImpl();
741 scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate(impl);
742 impl->InitializeAsync(base::Bind(
743 &InputMethodManagerImpl::OnComponentExtensionInitialized,
744 weak_ptr_factory_.GetWeakPtr(),
745 base::Passed(&delegate)));
748 void InputMethodManagerImpl::Init(base::SequencedTaskRunner* ui_task_runner) {
749 DCHECK(!ibus_controller_.get());
750 DCHECK(thread_checker_.CalledOnValidThread());
752 ibus_controller_.reset(IBusController::Create());
753 xkeyboard_.reset(XKeyboard::Create());
754 ibus_controller_->AddObserver(this);
756 // We can't call impl->Initialize here, because file thread is not available
758 ui_task_runner->PostTask(
760 base::Bind(&InputMethodManagerImpl::InitializeComponentExtension,
761 weak_ptr_factory_.GetWeakPtr()));
764 void InputMethodManagerImpl::SetIBusControllerForTesting(
765 IBusController* ibus_controller) {
766 ibus_controller_.reset(ibus_controller);
767 ibus_controller_->AddObserver(this);
770 void InputMethodManagerImpl::SetCandidateWindowControllerForTesting(
771 CandidateWindowController* candidate_window_controller) {
772 candidate_window_controller_.reset(candidate_window_controller);
773 candidate_window_controller_->Init();
774 candidate_window_controller_->AddObserver(this);
777 void InputMethodManagerImpl::SetXKeyboardForTesting(XKeyboard* xkeyboard) {
778 xkeyboard_.reset(xkeyboard);
781 void InputMethodManagerImpl::InitializeComponentExtensionForTesting(
782 scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) {
783 OnComponentExtensionInitialized(delegate.Pass());
786 void InputMethodManagerImpl::PropertyChanged() {
787 FOR_EACH_OBSERVER(InputMethodManager::Observer,
789 InputMethodPropertyChanged(this));
792 void InputMethodManagerImpl::CandidateWindowOpened() {
793 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
794 candidate_window_observers_,
795 CandidateWindowOpened(this));
798 void InputMethodManagerImpl::CandidateWindowClosed() {
799 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
800 candidate_window_observers_,
801 CandidateWindowClosed(this));
804 void InputMethodManagerImpl::OnScreenLocked() {
805 saved_previous_input_method_ = previous_input_method_;
806 saved_current_input_method_ = current_input_method_;
807 saved_active_input_method_ids_ = active_input_method_ids_;
809 const std::string hardware_keyboard_id = util_.GetHardwareInputMethodId();
810 // We'll add the hardware keyboard if it's not included in
811 // |active_input_method_list| so that the user can always use the hardware
812 // keyboard on the screen locker.
813 bool should_add_hardware_keyboard = true;
815 active_input_method_ids_.clear();
816 for (size_t i = 0; i < saved_active_input_method_ids_.size(); ++i) {
817 const std::string& input_method_id = saved_active_input_method_ids_[i];
818 // Skip if it's not a keyboard layout. Drop input methods including
820 if (!IsLoginKeyboard(input_method_id))
822 active_input_method_ids_.push_back(input_method_id);
823 if (input_method_id == hardware_keyboard_id)
824 should_add_hardware_keyboard = false;
826 if (should_add_hardware_keyboard)
827 active_input_method_ids_.push_back(hardware_keyboard_id);
829 ChangeInputMethod(current_input_method_.id());
832 void InputMethodManagerImpl::OnScreenUnlocked() {
833 previous_input_method_ = saved_previous_input_method_;
834 current_input_method_ = saved_current_input_method_;
835 active_input_method_ids_ = saved_active_input_method_ids_;
837 ChangeInputMethod(current_input_method_.id());
840 bool InputMethodManagerImpl::InputMethodIsActivated(
841 const std::string& input_method_id) {
842 return Contains(active_input_method_ids_, input_method_id);
845 bool InputMethodManagerImpl::ContainsOnlyKeyboardLayout(
846 const std::vector<std::string>& value) {
847 for (size_t i = 0; i < value.size(); ++i) {
848 if (!InputMethodUtil::IsKeyboardLayout(value[i]))
854 void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() {
855 if (candidate_window_controller_.get())
858 candidate_window_controller_.reset(
859 CandidateWindowController::CreateCandidateWindowController());
860 if (candidate_window_controller_->Init())
861 candidate_window_controller_->AddObserver(this);
863 DVLOG(1) << "Failed to initialize the candidate window controller";
866 bool InputMethodManagerImpl::IsIBusConnectionAlive() {
867 return DBusThreadManager::Get() && DBusThreadManager::Get()->GetIBusClient();
870 } // namespace input_method
871 } // namespace chromeos