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 const char nacl_mozc_jp_id
[] =
39 "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_jp";
41 bool Contains(const std::vector
<std::string
>& container
,
42 const std::string
& value
) {
43 return std::find(container
.begin(), container
.end(), value
) !=
49 bool InputMethodManagerImpl::IsLoginKeyboard(
50 const std::string
& layout
) const {
51 return util_
.IsLoginKeyboard(layout
);
54 bool InputMethodManagerImpl::MigrateXkbInputMethods(
55 std::vector
<std::string
>* input_method_ids
) {
56 return util_
.MigrateXkbInputMethods(input_method_ids
);
59 InputMethodManagerImpl::InputMethodManagerImpl(
60 scoped_ptr
<InputMethodDelegate
> delegate
)
61 : delegate_(delegate
.Pass()),
62 state_(STATE_LOGIN_SCREEN
),
63 util_(delegate_
.get(), GetSupportedInputMethods()),
64 component_extension_ime_manager_(new ComponentExtensionIMEManager()),
65 weak_ptr_factory_(this) {
68 InputMethodManagerImpl::~InputMethodManagerImpl() {
69 if (candidate_window_controller_
.get())
70 candidate_window_controller_
->RemoveObserver(this);
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_
.reset();
114 scoped_ptr
<InputMethodDescriptors
>
115 InputMethodManagerImpl::GetSupportedInputMethods() const {
116 scoped_ptr
<InputMethodDescriptors
> whitelist_imes
=
117 whitelist_
.GetSupportedInputMethods();
118 if (!extension_ime_util::UseWrappedExtensionKeyboardLayouts())
119 return whitelist_imes
.Pass();
120 return scoped_ptr
<InputMethodDescriptors
>(new InputMethodDescriptors
).Pass();
123 scoped_ptr
<InputMethodDescriptors
>
124 InputMethodManagerImpl::GetActiveInputMethods() const {
125 scoped_ptr
<InputMethodDescriptors
> result(new InputMethodDescriptors
);
126 // Build the active input method descriptors from the active input
127 // methods cache |active_input_method_ids_|.
128 for (size_t i
= 0; i
< active_input_method_ids_
.size(); ++i
) {
129 const std::string
& input_method_id
= active_input_method_ids_
[i
];
130 const InputMethodDescriptor
* descriptor
=
131 util_
.GetInputMethodDescriptorFromId(input_method_id
);
133 result
->push_back(*descriptor
);
135 std::map
<std::string
, InputMethodDescriptor
>::const_iterator ix
=
136 extra_input_methods_
.find(input_method_id
);
137 if (ix
!= extra_input_methods_
.end())
138 result
->push_back(ix
->second
);
140 DVLOG(1) << "Descriptor is not found for: " << input_method_id
;
143 if (result
->empty()) {
144 // Initially |active_input_method_ids_| is empty. browser_tests might take
147 InputMethodUtil::GetFallbackInputMethodDescriptor());
149 return result
.Pass();
152 const std::vector
<std::string
>&
153 InputMethodManagerImpl::GetActiveInputMethodIds() const {
154 return active_input_method_ids_
;
157 size_t InputMethodManagerImpl::GetNumActiveInputMethods() const {
158 return active_input_method_ids_
.size();
161 const InputMethodDescriptor
* InputMethodManagerImpl::GetInputMethodFromId(
162 const std::string
& input_method_id
) const {
163 const InputMethodDescriptor
* ime
= util_
.GetInputMethodDescriptorFromId(
166 std::map
<std::string
, InputMethodDescriptor
>::const_iterator ix
=
167 extra_input_methods_
.find(input_method_id
);
168 if (ix
!= extra_input_methods_
.end())
174 void InputMethodManagerImpl::EnableLoginLayouts(
175 const std::string
& language_code
,
176 const std::vector
<std::string
>& initial_layouts
) {
177 if (state_
== STATE_TERMINATING
)
180 // First, hardware keyboard layout should be shown.
181 std::vector
<std::string
> candidates
=
182 util_
.GetHardwareLoginInputMethodIds();
184 // Seocnd, locale based input method should be shown.
185 // Add input methods associated with the language.
186 std::vector
<std::string
> layouts_from_locale
;
187 util_
.GetInputMethodIdsFromLanguageCode(language_code
,
188 kKeyboardLayoutsOnly
,
189 &layouts_from_locale
);
190 candidates
.insert(candidates
.end(), layouts_from_locale
.begin(),
191 layouts_from_locale
.end());
193 std::vector
<std::string
> layouts
;
194 // First, add the initial input method ID, if it's requested, to
195 // layouts, so it appears first on the list of active input
196 // methods at the input language status menu.
197 for (size_t i
= 0; i
< initial_layouts
.size(); ++i
) {
198 if (util_
.IsValidInputMethodId(initial_layouts
[i
])) {
199 if (IsLoginKeyboard(initial_layouts
[i
])) {
200 layouts
.push_back(initial_layouts
[i
]);
203 << "EnableLoginLayouts: ignoring non-login initial keyboard layout:"
204 << initial_layouts
[i
];
206 } else if (!initial_layouts
[i
].empty()) {
207 DVLOG(1) << "EnableLoginLayouts: ignoring non-keyboard or invalid ID: "
208 << initial_layouts
[i
];
212 // Add candidates to layouts, while skipping duplicates.
213 for (size_t i
= 0; i
< candidates
.size(); ++i
) {
214 const std::string
& candidate
= candidates
[i
];
215 // Not efficient, but should be fine, as the two vectors are very
216 // short (2-5 items).
217 if (!Contains(layouts
, candidate
) && IsLoginKeyboard(candidate
))
218 layouts
.push_back(candidate
);
221 MigrateXkbInputMethods(&layouts
);
222 active_input_method_ids_
.swap(layouts
);
224 // Initialize candidate window controller and widgets such as
225 // candidate window, infolist and mode indicator. Note, mode
226 // indicator is used by only keyboard layout input methods.
227 if (active_input_method_ids_
.size() > 1)
228 MaybeInitializeCandidateWindowController();
230 // you can pass empty |initial_layout|.
231 ChangeInputMethod(initial_layouts
.empty() ? "" :
232 extension_ime_util::GetInputMethodIDByKeyboardLayout(
233 initial_layouts
[0]));
236 // Adds new input method to given list.
237 bool InputMethodManagerImpl::EnableInputMethodImpl(
238 const std::string
& input_method_id
,
239 std::vector
<std::string
>* new_active_input_method_ids
) const {
240 DCHECK(new_active_input_method_ids
);
241 if (!util_
.IsValidInputMethodId(input_method_id
)) {
242 DVLOG(1) << "EnableInputMethod: Invalid ID: " << input_method_id
;
246 if (!Contains(*new_active_input_method_ids
, input_method_id
))
247 new_active_input_method_ids
->push_back(input_method_id
);
252 // Starts or stops the system input method framework as needed.
253 void InputMethodManagerImpl::ReconfigureIMFramework() {
254 LoadNecessaryComponentExtensions();
256 // Initialize candidate window controller and widgets such as
257 // candidate window, infolist and mode indicator. Note, mode
258 // indicator is used by only keyboard layout input methods.
259 MaybeInitializeCandidateWindowController();
262 bool InputMethodManagerImpl::EnableInputMethod(
263 const std::string
& input_method_id
) {
264 if (!EnableInputMethodImpl(input_method_id
, &active_input_method_ids_
))
267 ReconfigureIMFramework();
271 bool InputMethodManagerImpl::ReplaceEnabledInputMethods(
272 const std::vector
<std::string
>& new_active_input_method_ids
) {
273 if (state_
== STATE_TERMINATING
)
276 // Filter unknown or obsolete IDs.
277 std::vector
<std::string
> new_active_input_method_ids_filtered
;
279 for (size_t i
= 0; i
< new_active_input_method_ids
.size(); ++i
)
280 EnableInputMethodImpl(new_active_input_method_ids
[i
],
281 &new_active_input_method_ids_filtered
);
283 if (new_active_input_method_ids_filtered
.empty()) {
284 DVLOG(1) << "ReplaceEnabledInputMethods: No valid input method ID";
288 // Copy extension IDs to |new_active_input_method_ids_filtered|. We have to
289 // keep relative order of the extension input method IDs.
290 for (size_t i
= 0; i
< active_input_method_ids_
.size(); ++i
) {
291 const std::string
& input_method_id
= active_input_method_ids_
[i
];
292 if (extension_ime_util::IsExtensionIME(input_method_id
))
293 new_active_input_method_ids_filtered
.push_back(input_method_id
);
295 active_input_method_ids_
.swap(new_active_input_method_ids_filtered
);
296 MigrateXkbInputMethods(&active_input_method_ids_
);
298 ReconfigureIMFramework();
300 // If |current_input_method| is no longer in |active_input_method_ids_|,
301 // ChangeInputMethod() picks the first one in |active_input_method_ids_|.
302 ChangeInputMethod(current_input_method_
.id());
306 void InputMethodManagerImpl::ChangeInputMethod(
307 const std::string
& input_method_id
) {
308 ChangeInputMethodInternal(input_method_id
, false);
311 bool InputMethodManagerImpl::ChangeInputMethodInternal(
312 const std::string
& input_method_id
,
314 if (state_
== STATE_TERMINATING
)
317 std::string input_method_id_to_switch
= input_method_id
;
320 if (!InputMethodIsActivated(input_method_id
)) {
321 scoped_ptr
<InputMethodDescriptors
> input_methods(GetActiveInputMethods());
322 DCHECK(!input_methods
->empty());
323 input_method_id_to_switch
= input_methods
->at(0).id();
324 if (!input_method_id
.empty()) {
325 DVLOG(1) << "Can't change the current input method to "
326 << input_method_id
<< " since the engine is not enabled. "
327 << "Switch to " << input_method_id_to_switch
<< " instead.";
331 if (!component_extension_ime_manager_
->IsInitialized() &&
332 (!InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch
) ||
333 extension_ime_util::IsKeyboardLayoutExtension(
334 input_method_id_to_switch
))) {
335 // We can't change input method before the initialization of
336 // component extension ime manager. ChangeInputMethod will be
337 // called with |pending_input_method_| when the initialization is
339 pending_input_method_
= input_method_id_to_switch
;
342 pending_input_method_
.clear();
344 // Hide candidate window and info list.
345 if (candidate_window_controller_
.get())
346 candidate_window_controller_
->Hide();
348 // Disable the current engine handler.
349 IMEEngineHandlerInterface
* engine
=
350 IMEBridge::Get()->GetCurrentEngineHandler();
354 // Configure the next engine handler.
355 if (InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch
) &&
356 !extension_ime_util::IsKeyboardLayoutExtension(
357 input_method_id_to_switch
)) {
358 IMEBridge::Get()->SetCurrentEngineHandler(NULL
);
360 IMEEngineHandlerInterface
* next_engine
=
361 profile_engine_map_
[GetProfile()][input_method_id_to_switch
];
363 IMEBridge::Get()->SetCurrentEngineHandler(next_engine
);
364 next_engine
->Enable();
368 // TODO(komatsu): Check if it is necessary to perform the above routine
369 // when the current input method is equal to |input_method_id_to_swich|.
370 if (current_input_method_
.id() != input_method_id_to_switch
) {
371 // Clear property list. Property list would be updated by
372 // extension IMEs via InputMethodEngine::(Set|Update)MenuItems.
373 // If the current input method is a keyboard layout, empty
374 // properties are sufficient.
375 const ash::ime::InputMethodMenuItemList empty_menu_item_list
;
376 ash::ime::InputMethodMenuManager
* input_method_menu_manager
=
377 ash::ime::InputMethodMenuManager::GetInstance();
378 input_method_menu_manager
->SetCurrentInputMethodMenuItemList(
379 empty_menu_item_list
);
381 const InputMethodDescriptor
* descriptor
= NULL
;
382 if (extension_ime_util::IsExtensionIME(input_method_id_to_switch
)) {
383 DCHECK(extra_input_methods_
.find(input_method_id_to_switch
) !=
384 extra_input_methods_
.end());
385 descriptor
= &(extra_input_methods_
[input_method_id_to_switch
]);
388 util_
.GetInputMethodDescriptorFromId(input_method_id_to_switch
);
392 previous_input_method_
= current_input_method_
;
393 current_input_method_
= *descriptor
;
396 // Change the keyboard layout to a preferred layout for the input method.
397 if (!keyboard_
->SetCurrentKeyboardLayoutByName(
398 current_input_method_
.GetPreferredKeyboardLayout())) {
399 LOG(ERROR
) << "Failed to change keyboard layout to "
400 << current_input_method_
.GetPreferredKeyboardLayout();
403 // Update input method indicators (e.g. "US", "DV") in Chrome windows.
404 FOR_EACH_OBSERVER(InputMethodManager::Observer
,
406 InputMethodChanged(this, show_message
));
410 void InputMethodManagerImpl::OnComponentExtensionInitialized(
411 scoped_ptr
<ComponentExtensionIMEManagerDelegate
> delegate
) {
412 DCHECK(thread_checker_
.CalledOnValidThread());
413 component_extension_ime_manager_
->Initialize(delegate
.Pass());
414 util_
.SetComponentExtensions(
415 component_extension_ime_manager_
->GetAllIMEAsInputMethodDescriptor());
417 LoadNecessaryComponentExtensions();
419 if (!pending_input_method_
.empty())
420 ChangeInputMethodInternal(pending_input_method_
, false);
423 void InputMethodManagerImpl::LoadNecessaryComponentExtensions() {
424 if (!component_extension_ime_manager_
->IsInitialized())
426 // Load component extensions but also update |active_input_method_ids_| as
427 // some component extension IMEs may have been removed from the Chrome OS
428 // image. If specified component extension IME no longer exists, falling back
429 // to an existing IME.
430 std::vector
<std::string
> unfiltered_input_method_ids
;
431 unfiltered_input_method_ids
.swap(active_input_method_ids_
);
432 for (size_t i
= 0; i
< unfiltered_input_method_ids
.size(); ++i
) {
433 if (!extension_ime_util::IsComponentExtensionIME(
434 unfiltered_input_method_ids
[i
])) {
435 // Legacy IMEs or xkb layouts are alwayes active.
436 active_input_method_ids_
.push_back(unfiltered_input_method_ids
[i
]);
437 } else if (component_extension_ime_manager_
->IsWhitelisted(
438 unfiltered_input_method_ids
[i
])) {
439 component_extension_ime_manager_
->LoadComponentExtensionIME(
440 unfiltered_input_method_ids
[i
]);
441 active_input_method_ids_
.push_back(unfiltered_input_method_ids
[i
]);
444 // TODO(shuchen): move this call in ComponentExtensionIMEManager.
445 component_extension_ime_manager_
->NotifyInitialized();
448 void InputMethodManagerImpl::ActivateInputMethodMenuItem(
449 const std::string
& key
) {
450 DCHECK(!key
.empty());
452 if (ash::ime::InputMethodMenuManager::GetInstance()->
453 HasInputMethodMenuItemForKey(key
)) {
454 IMEEngineHandlerInterface
* engine
=
455 IMEBridge::Get()->GetCurrentEngineHandler();
457 engine
->PropertyActivate(key
);
461 DVLOG(1) << "ActivateInputMethodMenuItem: unknown key: " << key
;
464 void InputMethodManagerImpl::AddInputMethodExtension(
465 const std::string
& id
,
466 InputMethodEngineInterface
* engine
) {
467 if (state_
== STATE_TERMINATING
)
470 if (!extension_ime_util::IsExtensionIME(id
) &&
471 !extension_ime_util::IsComponentExtensionIME(id
)) {
472 DVLOG(1) << id
<< " is not a valid extension input method ID.";
478 const InputMethodDescriptor
& descriptor
= engine
->GetDescriptor();
479 extra_input_methods_
[id
] = descriptor
;
480 if (Contains(enabled_extension_imes_
, id
) &&
481 !extension_ime_util::IsComponentExtensionIME(id
)) {
482 if (!Contains(active_input_method_ids_
, id
)) {
483 active_input_method_ids_
.push_back(id
);
485 DVLOG(1) << "AddInputMethodExtension: alread added: "
486 << id
<< ", " << descriptor
.name();
487 // Call Start() anyway, just in case.
490 // Ensure that the input method daemon is running.
491 MaybeInitializeCandidateWindowController();
494 profile_engine_map_
[GetProfile()][id
] = engine
;
497 void InputMethodManagerImpl::RemoveInputMethodExtension(const std::string
& id
) {
498 if (!extension_ime_util::IsExtensionIME(id
))
499 DVLOG(1) << id
<< " is not a valid extension input method ID.";
501 std::vector
<std::string
>::iterator i
= std::find(
502 active_input_method_ids_
.begin(), active_input_method_ids_
.end(), id
);
503 if (i
!= active_input_method_ids_
.end())
504 active_input_method_ids_
.erase(i
);
505 extra_input_methods_
.erase(id
);
507 // If |current_input_method| is no longer in |active_input_method_ids_|,
508 // switch to the first one in |active_input_method_ids_|.
509 ChangeInputMethod(current_input_method_
.id());
511 if (IMEBridge::Get()->GetCurrentEngineHandler() ==
512 profile_engine_map_
[GetProfile()][id
])
513 IMEBridge::Get()->SetCurrentEngineHandler(NULL
);
516 void InputMethodManagerImpl::GetInputMethodExtensions(
517 InputMethodDescriptors
* result
) {
518 // Build the extension input method descriptors from the extra input
519 // methods cache |extra_input_methods_|.
520 std::map
<std::string
, InputMethodDescriptor
>::iterator iter
;
521 for (iter
= extra_input_methods_
.begin(); iter
!= extra_input_methods_
.end();
523 if (extension_ime_util::IsExtensionIME(iter
->first
))
524 result
->push_back(iter
->second
);
528 void InputMethodManagerImpl::SetEnabledExtensionImes(
529 std::vector
<std::string
>* ids
) {
530 enabled_extension_imes_
.clear();
531 enabled_extension_imes_
.insert(enabled_extension_imes_
.end(),
535 bool active_imes_changed
= false;
537 for (std::map
<std::string
, InputMethodDescriptor
>::iterator extra_iter
=
538 extra_input_methods_
.begin(); extra_iter
!= extra_input_methods_
.end();
540 if (extension_ime_util::IsComponentExtensionIME(
542 continue; // Do not filter component extension.
543 std::vector
<std::string
>::iterator active_iter
= std::find(
544 active_input_method_ids_
.begin(), active_input_method_ids_
.end(),
547 bool active
= active_iter
!= active_input_method_ids_
.end();
548 bool enabled
= Contains(enabled_extension_imes_
, extra_iter
->first
);
550 if (active
&& !enabled
)
551 active_input_method_ids_
.erase(active_iter
);
553 if (!active
&& enabled
)
554 active_input_method_ids_
.push_back(extra_iter
->first
);
556 if (active
== !enabled
)
557 active_imes_changed
= true;
560 if (active_imes_changed
) {
561 MaybeInitializeCandidateWindowController();
563 // If |current_input_method| is no longer in |active_input_method_ids_|,
564 // switch to the first one in |active_input_method_ids_|.
565 ChangeInputMethod(current_input_method_
.id());
569 void InputMethodManagerImpl::SetInputMethodLoginDefault() {
570 // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
571 // and US dvorak keyboard layouts.
572 if (g_browser_process
&& g_browser_process
->local_state()) {
573 const std::string locale
= g_browser_process
->GetApplicationLocale();
574 // If the preferred keyboard for the login screen has been saved, use it.
575 PrefService
* prefs
= g_browser_process
->local_state();
576 std::string initial_input_method_id
=
577 prefs
->GetString(chromeos::language_prefs::kPreferredKeyboardLayout
);
578 std::vector
<std::string
> input_methods_to_be_enabled
;
579 if (initial_input_method_id
.empty()) {
580 // If kPreferredKeyboardLayout is not specified, use the hardware layout.
581 input_methods_to_be_enabled
= util_
.GetHardwareLoginInputMethodIds();
583 input_methods_to_be_enabled
.push_back(initial_input_method_id
);
585 EnableLoginLayouts(locale
, input_methods_to_be_enabled
);
589 bool InputMethodManagerImpl::SwitchToNextInputMethod() {
591 if (active_input_method_ids_
.empty()) {
592 DVLOG(1) << "active input method is empty";
596 if (current_input_method_
.id().empty()) {
597 DVLOG(1) << "current_input_method_ is unknown";
601 // Do not consume key event if there is only one input method is enabled.
602 // Ctrl+Space or Alt+Shift may be used by other application.
603 if (active_input_method_ids_
.size() == 1)
606 // Find the next input method and switch to it.
607 SwitchToNextInputMethodInternal(active_input_method_ids_
,
608 current_input_method_
.id());
612 bool InputMethodManagerImpl::SwitchToPreviousInputMethod(
613 const ui::Accelerator
& accelerator
) {
615 if (active_input_method_ids_
.empty()) {
616 DVLOG(1) << "active input method is empty";
620 // Do not consume key event if there is only one input method is enabled.
621 // Ctrl+Space or Alt+Shift may be used by other application.
622 if (active_input_method_ids_
.size() == 1)
625 if (accelerator
.type() == ui::ET_KEY_RELEASED
)
628 if (previous_input_method_
.id().empty() ||
629 previous_input_method_
.id() == current_input_method_
.id()) {
630 return SwitchToNextInputMethod();
633 std::vector
<std::string
>::const_iterator iter
=
634 std::find(active_input_method_ids_
.begin(),
635 active_input_method_ids_
.end(),
636 previous_input_method_
.id());
637 if (iter
== active_input_method_ids_
.end()) {
638 // previous_input_method_ is not supported.
639 return SwitchToNextInputMethod();
641 ChangeInputMethodInternal(*iter
, true);
645 bool InputMethodManagerImpl::SwitchInputMethod(
646 const ui::Accelerator
& accelerator
) {
648 if (active_input_method_ids_
.empty()) {
649 DVLOG(1) << "active input method is empty";
653 // Get the list of input method ids for the |accelerator|. For example, get
654 // { "mozc-hangul", "xkb:kr:kr104:kor" } for ui::VKEY_DBE_SBCSCHAR.
655 std::vector
<std::string
> input_method_ids_to_switch
;
656 switch (accelerator
.key_code()) {
657 case ui::VKEY_CONVERT
: // Henkan key on JP106 keyboard
658 input_method_ids_to_switch
.push_back(nacl_mozc_jp_id
);
660 case ui::VKEY_NONCONVERT
: // Muhenkan key on JP106 keyboard
661 input_method_ids_to_switch
.push_back("xkb:jp::jpn");
663 case ui::VKEY_DBE_SBCSCHAR
: // ZenkakuHankaku key on JP106 keyboard
664 case ui::VKEY_DBE_DBCSCHAR
:
665 input_method_ids_to_switch
.push_back(nacl_mozc_jp_id
);
666 input_method_ids_to_switch
.push_back("xkb:jp::jpn");
672 if (input_method_ids_to_switch
.empty()) {
673 DVLOG(1) << "Unexpected VKEY: " << accelerator
.key_code();
677 MigrateXkbInputMethods(&input_method_ids_to_switch
);
679 // Obtain the intersection of input_method_ids_to_switch and
680 // active_input_method_ids_. The order of IDs in active_input_method_ids_ is
682 std::vector
<std::string
> ids
;
683 for (size_t i
= 0; i
< input_method_ids_to_switch
.size(); ++i
) {
684 const std::string
& id
= input_method_ids_to_switch
[i
];
685 if (Contains(active_input_method_ids_
, id
))
689 // No input method for the accelerator is active. For example, we should
690 // just ignore VKEY_HANGUL when mozc-hangul is not active.
694 SwitchToNextInputMethodInternal(ids
, current_input_method_
.id());
695 return true; // consume the accelerator.
698 void InputMethodManagerImpl::SwitchToNextInputMethodInternal(
699 const std::vector
<std::string
>& input_method_ids
,
700 const std::string
& current_input_method_id
) {
701 std::vector
<std::string
>::const_iterator iter
=
702 std::find(input_method_ids
.begin(),
703 input_method_ids
.end(),
704 current_input_method_id
);
705 if (iter
!= input_method_ids
.end())
707 if (iter
== input_method_ids
.end())
708 iter
= input_method_ids
.begin();
709 ChangeInputMethodInternal(*iter
, true);
712 InputMethodDescriptor
InputMethodManagerImpl::GetCurrentInputMethod() const {
713 if (current_input_method_
.id().empty())
714 return InputMethodUtil::GetFallbackInputMethodDescriptor();
716 return current_input_method_
;
719 bool InputMethodManagerImpl::IsISOLevel5ShiftUsedByCurrentInputMethod() const {
720 return keyboard_
->IsISOLevel5ShiftAvailable();
723 bool InputMethodManagerImpl::IsAltGrUsedByCurrentInputMethod() const {
724 return keyboard_
->IsAltGrAvailable();
727 ImeKeyboard
* InputMethodManagerImpl::GetImeKeyboard() {
728 return keyboard_
.get();
731 InputMethodUtil
* InputMethodManagerImpl::GetInputMethodUtil() {
735 ComponentExtensionIMEManager
*
736 InputMethodManagerImpl::GetComponentExtensionIMEManager() {
737 DCHECK(thread_checker_
.CalledOnValidThread());
738 return component_extension_ime_manager_
.get();
741 void InputMethodManagerImpl::InitializeComponentExtension() {
742 ComponentExtensionIMEManagerImpl
* impl
=
743 new ComponentExtensionIMEManagerImpl();
744 scoped_ptr
<ComponentExtensionIMEManagerDelegate
> delegate(impl
);
745 impl
->InitializeAsync(base::Bind(
746 &InputMethodManagerImpl::OnComponentExtensionInitialized
,
747 weak_ptr_factory_
.GetWeakPtr(),
748 base::Passed(&delegate
)));
751 void InputMethodManagerImpl::Init(base::SequencedTaskRunner
* ui_task_runner
) {
752 DCHECK(thread_checker_
.CalledOnValidThread());
754 if (base::SysInfo::IsRunningOnChromeOS())
755 keyboard_
.reset(ImeKeyboard::Create());
757 keyboard_
.reset(new FakeImeKeyboard());
759 // We can't call impl->Initialize here, because file thread is not available
761 ui_task_runner
->PostTask(
763 base::Bind(&InputMethodManagerImpl::InitializeComponentExtension
,
764 weak_ptr_factory_
.GetWeakPtr()));
767 void InputMethodManagerImpl::SetCandidateWindowControllerForTesting(
768 CandidateWindowController
* candidate_window_controller
) {
769 candidate_window_controller_
.reset(candidate_window_controller
);
770 candidate_window_controller_
->AddObserver(this);
773 void InputMethodManagerImpl::SetImeKeyboardForTesting(ImeKeyboard
* keyboard
) {
774 keyboard_
.reset(keyboard
);
777 void InputMethodManagerImpl::InitializeComponentExtensionForTesting(
778 scoped_ptr
<ComponentExtensionIMEManagerDelegate
> delegate
) {
779 OnComponentExtensionInitialized(delegate
.Pass());
782 void InputMethodManagerImpl::CandidateClicked(int index
) {
783 IMEEngineHandlerInterface
* engine
=
784 IMEBridge::Get()->GetCurrentEngineHandler();
786 engine
->CandidateClicked(index
);
789 void InputMethodManagerImpl::CandidateWindowOpened() {
790 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver
,
791 candidate_window_observers_
,
792 CandidateWindowOpened(this));
795 void InputMethodManagerImpl::CandidateWindowClosed() {
796 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver
,
797 candidate_window_observers_
,
798 CandidateWindowClosed(this));
801 void InputMethodManagerImpl::OnScreenLocked() {
802 saved_previous_input_method_
= previous_input_method_
;
803 saved_current_input_method_
= current_input_method_
;
804 saved_active_input_method_ids_
= active_input_method_ids_
;
806 std::set
<std::string
> added_ids_
;
808 const std::vector
<std::string
>& hardware_keyboard_ids
=
809 util_
.GetHardwareLoginInputMethodIds();
811 active_input_method_ids_
.clear();
812 for (size_t i
= 0; i
< saved_active_input_method_ids_
.size(); ++i
) {
813 const std::string
& input_method_id
= saved_active_input_method_ids_
[i
];
814 // Skip if it's not a keyboard layout. Drop input methods including
816 if (!IsLoginKeyboard(input_method_id
) ||
817 added_ids_
.find(input_method_id
) != added_ids_
.end())
819 active_input_method_ids_
.push_back(input_method_id
);
820 added_ids_
.insert(input_method_id
);
823 // We'll add the hardware keyboard if it's not included in
824 // |active_input_method_ids_| so that the user can always use the hardware
825 // keyboard on the screen locker.
826 for (size_t i
= 0; i
< hardware_keyboard_ids
.size(); ++i
) {
827 if (added_ids_
.find(hardware_keyboard_ids
[i
]) == added_ids_
.end()) {
828 active_input_method_ids_
.push_back(hardware_keyboard_ids
[i
]);
829 added_ids_
.insert(hardware_keyboard_ids
[i
]);
833 ChangeInputMethod(current_input_method_
.id());
836 void InputMethodManagerImpl::OnScreenUnlocked() {
837 previous_input_method_
= saved_previous_input_method_
;
838 current_input_method_
= saved_current_input_method_
;
839 active_input_method_ids_
= saved_active_input_method_ids_
;
841 ChangeInputMethod(current_input_method_
.id());
844 bool InputMethodManagerImpl::InputMethodIsActivated(
845 const std::string
& input_method_id
) {
846 return Contains(active_input_method_ids_
, input_method_id
);
849 void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() {
850 if (candidate_window_controller_
.get())
853 candidate_window_controller_
.reset(
854 CandidateWindowController::CreateCandidateWindowController());
855 candidate_window_controller_
->AddObserver(this);
858 Profile
* InputMethodManagerImpl::GetProfile() const {
859 return ProfileManager::GetActiveUserProfile();
862 } // namespace input_method
863 } // namespace chromeos