Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(286)

Unified Diff: chrome/browser/chromeos/input_method/input_method_manager_impl.cc

Issue 9999018: chrome/browser/chromeos/input_method/ refactoring [part 6 of 6] (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/chromeos/input_method/input_method_manager_impl.cc
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
new file mode 100644
index 0000000000000000000000000000000000000000..34a5410134b7ec3612867f082892d53dffc32f18
--- /dev/null
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
@@ -0,0 +1,613 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/input_method/input_method_manager_impl.h"
+
+#include <algorithm> // std::find
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "chrome/browser/chromeos/input_method/browser_state_monitor.h"
+#include "chrome/browser/chromeos/input_method/candidate_window.h"
+#include "chrome/browser/chromeos/input_method/input_method_util.h"
+#include "chrome/browser/chromeos/input_method/xkeyboard.h"
+#include "chrome/browser/chromeos/language_preferences.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "unicode/uloc.h"
+
+namespace chromeos {
+namespace input_method {
+
+namespace {
+
+template<typename T>
+typename T::iterator Find(T& container, const typename T::value_type& value) {
+ return std::find(container.begin(), container.end(), value);
+}
+
+template<typename T>
+typename T::const_iterator Find(const T& container,
+ const typename T::value_type& value) {
+ return std::find(container.begin(), container.end(), value);
+}
+
+template<typename T>
+bool Contains(const T& container, const typename T::value_type& value) {
+ return Find(container, value) != container.end();
+}
+
+} // namespace
+
+InputMethodManagerImpl::InputMethodManagerImpl()
+ : should_hide_properties_(true),
+ ignore_hotkeys_(false),
+ state_(STATE_LOGIN_SCREEN),
+ util_(GetSupportedInputMethods()) {
+}
+
+InputMethodManagerImpl::~InputMethodManagerImpl() {
+ if (ibus_controller_.get())
+ ibus_controller_->RemoveObserver(this);
+ if (candidate_window_controller_.get())
+ candidate_window_controller_->RemoveObserver(this);
+}
+
+void InputMethodManagerImpl::AddObserver(
+ InputMethodManager::Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void InputMethodManagerImpl::AddCandidateWindowObserver(
+ InputMethodManager::CandidateWindowObserver* observer) {
+ candidate_window_observers_.AddObserver(observer);
+}
+
+void InputMethodManagerImpl::RemoveObserver(
+ InputMethodManager::Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void InputMethodManagerImpl::RemoveCandidateWindowObserver(
+ InputMethodManager::CandidateWindowObserver* observer) {
+ candidate_window_observers_.RemoveObserver(observer);
+}
+
+void InputMethodManagerImpl::SetState(State new_state) {
+ const State old_state = state_;
+ state_ = new_state;
+ switch (state_) {
+ case STATE_LOGIN_SCREEN:
+ break;
+ case STATE_BROWSER_SCREEN:
+ if (old_state == STATE_LOCK_SCREEN)
+ OnScreenUnlocked();
+ break;
+ case STATE_LOCK_SCREEN:
+ OnScreenLocked();
+ break;
+ case STATE_TERMINATING:
+ ibus_controller_->Stop();
+ browser_state_monitor_.reset(); // For crbug.com/120183.
+ candidate_window_controller_.reset();
+ break;
+ }
+}
+
+InputMethodDescriptors* InputMethodManagerImpl::GetSupportedInputMethods() {
+ return whitelist_.GetSupportedInputMethods();
+}
+
+InputMethodDescriptors* InputMethodManagerImpl::GetActiveInputMethods() {
+ InputMethodDescriptors* result = new InputMethodDescriptors;
+ // Build the active input method descriptors from the active input
+ // methods cache |active_input_method_ids_|.
+ for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
+ const std::string& input_method_id = active_input_method_ids_[i];
+ const InputMethodDescriptor* descriptor =
+ util_.GetInputMethodDescriptorFromId(input_method_id);
+ if (descriptor) {
+ result->push_back(*descriptor);
+ } else {
+ std::map<std::string, InputMethodDescriptor>::const_iterator ix =
+ extra_input_methods_.find(input_method_id);
+ if (ix != extra_input_methods_.end())
+ result->push_back(ix->second);
+ else
+ LOG(ERROR) << "Descriptor is not found for: " << input_method_id;
+ }
+ }
+ if (result->empty()) {
+ // Initially |active_input_method_ids_| is empty. browser_tests might take
+ // this path.
+ result->push_back(
+ InputMethodDescriptor::GetFallbackInputMethodDescriptor());
+ }
+ return result;
+}
+
+size_t InputMethodManagerImpl::GetNumActiveInputMethods() const {
+ return active_input_method_ids_.size();
+}
+
+void InputMethodManagerImpl::EnableLayouts(const std::string& language_code,
+ const std::string& initial_layout) {
+ if (state_ == STATE_TERMINATING)
+ return;
+
+ std::vector<std::string> candidates;
+ // Add input methods associated with the language.
+ util_.GetInputMethodIdsFromLanguageCode(language_code,
+ kKeyboardLayoutsOnly,
+ &candidates);
+ // Add the hardware keyboard as well. We should always add this so users
+ // can use the hardware keyboard on the login screen and the screen locker.
+ candidates.push_back(util_.GetHardwareInputMethodId());
+
+ std::vector<std::string> layouts;
+ // First, add the initial input method ID, if it's requested, to
+ // layouts, so it appears first on the list of active input
+ // methods at the input language status menu.
+ if (util_.IsValidInputMethodId(initial_layout) &&
+ InputMethodUtil::IsKeyboardLayout(initial_layout)) {
+ layouts.push_back(initial_layout);
+ } else if (!initial_layout.empty()) {
+ LOG(ERROR) << "EnableLayouts: ignoring non-keyboard or invalid ID: "
+ << initial_layout;
+ }
+
+ // Add candidates to layouts, while skipping duplicates.
+ for (size_t i = 0; i < candidates.size(); ++i) {
+ const std::string& candidate = candidates[i];
+ // Not efficient, but should be fine, as the two vectors are very
+ // short (2-5 items).
+ if (!Contains(layouts, candidate))
+ layouts.push_back(candidate);
+ }
+
+ active_input_method_ids_.swap(layouts);
+ ChangeInputMethod(initial_layout); // you can pass empty |initial_layout|.
+}
+
+bool InputMethodManagerImpl::EnableInputMethods(
+ const std::vector<std::string>& new_active_input_method_ids) {
+ if (state_ == STATE_TERMINATING)
+ return false;
+
+ // Filter unknown or obsolete IDs.
+ std::vector<std::string> new_active_input_method_ids_filtered;
+
+ for (size_t i = 0; i < new_active_input_method_ids.size(); ++i) {
+ const std::string& input_method_id = new_active_input_method_ids[i];
+ if (util_.IsValidInputMethodId(input_method_id))
+ new_active_input_method_ids_filtered.push_back(input_method_id);
+ else
+ LOG(ERROR) << "EnableInputMethods: Invalid ID: " << input_method_id;
+ }
+
+ if (new_active_input_method_ids_filtered.empty()) {
+ LOG(ERROR) << "EnableInputMethods: No valid input method ID";
+ return false;
+ }
+
+ // Copy extension IDs to |new_active_input_method_ids_filtered|. We have to
+ // keep reletive order of the extension input method IDs.
+ for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
+ const std::string& input_method_id = active_input_method_ids_[i];
+ if (InputMethodUtil::IsExtensionInputMethod(input_method_id))
+ new_active_input_method_ids_filtered.push_back(input_method_id);
+ }
+ active_input_method_ids_.swap(new_active_input_method_ids_filtered);
+
+ if (ContainOnlyKeyboardLayout(active_input_method_ids_)) {
+ // Do NOT call ibus_controller_->Stop(); here to work around a crash issue
+ // at crosbug.com/27051.
+ // TODO(yusukes): We can safely call Stop(); here once crosbug.com/26443
+ // is implemented.
+ } else {
+ MaybeInitializeCandidateWindowController();
+ ibus_controller_->Start(active_input_method_ids_);
+ }
+
+ // If |current_input_method| is no longer in |active_input_method_ids_|,
+ // ChangeInputMethod() picks the first one in |active_input_method_ids_|.
+ ChangeInputMethod(current_input_method_.id());
+ return true;
+}
+
+bool InputMethodManagerImpl::SetInputMethodConfig(
+ const std::string& section,
+ const std::string& config_name,
+ const InputMethodConfigValue& value) {
+ DCHECK(section != language_prefs::kGeneralSectionName ||
+ config_name != language_prefs::kPreloadEnginesConfigName);
+
+ if (state_ == STATE_TERMINATING)
+ return false;
+ return ibus_controller_->SetInputMethodConfig(section, config_name, value);
+}
+
+void InputMethodManagerImpl::ChangeInputMethod(
+ const std::string& input_method_id) {
+ if (state_ == STATE_TERMINATING)
+ return;
+
+ std::string input_method_id_to_switch = input_method_id;
+
+ // Sanity check.
+ if (!InputMethodIsActivated(input_method_id)) {
+ scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods());
+ DCHECK(!input_methods->empty());
+ input_method_id_to_switch = input_methods->at(0).id();
+ if (!input_method_id.empty()) {
+ VLOG(1) << "Can't change the current input method to "
+ << input_method_id << " since the engine is not enabled. "
+ << "Switch to " << input_method_id_to_switch << " instead.";
+ }
+ }
+
+ if (InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch)) {
+ should_hide_properties_ = true;
+ FOR_EACH_OBSERVER(InputMethodManager::Observer,
+ observers_,
+ InputMethodPropertyChanged(this));
+ } else {
+ ibus_controller_->ChangeInputMethod(input_method_id_to_switch);
+ }
+
+ if (current_input_method_.id() != input_method_id_to_switch) {
+ const InputMethodDescriptor* descriptor = NULL;
+ if (!InputMethodUtil::IsExtensionInputMethod(input_method_id_to_switch)) {
+ descriptor =
+ util_.GetInputMethodDescriptorFromId(input_method_id_to_switch);
+ } else {
+ std::map<std::string, InputMethodDescriptor>::const_iterator i =
+ extra_input_methods_.find(input_method_id_to_switch);
+ DCHECK(i != extra_input_methods_.end());
+ descriptor = &(i->second);
+ }
+ DCHECK(descriptor);
+
+ previous_input_method_ = current_input_method_;
+ current_input_method_ = *descriptor;
+
+ // Change the keyboard layout to a preferred layout for the input method.
+ if (!xkeyboard_->SetCurrentKeyboardLayoutByName(
+ current_input_method_.keyboard_layout())) {
+ LOG(ERROR) << "Failed to change keyboard layout to "
+ << current_input_method_.keyboard_layout();
+ }
+ }
+
+ // Update input method indicators (e.g. "US", "DV") in Chrome windows.
+ FOR_EACH_OBSERVER(InputMethodManager::Observer,
+ observers_,
+ InputMethodChanged(this));
+}
+
+void InputMethodManagerImpl::ActivateInputMethodProperty(
+ const std::string& key) {
+ DCHECK(!key.empty());
+ ibus_controller_->ActivateInputMethodProperty(key);
+}
+
+void InputMethodManagerImpl::AddInputMethodExtension(
+ const std::string& id,
+ const std::string& name,
+ const std::vector<std::string>& layouts,
+ const std::string& language) {
+ if (state_ == STATE_TERMINATING)
+ return;
+
+ if (!InputMethodUtil::IsExtensionInputMethod(id)) {
+ LOG(ERROR) << id << " is not a valid extension input method ID.";
+ return;
+ }
+
+ const std::string virtual_layouts = JoinString(layouts, ',');
+ extra_input_methods_[id] = InputMethodDescriptor(
+ whitelist_, id, name, virtual_layouts, language);
+
+ if (!Contains(active_input_method_ids_, id)) {
+ active_input_method_ids_.push_back(id);
+ } else {
+ LOG(ERROR) << "AddInputMethodExtension: alread added: "
+ << id << ", " << name;
+ // Call Start() anyway, just in case.
+ }
+
+ // Ensure that the input method daemon is running.
+ MaybeInitializeCandidateWindowController();
+ ibus_controller_->Start(active_input_method_ids_);
+}
+
+void InputMethodManagerImpl::RemoveInputMethodExtension(const std::string& id) {
+ if (!InputMethodUtil::IsExtensionInputMethod(id))
+ LOG(ERROR) << id << " is not a valid extension input method ID.";
+
+ std::vector<std::string>::iterator i = Find(active_input_method_ids_, id);
+ if (i != active_input_method_ids_.end())
+ active_input_method_ids_.erase(i);
+ extra_input_methods_.erase(id);
+
+ if (ContainOnlyKeyboardLayout(active_input_method_ids_)) {
+ // Do NOT call ibus_controller_->Stop(); here to work around a crash issue
+ // at crosbug.com/27051.
+ // TODO(yusukes): We can safely call Stop(); here once crosbug.com/26443
+ // is implemented.
+ }
+
+ // If |current_input_method| is no longer in |active_input_method_ids_|,
+ // switch to the first one in |active_input_method_ids_|.
+ ChangeInputMethod(current_input_method_.id());
+}
+
+void InputMethodManagerImpl::EnableHotkeys() {
+ ignore_hotkeys_ = false;
+}
+
+void InputMethodManagerImpl::DisableHotkeys() {
+ ignore_hotkeys_ = true;
+}
+
+bool InputMethodManagerImpl::SwitchToNextInputMethod() {
+ if (ignore_hotkeys_)
+ return false;
+
+ // Sanity checks.
+ if (active_input_method_ids_.empty()) {
+ LOG(ERROR) << "active input method is empty";
+ return false;
+ }
+ if (current_input_method_.id().empty()) {
+ LOG(ERROR) << "current_input_method_ is unknown";
+ return false;
+ }
+
+ // Find the next input method.
+ std::vector<std::string>::const_iterator iter =
+ Find(active_input_method_ids_, current_input_method_.id());
+ if (iter != active_input_method_ids_.end())
+ ++iter;
+ if (iter == active_input_method_ids_.end())
+ iter = active_input_method_ids_.begin();
+ ChangeInputMethod(*iter);
+ return true;
+}
+
+bool InputMethodManagerImpl::SwitchToPreviousInputMethod() {
+ if (ignore_hotkeys_)
+ return false;
+
+ // Sanity check.
+ if (active_input_method_ids_.empty()) {
+ LOG(ERROR) << "active input method is empty";
+ return false;
+ }
+
+ if (previous_input_method_.id().empty() ||
+ previous_input_method_.id() == current_input_method_.id()) {
+ return SwitchToNextInputMethod();
+ }
+
+ std::vector<std::string>::const_iterator iter =
+ Find(active_input_method_ids_, previous_input_method_.id());
+ if (iter == active_input_method_ids_.end()) {
+ // previous_input_method_ is not supported.
+ return SwitchToNextInputMethod();
+ }
+ ChangeInputMethod(*iter);
+ return true;
+}
+
+bool InputMethodManagerImpl::SwitchInputMethod(
+ const ui::Accelerator& accelerator) {
+ if (ignore_hotkeys_)
+ return false;
+
+ // Sanity check.
+ if (active_input_method_ids_.empty()) {
+ LOG(ERROR) << "active input method is empty";
+ return false;
+ }
+
+ // Get the list of input method ids for the |accelerator|. For example, get
+ // { "mozc-hangul", "xkb:kr:kr104:kor" } for ui::VKEY_DBE_SBCSCHAR.
+ std::vector<std::string> input_method_ids_to_switch;
+ switch (accelerator.key_code()) {
+ case ui::VKEY_CONVERT: // Henkan key on JP106 keyboard
+ input_method_ids_to_switch.push_back("mozc-jp");
+ break;
+ case ui::VKEY_NONCONVERT: // Muhenkan key on JP106 keyboard
+ input_method_ids_to_switch.push_back("xkb:jp::jpn");
+ break;
+ case ui::VKEY_DBE_SBCSCHAR: // ZenkakuHankaku key on JP106 keyboard
+ case ui::VKEY_DBE_DBCSCHAR:
+ input_method_ids_to_switch.push_back("mozc-jp");
+ input_method_ids_to_switch.push_back("xkb:jp::jpn");
+ break;
+ case ui::VKEY_HANGUL: // Hangul (or right Alt) key on Korean keyboard
+ case ui::VKEY_SPACE: // Shift+Space
+ input_method_ids_to_switch.push_back("mozc-hangul");
+ input_method_ids_to_switch.push_back("xkb:kr:kr104:kor");
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ if (input_method_ids_to_switch.empty()) {
+ LOG(ERROR) << "Unexpected VKEY: " << accelerator.key_code();
+ return false;
+ }
+
+ // Obtain the intersection of input_method_ids_to_switch and
+ // active_input_method_ids_. The order of IDs in active_input_method_ids_ is
+ // preserved.
+ std::vector<std::string> ids;
+ for (size_t i = 0; i < input_method_ids_to_switch.size(); ++i) {
+ const std::string& id = input_method_ids_to_switch[i];
+ if (Contains(active_input_method_ids_, id))
+ ids.push_back(id);
+ }
+ if (ids.empty()) {
+ // No input method for the accelerator is active. For example, we should
+ // just ignore VKEY_HANGUL when mozc-hangul is not active.
+ return false;
+ }
+
+ // If |current_input_method_| is not in ids, switch to ids[0]. If
+ // |current_input_method_| is ids[N], switch to ids[N+1].
+ std::vector<std::string>::const_iterator iter =
+ Find(ids, current_input_method_.id());
+ if (iter != ids.end())
+ ++iter;
+ if (iter == ids.end())
+ iter = ids.begin();
+ ChangeInputMethod(*iter);
+ return true; // consume the accelerator.
+}
+
+InputMethodDescriptor InputMethodManagerImpl::GetCurrentInputMethod() const {
+ if (current_input_method_.id().empty())
+ return InputMethodDescriptor::GetFallbackInputMethodDescriptor();
+ return current_input_method_;
+}
+
+InputMethodPropertyList
+InputMethodManagerImpl::GetCurrentInputMethodProperties() const {
+ if (should_hide_properties_ ||
+ // This check is necessary since an IME property (e.g. for Pinyin) might
+ // be sent from ibus-daemon AFTER the current input method is switched
+ // to XKB.
+ InputMethodUtil::IsKeyboardLayout(GetCurrentInputMethod().id())) {
+ return InputMethodPropertyList();
+ }
+ return ibus_controller_->GetCurrentProperties();
+}
+
+XKeyboard* InputMethodManagerImpl::GetXKeyboard() {
+ return xkeyboard_.get();
+}
+
+InputMethodUtil* InputMethodManagerImpl::GetInputMethodUtil() {
+ return &util_;
+}
+
+void InputMethodManagerImpl::Init() {
+ DCHECK(!ibus_controller_.get());
+
+ browser_state_monitor_.reset(new BrowserStateMonitor(this));
+ ibus_controller_.reset(IBusController::Create());
+ xkeyboard_.reset(XKeyboard::Create(util_));
+ ibus_controller_->AddObserver(this);
+}
+
+void InputMethodManagerImpl::SetIBusControllerForTesting(
+ IBusController* ibus_controller) {
+ ibus_controller_.reset(ibus_controller);
+ ibus_controller_->AddObserver(this);
+}
+
+void InputMethodManagerImpl::SetCandidateWindowControllerForTesting(
+ CandidateWindowController* candidate_window_controller) {
+ candidate_window_controller_.reset(candidate_window_controller);
+ candidate_window_controller_->Init();
+ candidate_window_controller_->AddObserver(this);
+}
+
+void InputMethodManagerImpl::SetXKeyboardForTesting(XKeyboard* xkeyboard) {
+ xkeyboard_.reset(xkeyboard);
+}
+
+void InputMethodManagerImpl::PropertyChanged() {
+ should_hide_properties_ = ibus_controller_->GetCurrentProperties().empty();
+ FOR_EACH_OBSERVER(InputMethodManager::Observer,
+ observers_,
+ InputMethodPropertyChanged(this));
+}
+
+void InputMethodManagerImpl::CandidateWindowOpened() {
+ FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
+ candidate_window_observers_,
+ CandidateWindowOpened(this));
+}
+
+void InputMethodManagerImpl::CandidateWindowClosed() {
+ FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
+ candidate_window_observers_,
+ CandidateWindowClosed(this));
+}
+
+void InputMethodManagerImpl::OnScreenLocked() {
+ saved_previous_input_method_ = previous_input_method_;
+ saved_current_input_method_ = current_input_method_;
+ saved_active_input_method_ids_ = active_input_method_ids_;
+
+ const std::string hardware_keyboard_id = util_.GetHardwareInputMethodId();
+ // We'll add the hardware keyboard if it's not included in
+ // |active_input_method_list| so that the user can always use the hardware
+ // keyboard on the screen locker.
+ bool should_add_hardware_keyboard = true;
+
+ active_input_method_ids_.clear();
+ for (size_t i = 0; i < saved_active_input_method_ids_.size(); ++i) {
+ const std::string& input_method_id = saved_active_input_method_ids_[i];
+ // Skip if it's not a keyboard layout. Drop input methods including
+ // extension ones.
+ if (!InputMethodUtil::IsKeyboardLayout(input_method_id))
+ continue;
+ active_input_method_ids_.push_back(input_method_id);
+ if (input_method_id == hardware_keyboard_id)
+ should_add_hardware_keyboard = false;
+ }
+ if (should_add_hardware_keyboard)
+ active_input_method_ids_.push_back(hardware_keyboard_id);
+
+ ChangeInputMethod(current_input_method_.id());
+}
+
+void InputMethodManagerImpl::OnScreenUnlocked() {
+ previous_input_method_ = saved_previous_input_method_;
+ current_input_method_ = saved_current_input_method_;
+ active_input_method_ids_ = saved_active_input_method_ids_;
+
+ ChangeInputMethod(current_input_method_.id());
+}
+
+bool InputMethodManagerImpl::InputMethodIsActivated(
+ const std::string& input_method_id) {
+ return Contains(active_input_method_ids_, input_method_id);
+}
+
+bool InputMethodManagerImpl::ContainOnlyKeyboardLayout(
+ const std::vector<std::string>& value) {
+ for (size_t i = 0; i < value.size(); ++i) {
+ if (!InputMethodUtil::IsKeyboardLayout(value[i]))
+ return false;
+ }
+ return true;
+}
+
+void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() {
+#if !defined(USE_VIRTUAL_KEYBOARD)
+ if (candidate_window_controller_.get())
+ return;
+
+ candidate_window_controller_.reset(
+ CandidateWindowController::CreateCandidateWindowController());
+ if (candidate_window_controller_->Init())
+ candidate_window_controller_->AddObserver(this);
+ else
+ LOG(WARNING) << "Failed to initialize the candidate window controller";
+#endif
+}
+
+// static
+InputMethodManagerImpl* InputMethodManagerImpl::GetInstanceForTesting() {
+ return new InputMethodManagerImpl;
+}
+
+} // namespace input_method
+} // namespace chromeos

Powered by Google App Engine
This is Rietveld 408576698