Adds Irish keyboard to linux_chromeos, and fixes some other xkb names.
[chromium-blink-merge.git] / chrome / browser / chromeos / input_method / input_method_manager_impl.cc
blobd5a658911582c0e6a4747e4ce3281f60a6be991d
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"
33 namespace chromeos {
34 namespace input_method {
36 namespace {
38 bool Contains(const std::vector<std::string>& container,
39 const std::string& value) {
40 return std::find(container.begin(), container.end(), value) !=
41 container.end();
44 } // namespace
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_;
92 state_ = new_state;
93 switch (state_) {
94 case STATE_LOGIN_SCREEN:
95 break;
96 case STATE_BROWSER_SCREEN:
97 if (old_state == STATE_LOCK_SCREEN)
98 OnScreenUnlocked();
99 break;
100 case STATE_LOCK_SCREEN:
101 OnScreenLocked();
102 break;
103 case STATE_TERMINATING: {
104 if (candidate_window_controller_.get())
105 candidate_window_controller_.reset();
106 break;
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);
127 if (descriptor) {
128 result->push_back(*descriptor);
129 } else {
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);
134 else
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
140 // this path.
141 result->push_back(
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(
159 input_method_id);
160 if (!ime) {
161 std::map<std::string, InputMethodDescriptor>::const_iterator ix =
162 extra_input_methods_.find(input_method_id);
163 if (ix != extra_input_methods_.end())
164 ime = &ix->second;
166 return ime;
169 void InputMethodManagerImpl::EnableLoginLayouts(
170 const std::string& language_code,
171 const std::vector<std::string>& initial_layouts) {
172 if (state_ == STATE_TERMINATING)
173 return;
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]);
196 } else {
197 DVLOG(1)
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;
237 return false;
240 if (!Contains(*new_active_input_method_ids, input_method_id))
241 new_active_input_method_ids->push_back(input_method_id);
243 return true;
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_))
259 return false;
261 ReconfigureIMFramework();
262 return true;
265 bool InputMethodManagerImpl::ReplaceEnabledInputMethods(
266 const std::vector<std::string>& new_active_input_method_ids) {
267 if (state_ == STATE_TERMINATING)
268 return false;
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";
279 return false;
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());
297 return true;
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,
307 bool show_message) {
308 if (state_ == STATE_TERMINATING)
309 return false;
311 std::string input_method_id_to_switch = input_method_id;
313 // Sanity check.
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
330 // done.
331 pending_input_method_ = input_method_id_to_switch;
332 return false;
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();
343 if (engine)
344 engine->Disable();
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);
351 } else {
352 IMEEngineHandlerInterface* next_engine =
353 profile_engine_map_[GetProfile()][input_method_id_to_switch];
354 if (next_engine) {
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]);
378 } else {
379 descriptor =
380 util_.GetInputMethodDescriptorFromId(input_method_id_to_switch);
381 if (!descriptor)
382 LOG(ERROR) << "Unknown input method id: " << input_method_id_to_switch;
384 DCHECK(descriptor);
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,
399 observers_,
400 InputMethodChanged(this, show_message));
401 return true;
404 bool InputMethodManagerImpl::IsXkbComponentExtensionAvailable() const {
405 if (!component_extension_ime_manager_->IsInitialized())
406 return false;
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))
412 return true;
414 return false;
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);
427 else
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())
438 return;
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();
469 if (engine)
470 engine->PropertyActivate(key);
471 return;
474 DVLOG(1) << "ActivateInputMethodMenuItem: unknown key: " << key;
477 void InputMethodManagerImpl::AddInputMethodExtension(
478 const std::string& id,
479 InputMethodEngineInterface* engine) {
480 if (state_ == STATE_TERMINATING)
481 return;
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.";
486 return;
489 DCHECK(engine);
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);
497 } else {
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();
535 ++iter) {
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(),
545 ids->begin(),
546 ids->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();
552 ++extra_iter) {
553 if (extension_ime_util::IsComponentExtensionIME(
554 extra_iter->first))
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(),
558 extra_iter->first);
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();
595 } else {
596 input_methods_to_be_enabled.push_back(initial_input_method_id);
598 EnableLoginLayouts(locale, input_methods_to_be_enabled);
602 bool InputMethodManagerImpl::SwitchToNextInputMethod() {
603 // Sanity checks.
604 if (active_input_method_ids_.empty()) {
605 DVLOG(1) << "active input method is empty";
606 return false;
609 if (current_input_method_.id().empty()) {
610 DVLOG(1) << "current_input_method_ is unknown";
611 return false;
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)
617 return false;
619 // Find the next input method and switch to it.
620 SwitchToNextInputMethodInternal(active_input_method_ids_,
621 current_input_method_.id());
622 return true;
625 bool InputMethodManagerImpl::SwitchToPreviousInputMethod(
626 const ui::Accelerator& accelerator) {
627 // Sanity check.
628 if (active_input_method_ids_.empty()) {
629 DVLOG(1) << "active input method is empty";
630 return false;
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)
636 return false;
638 if (accelerator.type() == ui::ET_KEY_RELEASED)
639 return true;
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);
655 return true;
658 bool InputMethodManagerImpl::SwitchInputMethod(
659 const ui::Accelerator& accelerator) {
660 // Sanity check.
661 if (active_input_method_ids_.empty()) {
662 DVLOG(1) << "active input method is empty";
663 return false;
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"));
673 break;
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"));
677 break;
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"));
684 break;
685 default:
686 NOTREACHED();
687 break;
689 if (input_method_ids_to_switch.empty()) {
690 DVLOG(1) << "Unexpected VKEY: " << accelerator.key_code();
691 return false;
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
696 // preserved.
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))
701 ids.push_back(id);
703 if (ids.empty()) {
704 // No input method for the accelerator is active. For example, we should
705 // just ignore VKEY_HANGUL when mozc-hangul is not active.
706 return false;
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())
721 ++iter;
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() {
747 return &util_;
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());
771 else
772 keyboard_.reset(new FakeImeKeyboard());
774 // We can't call impl->Initialize here, because file thread is not available
775 // at this moment.
776 ui_task_runner->PostTask(
777 FROM_HERE,
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();
800 if (engine)
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
830 // extension ones.
831 if (!IsLoginKeyboard(input_method_id) ||
832 added_ids_.find(input_method_id) != added_ids_.end())
833 continue;
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())
866 return;
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