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 "ash/ime/input_method_menu_item.h"
10 #include "ash/ime/input_method_menu_manager.h"
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/location.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/sys_info.h"
19 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/chromeos/input_method/candidate_window_controller.h"
21 #include "chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.h"
22 #include "chrome/browser/chromeos/input_method/input_method_engine.h"
23 #include "chrome/browser/chromeos/language_preferences.h"
24 #include "chrome/browser/profiles/profile_manager.h"
25 #include "chromeos/ime/component_extension_ime_manager.h"
26 #include "chromeos/ime/extension_ime_util.h"
27 #include "chromeos/ime/fake_ime_keyboard.h"
28 #include "chromeos/ime/ime_keyboard.h"
29 #include "chromeos/ime/input_method_delegate.h"
30 #include "third_party/icu/source/common/unicode/uloc.h"
31 #include "ui/base/accelerators/accelerator.h"
34 namespace input_method
{
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 return util_
.IsLoginKeyboard(layout
);
51 bool InputMethodManagerImpl::MigrateInputMethods(
52 std::vector
<std::string
>* input_method_ids
) {
53 return util_
.MigrateInputMethods(input_method_ids
);
56 InputMethodManagerImpl::InputMethodManagerImpl(
57 scoped_ptr
<InputMethodDelegate
> delegate
)
58 : delegate_(delegate
.Pass()),
59 state_(STATE_LOGIN_SCREEN
),
60 util_(delegate_
.get(), whitelist_
.GetSupportedInputMethods()),
61 component_extension_ime_manager_(new ComponentExtensionIMEManager()),
62 weak_ptr_factory_(this) {
65 InputMethodManagerImpl::~InputMethodManagerImpl() {
66 if (candidate_window_controller_
.get())
67 candidate_window_controller_
->RemoveObserver(this);
70 void InputMethodManagerImpl::AddObserver(
71 InputMethodManager::Observer
* observer
) {
72 observers_
.AddObserver(observer
);
75 void InputMethodManagerImpl::AddCandidateWindowObserver(
76 InputMethodManager::CandidateWindowObserver
* observer
) {
77 candidate_window_observers_
.AddObserver(observer
);
80 void InputMethodManagerImpl::RemoveObserver(
81 InputMethodManager::Observer
* observer
) {
82 observers_
.RemoveObserver(observer
);
85 void InputMethodManagerImpl::RemoveCandidateWindowObserver(
86 InputMethodManager::CandidateWindowObserver
* observer
) {
87 candidate_window_observers_
.RemoveObserver(observer
);
90 void InputMethodManagerImpl::SetState(State new_state
) {
91 const State old_state
= state_
;
94 case STATE_LOGIN_SCREEN
:
96 case STATE_BROWSER_SCREEN
:
97 if (old_state
== STATE_LOCK_SCREEN
)
100 case STATE_LOCK_SCREEN
:
103 case STATE_TERMINATING
: {
104 if (candidate_window_controller_
.get())
105 candidate_window_controller_
.reset();
111 scoped_ptr
<InputMethodDescriptors
>
112 InputMethodManagerImpl::GetSupportedInputMethods() const {
113 if (!IsXkbComponentExtensionAvailable())
114 return whitelist_
.GetSupportedInputMethods().Pass();
115 return scoped_ptr
<InputMethodDescriptors
>(new InputMethodDescriptors
).Pass();
118 scoped_ptr
<InputMethodDescriptors
>
119 InputMethodManagerImpl::GetActiveInputMethods() const {
120 scoped_ptr
<InputMethodDescriptors
> result(new InputMethodDescriptors
);
121 // Build the active input method descriptors from the active input
122 // methods cache |active_input_method_ids_|.
123 for (size_t i
= 0; i
< active_input_method_ids_
.size(); ++i
) {
124 const std::string
& input_method_id
= active_input_method_ids_
[i
];
125 const InputMethodDescriptor
* descriptor
=
126 util_
.GetInputMethodDescriptorFromId(input_method_id
);
128 result
->push_back(*descriptor
);
130 std::map
<std::string
, InputMethodDescriptor
>::const_iterator ix
=
131 extra_input_methods_
.find(input_method_id
);
132 if (ix
!= extra_input_methods_
.end())
133 result
->push_back(ix
->second
);
135 DVLOG(1) << "Descriptor is not found for: " << input_method_id
;
138 if (result
->empty()) {
139 // Initially |active_input_method_ids_| is empty. browser_tests might take
142 InputMethodUtil::GetFallbackInputMethodDescriptor());
144 return result
.Pass();
147 const std::vector
<std::string
>&
148 InputMethodManagerImpl::GetActiveInputMethodIds() const {
149 return active_input_method_ids_
;
152 size_t InputMethodManagerImpl::GetNumActiveInputMethods() const {
153 return active_input_method_ids_
.size();
156 const InputMethodDescriptor
* InputMethodManagerImpl::GetInputMethodFromId(
157 const std::string
& input_method_id
) const {
158 const InputMethodDescriptor
* ime
= util_
.GetInputMethodDescriptorFromId(
161 std::map
<std::string
, InputMethodDescriptor
>::const_iterator ix
=
162 extra_input_methods_
.find(input_method_id
);
163 if (ix
!= extra_input_methods_
.end())
169 void InputMethodManagerImpl::EnableLoginLayouts(
170 const std::string
& language_code
,
171 const std::vector
<std::string
>& initial_layouts
) {
172 if (state_
== STATE_TERMINATING
)
175 // First, hardware keyboard layout should be shown.
176 std::vector
<std::string
> candidates
=
177 util_
.GetHardwareLoginInputMethodIds();
179 // Seocnd, locale based input method should be shown.
180 // Add input methods associated with the language.
181 std::vector
<std::string
> layouts_from_locale
;
182 util_
.GetInputMethodIdsFromLanguageCode(language_code
,
183 kKeyboardLayoutsOnly
,
184 &layouts_from_locale
);
185 candidates
.insert(candidates
.end(), layouts_from_locale
.begin(),
186 layouts_from_locale
.end());
188 std::vector
<std::string
> layouts
;
189 // First, add the initial input method ID, if it's requested, to
190 // layouts, so it appears first on the list of active input
191 // methods at the input language status menu.
192 for (size_t i
= 0; i
< initial_layouts
.size(); ++i
) {
193 if (util_
.IsValidInputMethodId(initial_layouts
[i
])) {
194 if (IsLoginKeyboard(initial_layouts
[i
])) {
195 layouts
.push_back(initial_layouts
[i
]);
198 << "EnableLoginLayouts: ignoring non-login initial keyboard layout:"
199 << initial_layouts
[i
];
201 } else if (!initial_layouts
[i
].empty()) {
202 DVLOG(1) << "EnableLoginLayouts: ignoring non-keyboard or invalid ID: "
203 << initial_layouts
[i
];
207 // Add candidates to layouts, while skipping duplicates.
208 for (size_t i
= 0; i
< candidates
.size(); ++i
) {
209 const std::string
& candidate
= candidates
[i
];
210 // Not efficient, but should be fine, as the two vectors are very
211 // short (2-5 items).
212 if (!Contains(layouts
, candidate
) && IsLoginKeyboard(candidate
))
213 layouts
.push_back(candidate
);
216 MigrateInputMethods(&layouts
);
217 active_input_method_ids_
.swap(layouts
);
219 // Initialize candidate window controller and widgets such as
220 // candidate window, infolist and mode indicator. Note, mode
221 // indicator is used by only keyboard layout input methods.
222 if (active_input_method_ids_
.size() > 1)
223 MaybeInitializeCandidateWindowController();
225 // you can pass empty |initial_layout|.
226 ChangeInputMethod(initial_layouts
.empty() ? "" :
227 extension_ime_util::GetInputMethodIDByEngineID(initial_layouts
[0]));
230 // Adds new input method to given list.
231 bool InputMethodManagerImpl::EnableInputMethodImpl(
232 const std::string
& input_method_id
,
233 std::vector
<std::string
>* new_active_input_method_ids
) const {
234 DCHECK(new_active_input_method_ids
);
235 if (!util_
.IsValidInputMethodId(input_method_id
)) {
236 DVLOG(1) << "EnableInputMethod: Invalid ID: " << input_method_id
;
240 if (!Contains(*new_active_input_method_ids
, input_method_id
))
241 new_active_input_method_ids
->push_back(input_method_id
);
246 // Starts or stops the system input method framework as needed.
247 void InputMethodManagerImpl::ReconfigureIMFramework() {
248 LoadNecessaryComponentExtensions();
250 // Initialize candidate window controller and widgets such as
251 // candidate window, infolist and mode indicator. Note, mode
252 // indicator is used by only keyboard layout input methods.
253 MaybeInitializeCandidateWindowController();
256 bool InputMethodManagerImpl::EnableInputMethod(
257 const std::string
& input_method_id
) {
258 if (!EnableInputMethodImpl(input_method_id
, &active_input_method_ids_
))
261 ReconfigureIMFramework();
265 bool InputMethodManagerImpl::ReplaceEnabledInputMethods(
266 const std::vector
<std::string
>& new_active_input_method_ids
) {
267 if (state_
== STATE_TERMINATING
)
270 // Filter unknown or obsolete IDs.
271 std::vector
<std::string
> new_active_input_method_ids_filtered
;
273 for (size_t i
= 0; i
< new_active_input_method_ids
.size(); ++i
)
274 EnableInputMethodImpl(new_active_input_method_ids
[i
],
275 &new_active_input_method_ids_filtered
);
277 if (new_active_input_method_ids_filtered
.empty()) {
278 DVLOG(1) << "ReplaceEnabledInputMethods: No valid input method ID";
282 // Copy extension IDs to |new_active_input_method_ids_filtered|. We have to
283 // keep relative order of the extension input method IDs.
284 for (size_t i
= 0; i
< active_input_method_ids_
.size(); ++i
) {
285 const std::string
& input_method_id
= active_input_method_ids_
[i
];
286 if (extension_ime_util::IsExtensionIME(input_method_id
))
287 new_active_input_method_ids_filtered
.push_back(input_method_id
);
289 active_input_method_ids_
.swap(new_active_input_method_ids_filtered
);
290 MigrateInputMethods(&active_input_method_ids_
);
292 ReconfigureIMFramework();
294 // If |current_input_method| is no longer in |active_input_method_ids_|,
295 // ChangeInputMethod() picks the first one in |active_input_method_ids_|.
296 ChangeInputMethod(current_input_method_
.id());
300 void InputMethodManagerImpl::ChangeInputMethod(
301 const std::string
& input_method_id
) {
302 ChangeInputMethodInternal(input_method_id
, false);
305 bool InputMethodManagerImpl::ChangeInputMethodInternal(
306 const std::string
& input_method_id
,
308 if (state_
== STATE_TERMINATING
)
311 std::string input_method_id_to_switch
= input_method_id
;
314 if (!InputMethodIsActivated(input_method_id
)) {
315 scoped_ptr
<InputMethodDescriptors
> input_methods(GetActiveInputMethods());
316 DCHECK(!input_methods
->empty());
317 input_method_id_to_switch
= input_methods
->at(0).id();
318 if (!input_method_id
.empty()) {
319 DVLOG(1) << "Can't change the current input method to "
320 << input_method_id
<< " since the engine is not enabled. "
321 << "Switch to " << input_method_id_to_switch
<< " instead.";
325 if (!component_extension_ime_manager_
->IsInitialized() &&
326 !InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch
)) {
327 // We can't change input method before the initialization of
328 // component extension ime manager. ChangeInputMethod will be
329 // called with |pending_input_method_| when the initialization is
331 pending_input_method_
= input_method_id_to_switch
;
334 pending_input_method_
.clear();
336 // Hide candidate window and info list.
337 if (candidate_window_controller_
.get())
338 candidate_window_controller_
->Hide();
340 // Disable the current engine handler.
341 IMEEngineHandlerInterface
* engine
=
342 IMEBridge::Get()->GetCurrentEngineHandler();
346 // Configure the next engine handler.
347 if (InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch
) &&
348 !extension_ime_util::IsKeyboardLayoutExtension(
349 input_method_id_to_switch
)) {
350 IMEBridge::Get()->SetCurrentEngineHandler(NULL
);
352 IMEEngineHandlerInterface
* next_engine
=
353 profile_engine_map_
[GetProfile()][input_method_id_to_switch
];
355 IMEBridge::Get()->SetCurrentEngineHandler(next_engine
);
356 next_engine
->Enable();
360 // TODO(komatsu): Check if it is necessary to perform the above routine
361 // when the current input method is equal to |input_method_id_to_swich|.
362 if (current_input_method_
.id() != input_method_id_to_switch
) {
363 // Clear property list. Property list would be updated by
364 // extension IMEs via InputMethodEngine::(Set|Update)MenuItems.
365 // If the current input method is a keyboard layout, empty
366 // properties are sufficient.
367 const ash::ime::InputMethodMenuItemList empty_menu_item_list
;
368 ash::ime::InputMethodMenuManager
* input_method_menu_manager
=
369 ash::ime::InputMethodMenuManager::GetInstance();
370 input_method_menu_manager
->SetCurrentInputMethodMenuItemList(
371 empty_menu_item_list
);
373 const InputMethodDescriptor
* descriptor
= NULL
;
374 if (extension_ime_util::IsExtensionIME(input_method_id_to_switch
)) {
375 DCHECK(extra_input_methods_
.find(input_method_id_to_switch
) !=
376 extra_input_methods_
.end());
377 descriptor
= &(extra_input_methods_
[input_method_id_to_switch
]);
380 util_
.GetInputMethodDescriptorFromId(input_method_id_to_switch
);
382 LOG(ERROR
) << "Unknown input method id: " << input_method_id_to_switch
;
386 previous_input_method_
= current_input_method_
;
387 current_input_method_
= *descriptor
;
390 // Change the keyboard layout to a preferred layout for the input method.
391 if (!keyboard_
->SetCurrentKeyboardLayoutByName(
392 current_input_method_
.GetPreferredKeyboardLayout())) {
393 LOG(ERROR
) << "Failed to change keyboard layout to "
394 << current_input_method_
.GetPreferredKeyboardLayout();
397 // Update input method indicators (e.g. "US", "DV") in Chrome windows.
398 FOR_EACH_OBSERVER(InputMethodManager::Observer
,
400 InputMethodChanged(this, show_message
));
404 bool InputMethodManagerImpl::IsXkbComponentExtensionAvailable() const {
405 if (!component_extension_ime_manager_
->IsInitialized())
407 InputMethodDescriptors imes
=
408 component_extension_ime_manager_
->GetAllIMEAsInputMethodDescriptor();
409 for (size_t i
= 0; i
< imes
.size(); ++i
) {
410 if (StartsWithASCII(extension_ime_util::MaybeGetLegacyXkbId(
411 imes
[i
].id()), "xkb:", true))
417 void InputMethodManagerImpl::OnComponentExtensionInitialized(
418 scoped_ptr
<ComponentExtensionIMEManagerDelegate
> delegate
) {
419 DCHECK(thread_checker_
.CalledOnValidThread());
420 component_extension_ime_manager_
->Initialize(delegate
.Pass());
421 InputMethodDescriptors imes
=
422 component_extension_ime_manager_
->GetAllIMEAsInputMethodDescriptor();
423 // In case of XKB extension is not available (e.g. linux_chromeos), don't
424 // reset the input methods in InputMethodUtil, Instead append input methods.
425 if (IsXkbComponentExtensionAvailable())
426 util_
.ResetInputMethods(imes
);
428 util_
.AppendInputMethods(imes
);
430 LoadNecessaryComponentExtensions();
432 if (!pending_input_method_
.empty())
433 ChangeInputMethodInternal(pending_input_method_
, false);
436 void InputMethodManagerImpl::LoadNecessaryComponentExtensions() {
437 if (!component_extension_ime_manager_
->IsInitialized())
439 // Load component extensions but also update |active_input_method_ids_| as
440 // some component extension IMEs may have been removed from the Chrome OS
441 // image. If specified component extension IME no longer exists, falling back
442 // to an existing IME.
443 std::vector
<std::string
> unfiltered_input_method_ids
;
444 unfiltered_input_method_ids
.swap(active_input_method_ids_
);
445 for (size_t i
= 0; i
< unfiltered_input_method_ids
.size(); ++i
) {
446 if (!extension_ime_util::IsComponentExtensionIME(
447 unfiltered_input_method_ids
[i
])) {
448 // Legacy IMEs or xkb layouts are alwayes active.
449 active_input_method_ids_
.push_back(unfiltered_input_method_ids
[i
]);
450 } else if (component_extension_ime_manager_
->IsWhitelisted(
451 unfiltered_input_method_ids
[i
])) {
452 component_extension_ime_manager_
->LoadComponentExtensionIME(
453 unfiltered_input_method_ids
[i
]);
454 active_input_method_ids_
.push_back(unfiltered_input_method_ids
[i
]);
457 // TODO(shuchen): move this call in ComponentExtensionIMEManager.
458 component_extension_ime_manager_
->NotifyInitialized();
461 void InputMethodManagerImpl::ActivateInputMethodMenuItem(
462 const std::string
& key
) {
463 DCHECK(!key
.empty());
465 if (ash::ime::InputMethodMenuManager::GetInstance()->
466 HasInputMethodMenuItemForKey(key
)) {
467 IMEEngineHandlerInterface
* engine
=
468 IMEBridge::Get()->GetCurrentEngineHandler();
470 engine
->PropertyActivate(key
);
474 DVLOG(1) << "ActivateInputMethodMenuItem: unknown key: " << key
;
477 void InputMethodManagerImpl::AddInputMethodExtension(
478 const std::string
& id
,
479 InputMethodEngineInterface
* engine
) {
480 if (state_
== STATE_TERMINATING
)
483 if (!extension_ime_util::IsExtensionIME(id
) &&
484 !extension_ime_util::IsComponentExtensionIME(id
)) {
485 DVLOG(1) << id
<< " is not a valid extension input method ID.";
491 const InputMethodDescriptor
& descriptor
= engine
->GetDescriptor();
492 extra_input_methods_
[id
] = descriptor
;
493 if (Contains(enabled_extension_imes_
, id
) &&
494 !extension_ime_util::IsComponentExtensionIME(id
)) {
495 if (!Contains(active_input_method_ids_
, id
)) {
496 active_input_method_ids_
.push_back(id
);
498 DVLOG(1) << "AddInputMethodExtension: alread added: "
499 << id
<< ", " << descriptor
.name();
500 // Call Start() anyway, just in case.
503 // Ensure that the input method daemon is running.
504 MaybeInitializeCandidateWindowController();
507 profile_engine_map_
[GetProfile()][id
] = engine
;
510 void InputMethodManagerImpl::RemoveInputMethodExtension(const std::string
& id
) {
511 if (!extension_ime_util::IsExtensionIME(id
))
512 DVLOG(1) << id
<< " is not a valid extension input method ID.";
514 std::vector
<std::string
>::iterator i
= std::find(
515 active_input_method_ids_
.begin(), active_input_method_ids_
.end(), id
);
516 if (i
!= active_input_method_ids_
.end())
517 active_input_method_ids_
.erase(i
);
518 extra_input_methods_
.erase(id
);
520 // If |current_input_method| is no longer in |active_input_method_ids_|,
521 // switch to the first one in |active_input_method_ids_|.
522 ChangeInputMethod(current_input_method_
.id());
524 if (IMEBridge::Get()->GetCurrentEngineHandler() ==
525 profile_engine_map_
[GetProfile()][id
])
526 IMEBridge::Get()->SetCurrentEngineHandler(NULL
);
529 void InputMethodManagerImpl::GetInputMethodExtensions(
530 InputMethodDescriptors
* result
) {
531 // Build the extension input method descriptors from the extra input
532 // methods cache |extra_input_methods_|.
533 std::map
<std::string
, InputMethodDescriptor
>::iterator iter
;
534 for (iter
= extra_input_methods_
.begin(); iter
!= extra_input_methods_
.end();
536 if (extension_ime_util::IsExtensionIME(iter
->first
))
537 result
->push_back(iter
->second
);
541 void InputMethodManagerImpl::SetEnabledExtensionImes(
542 std::vector
<std::string
>* ids
) {
543 enabled_extension_imes_
.clear();
544 enabled_extension_imes_
.insert(enabled_extension_imes_
.end(),
548 bool active_imes_changed
= false;
550 for (std::map
<std::string
, InputMethodDescriptor
>::iterator extra_iter
=
551 extra_input_methods_
.begin(); extra_iter
!= extra_input_methods_
.end();
553 if (extension_ime_util::IsComponentExtensionIME(
555 continue; // Do not filter component extension.
556 std::vector
<std::string
>::iterator active_iter
= std::find(
557 active_input_method_ids_
.begin(), active_input_method_ids_
.end(),
560 bool active
= active_iter
!= active_input_method_ids_
.end();
561 bool enabled
= Contains(enabled_extension_imes_
, extra_iter
->first
);
563 if (active
&& !enabled
)
564 active_input_method_ids_
.erase(active_iter
);
566 if (!active
&& enabled
)
567 active_input_method_ids_
.push_back(extra_iter
->first
);
569 if (active
== !enabled
)
570 active_imes_changed
= true;
573 if (active_imes_changed
) {
574 MaybeInitializeCandidateWindowController();
576 // If |current_input_method| is no longer in |active_input_method_ids_|,
577 // switch to the first one in |active_input_method_ids_|.
578 ChangeInputMethod(current_input_method_
.id());
582 void InputMethodManagerImpl::SetInputMethodLoginDefault() {
583 // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
584 // and US dvorak keyboard layouts.
585 if (g_browser_process
&& g_browser_process
->local_state()) {
586 const std::string locale
= g_browser_process
->GetApplicationLocale();
587 // If the preferred keyboard for the login screen has been saved, use it.
588 PrefService
* prefs
= g_browser_process
->local_state();
589 std::string initial_input_method_id
=
590 prefs
->GetString(chromeos::language_prefs::kPreferredKeyboardLayout
);
591 std::vector
<std::string
> input_methods_to_be_enabled
;
592 if (initial_input_method_id
.empty()) {
593 // If kPreferredKeyboardLayout is not specified, use the hardware layout.
594 input_methods_to_be_enabled
= util_
.GetHardwareLoginInputMethodIds();
596 input_methods_to_be_enabled
.push_back(initial_input_method_id
);
598 EnableLoginLayouts(locale
, input_methods_to_be_enabled
);
602 bool InputMethodManagerImpl::SwitchToNextInputMethod() {
604 if (active_input_method_ids_
.empty()) {
605 DVLOG(1) << "active input method is empty";
609 if (current_input_method_
.id().empty()) {
610 DVLOG(1) << "current_input_method_ is unknown";
614 // Do not consume key event if there is only one input method is enabled.
615 // Ctrl+Space or Alt+Shift may be used by other application.
616 if (active_input_method_ids_
.size() == 1)
619 // Find the next input method and switch to it.
620 SwitchToNextInputMethodInternal(active_input_method_ids_
,
621 current_input_method_
.id());
625 bool InputMethodManagerImpl::SwitchToPreviousInputMethod(
626 const ui::Accelerator
& accelerator
) {
628 if (active_input_method_ids_
.empty()) {
629 DVLOG(1) << "active input method is empty";
633 // Do not consume key event if there is only one input method is enabled.
634 // Ctrl+Space or Alt+Shift may be used by other application.
635 if (active_input_method_ids_
.size() == 1)
638 if (accelerator
.type() == ui::ET_KEY_RELEASED
)
641 if (previous_input_method_
.id().empty() ||
642 previous_input_method_
.id() == current_input_method_
.id()) {
643 return SwitchToNextInputMethod();
646 std::vector
<std::string
>::const_iterator iter
=
647 std::find(active_input_method_ids_
.begin(),
648 active_input_method_ids_
.end(),
649 previous_input_method_
.id());
650 if (iter
== active_input_method_ids_
.end()) {
651 // previous_input_method_ is not supported.
652 return SwitchToNextInputMethod();
654 ChangeInputMethodInternal(*iter
, true);
658 bool InputMethodManagerImpl::SwitchInputMethod(
659 const ui::Accelerator
& accelerator
) {
661 if (active_input_method_ids_
.empty()) {
662 DVLOG(1) << "active input method is empty";
666 // Get the list of input method ids for the |accelerator|. For example, get
667 // { "mozc-hangul", "xkb:kr:kr104:kor" } for ui::VKEY_DBE_SBCSCHAR.
668 std::vector
<std::string
> input_method_ids_to_switch
;
669 switch (accelerator
.key_code()) {
670 case ui::VKEY_CONVERT
: // Henkan key on JP106 keyboard
671 input_method_ids_to_switch
.push_back(
672 extension_ime_util::GetInputMethodIDByEngineID("nacl_mozc_jp"));
674 case ui::VKEY_NONCONVERT
: // Muhenkan key on JP106 keyboard
675 input_method_ids_to_switch
.push_back(
676 extension_ime_util::GetInputMethodIDByEngineID("xkb:jp::jpn"));
678 case ui::VKEY_DBE_SBCSCHAR
: // ZenkakuHankaku key on JP106 keyboard
679 case ui::VKEY_DBE_DBCSCHAR
:
680 input_method_ids_to_switch
.push_back(
681 extension_ime_util::GetInputMethodIDByEngineID("nacl_mozc_jp"));
682 input_method_ids_to_switch
.push_back(
683 extension_ime_util::GetInputMethodIDByEngineID("xkb:jp::jpn"));
689 if (input_method_ids_to_switch
.empty()) {
690 DVLOG(1) << "Unexpected VKEY: " << accelerator
.key_code();
694 // Obtain the intersection of input_method_ids_to_switch and
695 // active_input_method_ids_. The order of IDs in active_input_method_ids_ is
697 std::vector
<std::string
> ids
;
698 for (size_t i
= 0; i
< input_method_ids_to_switch
.size(); ++i
) {
699 const std::string
& id
= input_method_ids_to_switch
[i
];
700 if (Contains(active_input_method_ids_
, id
))
704 // No input method for the accelerator is active. For example, we should
705 // just ignore VKEY_HANGUL when mozc-hangul is not active.
709 SwitchToNextInputMethodInternal(ids
, current_input_method_
.id());
710 return true; // consume the accelerator.
713 void InputMethodManagerImpl::SwitchToNextInputMethodInternal(
714 const std::vector
<std::string
>& input_method_ids
,
715 const std::string
& current_input_method_id
) {
716 std::vector
<std::string
>::const_iterator iter
=
717 std::find(input_method_ids
.begin(),
718 input_method_ids
.end(),
719 current_input_method_id
);
720 if (iter
!= input_method_ids
.end())
722 if (iter
== input_method_ids
.end())
723 iter
= input_method_ids
.begin();
724 ChangeInputMethodInternal(*iter
, true);
727 InputMethodDescriptor
InputMethodManagerImpl::GetCurrentInputMethod() const {
728 if (current_input_method_
.id().empty())
729 return InputMethodUtil::GetFallbackInputMethodDescriptor();
731 return current_input_method_
;
734 bool InputMethodManagerImpl::IsISOLevel5ShiftUsedByCurrentInputMethod() const {
735 return keyboard_
->IsISOLevel5ShiftAvailable();
738 bool InputMethodManagerImpl::IsAltGrUsedByCurrentInputMethod() const {
739 return keyboard_
->IsAltGrAvailable();
742 ImeKeyboard
* InputMethodManagerImpl::GetImeKeyboard() {
743 return keyboard_
.get();
746 InputMethodUtil
* InputMethodManagerImpl::GetInputMethodUtil() {
750 ComponentExtensionIMEManager
*
751 InputMethodManagerImpl::GetComponentExtensionIMEManager() {
752 DCHECK(thread_checker_
.CalledOnValidThread());
753 return component_extension_ime_manager_
.get();
756 void InputMethodManagerImpl::InitializeComponentExtension() {
757 ComponentExtensionIMEManagerImpl
* impl
=
758 new ComponentExtensionIMEManagerImpl();
759 scoped_ptr
<ComponentExtensionIMEManagerDelegate
> delegate(impl
);
760 impl
->InitializeAsync(base::Bind(
761 &InputMethodManagerImpl::OnComponentExtensionInitialized
,
762 weak_ptr_factory_
.GetWeakPtr(),
763 base::Passed(&delegate
)));
766 void InputMethodManagerImpl::Init(base::SequencedTaskRunner
* ui_task_runner
) {
767 DCHECK(thread_checker_
.CalledOnValidThread());
769 if (base::SysInfo::IsRunningOnChromeOS())
770 keyboard_
.reset(ImeKeyboard::Create());
772 keyboard_
.reset(new FakeImeKeyboard());
774 // We can't call impl->Initialize here, because file thread is not available
776 ui_task_runner
->PostTask(
778 base::Bind(&InputMethodManagerImpl::InitializeComponentExtension
,
779 weak_ptr_factory_
.GetWeakPtr()));
782 void InputMethodManagerImpl::SetCandidateWindowControllerForTesting(
783 CandidateWindowController
* candidate_window_controller
) {
784 candidate_window_controller_
.reset(candidate_window_controller
);
785 candidate_window_controller_
->AddObserver(this);
788 void InputMethodManagerImpl::SetImeKeyboardForTesting(ImeKeyboard
* keyboard
) {
789 keyboard_
.reset(keyboard
);
792 void InputMethodManagerImpl::InitializeComponentExtensionForTesting(
793 scoped_ptr
<ComponentExtensionIMEManagerDelegate
> delegate
) {
794 OnComponentExtensionInitialized(delegate
.Pass());
797 void InputMethodManagerImpl::CandidateClicked(int index
) {
798 IMEEngineHandlerInterface
* engine
=
799 IMEBridge::Get()->GetCurrentEngineHandler();
801 engine
->CandidateClicked(index
);
804 void InputMethodManagerImpl::CandidateWindowOpened() {
805 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver
,
806 candidate_window_observers_
,
807 CandidateWindowOpened(this));
810 void InputMethodManagerImpl::CandidateWindowClosed() {
811 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver
,
812 candidate_window_observers_
,
813 CandidateWindowClosed(this));
816 void InputMethodManagerImpl::OnScreenLocked() {
817 saved_previous_input_method_
= previous_input_method_
;
818 saved_current_input_method_
= current_input_method_
;
819 saved_active_input_method_ids_
= active_input_method_ids_
;
821 std::set
<std::string
> added_ids_
;
823 const std::vector
<std::string
>& hardware_keyboard_ids
=
824 util_
.GetHardwareLoginInputMethodIds();
826 active_input_method_ids_
.clear();
827 for (size_t i
= 0; i
< saved_active_input_method_ids_
.size(); ++i
) {
828 const std::string
& input_method_id
= saved_active_input_method_ids_
[i
];
829 // Skip if it's not a keyboard layout. Drop input methods including
831 if (!IsLoginKeyboard(input_method_id
) ||
832 added_ids_
.find(input_method_id
) != added_ids_
.end())
834 active_input_method_ids_
.push_back(input_method_id
);
835 added_ids_
.insert(input_method_id
);
838 // We'll add the hardware keyboard if it's not included in
839 // |active_input_method_ids_| so that the user can always use the hardware
840 // keyboard on the screen locker.
841 for (size_t i
= 0; i
< hardware_keyboard_ids
.size(); ++i
) {
842 if (added_ids_
.find(hardware_keyboard_ids
[i
]) == added_ids_
.end()) {
843 active_input_method_ids_
.push_back(hardware_keyboard_ids
[i
]);
844 added_ids_
.insert(hardware_keyboard_ids
[i
]);
848 ChangeInputMethod(current_input_method_
.id());
851 void InputMethodManagerImpl::OnScreenUnlocked() {
852 previous_input_method_
= saved_previous_input_method_
;
853 current_input_method_
= saved_current_input_method_
;
854 active_input_method_ids_
= saved_active_input_method_ids_
;
856 ChangeInputMethod(current_input_method_
.id());
859 bool InputMethodManagerImpl::InputMethodIsActivated(
860 const std::string
& input_method_id
) {
861 return Contains(active_input_method_ids_
, input_method_id
);
864 void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() {
865 if (candidate_window_controller_
.get())
868 candidate_window_controller_
.reset(
869 CandidateWindowController::CreateCandidateWindowController());
870 candidate_window_controller_
->AddObserver(this);
873 Profile
* InputMethodManagerImpl::GetProfile() const {
874 return ProfileManager::GetActiveUserProfile();
877 } // namespace input_method
878 } // namespace chromeos