| Index: chrome/browser/chromeos/input_method/ibus_controller.cc
|
| diff --git a/chrome/browser/chromeos/input_method/ibus_controller.cc b/chrome/browser/chromeos/input_method/ibus_controller.cc
|
| index da473785bb6c14dcb3816514ab26ac63b42ecb13..7307512b992bee5b81c5a59aecf3488cffae44f9 100644
|
| --- a/chrome/browser/chromeos/input_method/ibus_controller.cc
|
| +++ b/chrome/browser/chromeos/input_method/ibus_controller.cc
|
| @@ -5,1176 +5,25 @@
|
| #include "chrome/browser/chromeos/input_method/ibus_controller.h"
|
|
|
| #if defined(HAVE_IBUS)
|
| -#include <ibus.h>
|
| +#include "chrome/browser/chromeos/input_method/ibus_controller_impl.h"
|
| +#else
|
| +#include "chrome/browser/chromeos/input_method/mock_ibus_controller.h"
|
| #endif
|
|
|
| -#include <algorithm> // for std::reverse.
|
| -#include <cstdio>
|
| -#include <cstring> // for std::strcmp.
|
| -#include <set>
|
| -#include <sstream>
|
| -#include <stack>
|
| -#include <utility>
|
| -
|
| -#include "base/memory/scoped_ptr.h"
|
| -#include "base/observer_list.h"
|
| -#include "chrome/browser/chromeos/input_method/input_method_engine.h"
|
| -#include "chrome/browser/chromeos/input_method/input_method_manager.h"
|
| -#include "chrome/browser/chromeos/input_method/input_method_util.h"
|
| -#include "chrome/browser/chromeos/input_method/input_method_whitelist.h"
|
| -#include "chrome/browser/chromeos/input_method/input_methods.h"
|
| -
|
| namespace chromeos {
|
| namespace input_method {
|
|
|
| -#if defined(HAVE_IBUS)
|
| -const char kPanelObjectKey[] = "panel-object";
|
| -
|
| -namespace {
|
| -
|
| -// Also defined in chrome/browser/chromeos/language_preferences.h (Chrome tree).
|
| -const char kGeneralSectionName[] = "general";
|
| -const char kPreloadEnginesConfigName[] = "preload_engines";
|
| -
|
| -// The list of input method property keys that we don't handle.
|
| -const char* kInputMethodPropertyKeysBlacklist[] = {
|
| - "setup", // menu for showing setup dialog used in anthy and hangul.
|
| - "chewing_settings_prop", // menu for showing setup dialog used in chewing.
|
| - "status", // used in m17n.
|
| -};
|
| -
|
| -const char* Or(const char* str1, const char* str2) {
|
| - return str1 ? str1 : str2;
|
| -}
|
| -
|
| -// Returns true if |key| is blacklisted.
|
| -bool PropertyKeyIsBlacklisted(const char* key) {
|
| - for (size_t i = 0; i < arraysize(kInputMethodPropertyKeysBlacklist); ++i) {
|
| - if (!std::strcmp(key, kInputMethodPropertyKeysBlacklist[i])) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -// Returns IBusInputContext for |input_context_path|. NULL on errors.
|
| -IBusInputContext* GetInputContext(
|
| - const std::string& input_context_path, IBusBus* ibus) {
|
| - GDBusConnection* connection = ibus_bus_get_connection(ibus);
|
| - if (!connection) {
|
| - LOG(ERROR) << "IBusConnection is null";
|
| - return NULL;
|
| - }
|
| - // This function does not issue an IBus IPC.
|
| - IBusInputContext* context = ibus_input_context_get_input_context(
|
| - input_context_path.c_str(), connection);
|
| - if (!context) {
|
| - LOG(ERROR) << "IBusInputContext is null: " << input_context_path;
|
| - }
|
| - return context;
|
| -}
|
| -
|
| -// Returns true if |prop| has children.
|
| -bool PropertyHasChildren(IBusProperty* prop) {
|
| - return prop && ibus_property_get_sub_props(prop) &&
|
| - ibus_prop_list_get(
|
| - // TODO(yusukes): Remove the cast when we migrate to ibus-1.5.
|
| - const_cast<IBusPropList*>(ibus_property_get_sub_props(prop)), 0);
|
| -}
|
| -
|
| -// This function is called by and FlattenProperty() and converts IBus
|
| -// representation of a property, |ibus_prop|, to our own and push_back the
|
| -// result to |out_prop_list|. This function returns true on success, and
|
| -// returns false if sanity checks for |ibus_prop| fail.
|
| -bool ConvertProperty(IBusProperty* ibus_prop,
|
| - int selection_item_id,
|
| - InputMethodPropertyList* out_prop_list) {
|
| - DCHECK(ibus_prop);
|
| - DCHECK(out_prop_list);
|
| -
|
| - const IBusPropType type = ibus_property_get_prop_type(ibus_prop);
|
| - const IBusPropState state = ibus_property_get_state(ibus_prop);
|
| - const IBusText* tooltip = ibus_property_get_tooltip(ibus_prop);
|
| - const IBusText* label = ibus_property_get_label(ibus_prop);
|
| - const gchar* key = ibus_property_get_key(ibus_prop);
|
| - DCHECK(key);
|
| -
|
| - // Sanity checks.
|
| - const bool has_sub_props = PropertyHasChildren(ibus_prop);
|
| - if (has_sub_props && (type != PROP_TYPE_MENU)) {
|
| - LOG(ERROR) << "The property has sub properties, "
|
| - << "but the type of the property is not PROP_TYPE_MENU";
|
| - return false;
|
| - }
|
| - if ((!has_sub_props) && (type == PROP_TYPE_MENU)) {
|
| - // This is usually not an error. ibus-daemon sometimes sends empty props.
|
| - VLOG(1) << "Property list is empty";
|
| - return false;
|
| - }
|
| - if (type == PROP_TYPE_SEPARATOR || type == PROP_TYPE_MENU) {
|
| - // This is not an error, but we don't push an item for these types.
|
| - return true;
|
| - }
|
| -
|
| - const bool is_selection_item = (type == PROP_TYPE_RADIO);
|
| - selection_item_id = is_selection_item ?
|
| - selection_item_id : InputMethodProperty::kInvalidSelectionItemId;
|
| -
|
| - bool is_selection_item_checked = false;
|
| - if (state == PROP_STATE_INCONSISTENT) {
|
| - LOG(WARNING) << "The property is in PROP_STATE_INCONSISTENT, "
|
| - << "which is not supported.";
|
| - } else if ((!is_selection_item) && (state == PROP_STATE_CHECKED)) {
|
| - LOG(WARNING) << "PROP_STATE_CHECKED is meaningful only if the type is "
|
| - << "PROP_TYPE_RADIO.";
|
| - } else {
|
| - is_selection_item_checked = (state == PROP_STATE_CHECKED);
|
| - }
|
| -
|
| - if (!key) {
|
| - LOG(ERROR) << "key is NULL";
|
| - }
|
| - if (tooltip && !tooltip->text) {
|
| - LOG(ERROR) << "tooltip is NOT NULL, but tooltip->text IS NULL: key="
|
| - << Or(key, "");
|
| - }
|
| - if (label && !label->text) {
|
| - LOG(ERROR) << "label is NOT NULL, but label->text IS NULL: key="
|
| - << Or(key, "");
|
| - }
|
| -
|
| - // This label will be localized on Chrome side.
|
| - // See src/chrome/browser/chromeos/status/language_menu_l10n_util.h.
|
| - std::string label_to_use = ((tooltip && tooltip->text) ? tooltip->text : "");
|
| - if (label_to_use.empty()) {
|
| - // Usually tooltips are more descriptive than labels.
|
| - label_to_use = (label && label->text) ? label->text : "";
|
| - }
|
| - if (label_to_use.empty()) {
|
| - // ibus-pinyin has a property whose label and tooltip are empty. Fall back
|
| - // to the key.
|
| - label_to_use = Or(key, "");
|
| - }
|
| -
|
| - out_prop_list->push_back(InputMethodProperty(key,
|
| - label_to_use,
|
| - is_selection_item,
|
| - is_selection_item_checked,
|
| - selection_item_id));
|
| - return true;
|
| -}
|
| -
|
| -// Converts |ibus_prop| to |out_prop_list|. Please note that |ibus_prop|
|
| -// may or may not have children. See the comment for FlattenPropertyList
|
| -// for details. Returns true if no error is found.
|
| -// TODO(yusukes): Write unittest.
|
| -bool FlattenProperty(IBusProperty* ibus_prop,
|
| - InputMethodPropertyList* out_prop_list) {
|
| - DCHECK(ibus_prop);
|
| - DCHECK(out_prop_list);
|
| -
|
| - int selection_item_id = -1;
|
| - std::stack<std::pair<IBusProperty*, int> > prop_stack;
|
| - prop_stack.push(std::make_pair(ibus_prop, selection_item_id));
|
| -
|
| - while (!prop_stack.empty()) {
|
| - IBusProperty* prop = prop_stack.top().first;
|
| - const gchar* key = ibus_property_get_key(prop);
|
| - const int current_selection_item_id = prop_stack.top().second;
|
| - prop_stack.pop();
|
| -
|
| - // Filter out unnecessary properties.
|
| - if (PropertyKeyIsBlacklisted(key)) {
|
| - continue;
|
| - }
|
| -
|
| - // Convert |prop| to InputMethodProperty and push it to |out_prop_list|.
|
| - if (!ConvertProperty(prop, current_selection_item_id, out_prop_list)) {
|
| - return false;
|
| - }
|
| -
|
| - // Process childrens iteratively (if any). Push all sub properties to the
|
| - // stack.
|
| - if (PropertyHasChildren(prop)) {
|
| - ++selection_item_id;
|
| - for (int i = 0;; ++i) {
|
| - IBusProperty* sub_prop = ibus_prop_list_get(
|
| - // TODO(yusukes): Remove the cast when we migrate to ibus-1.5.
|
| - const_cast<IBusPropList*>(ibus_property_get_sub_props(prop)), i);
|
| - if (!sub_prop) {
|
| - break;
|
| - }
|
| - prop_stack.push(std::make_pair(sub_prop, selection_item_id));
|
| - }
|
| - ++selection_item_id;
|
| - }
|
| - }
|
| - std::reverse(out_prop_list->begin(), out_prop_list->end());
|
| -
|
| - return true;
|
| -}
|
| -
|
| -// Converts IBus representation of a property list, |ibus_prop_list| to our
|
| -// own. This function also flatten the original list (actually it's a tree).
|
| -// Returns true if no error is found. The conversion to our own type is
|
| -// necessary since our language switcher in Chrome tree don't (or can't) know
|
| -// IBus types. Here is an example:
|
| -//
|
| -// ======================================================================
|
| -// Input:
|
| -//
|
| -// --- Item-1
|
| -// |- Item-2
|
| -// |- SubMenuRoot --- Item-3-1
|
| -// | |- Item-3-2
|
| -// | |- Item-3-3
|
| -// |- Item-4
|
| -//
|
| -// (Note: Item-3-X is a selection item since they're on a sub menu.)
|
| -//
|
| -// Output:
|
| -//
|
| -// Item-1, Item-2, Item-3-1, Item-3-2, Item-3-3, Item-4
|
| -// (Note: SubMenuRoot does not appear in the output.)
|
| -// ======================================================================
|
| -// TODO(yusukes): Write unittest.
|
| -bool FlattenPropertyList(
|
| - IBusPropList* ibus_prop_list, InputMethodPropertyList* out_prop_list) {
|
| - DCHECK(ibus_prop_list);
|
| - DCHECK(out_prop_list);
|
| -
|
| - IBusProperty* fake_root_prop = ibus_property_new("Dummy.Key",
|
| - PROP_TYPE_MENU,
|
| - NULL, /* label */
|
| - "", /* icon */
|
| - NULL, /* tooltip */
|
| - FALSE, /* sensitive */
|
| - FALSE, /* visible */
|
| - PROP_STATE_UNCHECKED,
|
| - ibus_prop_list);
|
| - g_return_val_if_fail(fake_root_prop, false);
|
| - // Increase the ref count so it won't get deleted when |fake_root_prop|
|
| - // is deleted.
|
| - g_object_ref(ibus_prop_list);
|
| - const bool result = FlattenProperty(fake_root_prop, out_prop_list);
|
| - g_object_unref(fake_root_prop);
|
| -
|
| - return result;
|
| -}
|
| -
|
| -// Debug print function.
|
| -const char* PropTypeToString(int prop_type) {
|
| - switch (static_cast<IBusPropType>(prop_type)) {
|
| - case PROP_TYPE_NORMAL:
|
| - return "NORMAL";
|
| - case PROP_TYPE_TOGGLE:
|
| - return "TOGGLE";
|
| - case PROP_TYPE_RADIO:
|
| - return "RADIO";
|
| - case PROP_TYPE_MENU:
|
| - return "MENU";
|
| - case PROP_TYPE_SEPARATOR:
|
| - return "SEPARATOR";
|
| - }
|
| - return "UNKNOWN";
|
| -}
|
| -
|
| -// Debug print function.
|
| -const char* PropStateToString(int prop_state) {
|
| - switch (static_cast<IBusPropState>(prop_state)) {
|
| - case PROP_STATE_UNCHECKED:
|
| - return "UNCHECKED";
|
| - case PROP_STATE_CHECKED:
|
| - return "CHECKED";
|
| - case PROP_STATE_INCONSISTENT:
|
| - return "INCONSISTENT";
|
| - }
|
| - return "UNKNOWN";
|
| -}
|
| -
|
| -// Debug print function.
|
| -std::string Spacer(int n) {
|
| - return std::string(n, ' ');
|
| -}
|
| -
|
| -std::string PrintPropList(IBusPropList *prop_list, int tree_level);
|
| -// Debug print function.
|
| -std::string PrintProp(IBusProperty *prop, int tree_level) {
|
| - if (!prop) {
|
| - return "";
|
| - }
|
| -
|
| - const IBusPropType type = ibus_property_get_prop_type(prop);
|
| - const IBusPropState state = ibus_property_get_state(prop);
|
| - const IBusText* tooltip = ibus_property_get_tooltip(prop);
|
| - const IBusText* label = ibus_property_get_label(prop);
|
| - const gchar* key = ibus_property_get_key(prop);
|
| -
|
| - std::stringstream stream;
|
| - stream << Spacer(tree_level) << "=========================" << std::endl;
|
| - stream << Spacer(tree_level) << "key: " << Or(key, "<none>")
|
| - << std::endl;
|
| - stream << Spacer(tree_level) << "label: "
|
| - << ((label && label->text) ? label->text : "<none>")
|
| - << std::endl;
|
| - stream << Spacer(tree_level) << "tooptip: "
|
| - << ((tooltip && tooltip->text)
|
| - ? tooltip->text : "<none>") << std::endl;
|
| - stream << Spacer(tree_level) << "sensitive: "
|
| - << (ibus_property_get_sensitive(prop) ? "YES" : "NO") << std::endl;
|
| - stream << Spacer(tree_level) << "visible: "
|
| - << (ibus_property_get_visible(prop) ? "YES" : "NO") << std::endl;
|
| - stream << Spacer(tree_level) << "type: " << PropTypeToString(type)
|
| - << std::endl;
|
| - stream << Spacer(tree_level) << "state: " << PropStateToString(state)
|
| - << std::endl;
|
| - stream << Spacer(tree_level) << "sub_props: "
|
| - << (PropertyHasChildren(prop) ? "" : "<none>") << std::endl;
|
| - stream << PrintPropList(
|
| - // TODO(yusukes): Remove the cast when we migrate to ibus-1.5.
|
| - const_cast<IBusPropList*>(ibus_property_get_sub_props(prop)),
|
| - tree_level + 1);
|
| - stream << Spacer(tree_level) << "=========================" << std::endl;
|
| -
|
| - return stream.str();
|
| -}
|
| -
|
| -// Debug print function.
|
| -std::string PrintPropList(IBusPropList *prop_list, int tree_level) {
|
| - if (!prop_list) {
|
| - return "";
|
| - }
|
| -
|
| - std::stringstream stream;
|
| - for (int i = 0;; ++i) {
|
| - IBusProperty* prop = ibus_prop_list_get(prop_list, i);
|
| - if (!prop) {
|
| - break;
|
| - }
|
| - stream << PrintProp(prop, tree_level);
|
| - }
|
| - return stream.str();
|
| +IBusController::~IBusController() {
|
| }
|
|
|
| -} // namespace
|
| -
|
| -// The real implementation of the IBusController.
|
| -class IBusControllerImpl : public IBusController {
|
| - public:
|
| - IBusControllerImpl()
|
| - : ibus_(NULL),
|
| - ibus_config_(NULL) {
|
| - }
|
| -
|
| - ~IBusControllerImpl() {
|
| - // Disconnect signals so the handler functions will not be called with
|
| - // |this| which is already freed.
|
| - if (ibus_) {
|
| - g_signal_handlers_disconnect_by_func(
|
| - ibus_,
|
| - reinterpret_cast<gpointer>(G_CALLBACK(IBusBusConnectedThunk)),
|
| - this);
|
| - g_signal_handlers_disconnect_by_func(
|
| - ibus_,
|
| - reinterpret_cast<gpointer>(G_CALLBACK(IBusBusDisconnectedThunk)),
|
| - this);
|
| - g_signal_handlers_disconnect_by_func(
|
| - ibus_,
|
| - reinterpret_cast<gpointer>(G_CALLBACK(IBusBusNameOwnerChangedThunk)),
|
| - this);
|
| -
|
| - // Disconnect signals for the panel service as well.
|
| - IBusPanelService* ibus_panel_service = IBUS_PANEL_SERVICE(
|
| - g_object_get_data(G_OBJECT(ibus_), kPanelObjectKey));
|
| - if (ibus_panel_service) {
|
| - g_signal_handlers_disconnect_by_func(
|
| - ibus_panel_service,
|
| - reinterpret_cast<gpointer>(G_CALLBACK(FocusInThunk)),
|
| - this);
|
| - g_signal_handlers_disconnect_by_func(
|
| - ibus_panel_service,
|
| - reinterpret_cast<gpointer>(G_CALLBACK(RegisterPropertiesThunk)),
|
| - this);
|
| - g_signal_handlers_disconnect_by_func(
|
| - ibus_panel_service,
|
| - reinterpret_cast<gpointer>(G_CALLBACK(UpdatePropertyThunk)),
|
| - this);
|
| - }
|
| - }
|
| - }
|
| -
|
| - virtual void Connect() {
|
| - MaybeRestoreConnections();
|
| - }
|
| -
|
| - // IBusController override.
|
| - virtual bool StopInputMethodProcess() {
|
| - if (!IBusConnectionsAreAlive()) {
|
| - LOG(ERROR) << "StopInputMethodProcess: IBus connection is not alive";
|
| - return false;
|
| - }
|
| -
|
| - // Ask IBus to exit *asynchronously*.
|
| - ibus_bus_exit_async(ibus_,
|
| - FALSE /* do not restart */,
|
| - -1 /* timeout */,
|
| - NULL /* cancellable */,
|
| - NULL /* callback */,
|
| - NULL /* user_data */);
|
| -
|
| - if (ibus_config_) {
|
| - // Release |ibus_config_| unconditionally to make sure next
|
| - // IBusConnectionsAreAlive() call will return false.
|
| - g_object_unref(ibus_config_);
|
| - ibus_config_ = NULL;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - // IBusController override.
|
| - virtual void SetImePropertyActivated(const std::string& key,
|
| - bool activated) {
|
| - if (!IBusConnectionsAreAlive()) {
|
| - LOG(ERROR) << "SetImePropertyActivated: IBus connection is not alive";
|
| - return;
|
| - }
|
| - if (key.empty()) {
|
| - return;
|
| - }
|
| - if (input_context_path_.empty()) {
|
| - LOG(ERROR) << "Input context is unknown";
|
| - return;
|
| - }
|
| -
|
| - IBusInputContext* context = GetInputContext(input_context_path_, ibus_);
|
| - if (!context) {
|
| - return;
|
| - }
|
| - // Activate the property *asynchronously*.
|
| - ibus_input_context_property_activate(
|
| - context, key.c_str(),
|
| - (activated ? PROP_STATE_CHECKED : PROP_STATE_UNCHECKED));
|
| -
|
| - // We don't have to call ibus_proxy_destroy(context) explicitly here,
|
| - // i.e. we can just call g_object_unref(context), since g_object_unref can
|
| - // trigger both dispose, which is overridden by src/ibusproxy.c, and
|
| - // finalize functions. For details, see
|
| - // http://library.gnome.org/devel/gobject/stable/gobject-memory.html
|
| - g_object_unref(context);
|
| - }
|
| -
|
| - // IBusController override.
|
| - virtual bool ChangeInputMethod(const std::string& name) {
|
| - DCHECK(!InputMethodUtil::IsKeyboardLayout(name));
|
| -
|
| - if (!IBusConnectionsAreAlive()) {
|
| - LOG(ERROR) << "ChangeInputMethod: IBus connection is not alive";
|
| - return false;
|
| - }
|
| - if (name.empty()) {
|
| - return false;
|
| - }
|
| - if (!InputMethodIdIsWhitelisted(name) &&
|
| - name.find(kExtensionImePrefix) != 0) {
|
| - return false;
|
| - }
|
| -
|
| - // Clear all input method properties unconditionally.
|
| - //
|
| - // When switching to another input method and no text area is focused,
|
| - // RegisterProperties signal for the new input method will NOT be sent
|
| - // until a text area is focused. Therefore, we have to clear the old input
|
| - // method properties here to keep the input method switcher status
|
| - // consistent.
|
| - DoRegisterProperties(NULL);
|
| -
|
| - // Change the global engine *asynchronously*.
|
| - ibus_bus_set_global_engine_async(ibus_,
|
| - name.c_str(),
|
| - -1, // use the default ibus timeout
|
| - NULL, // cancellable
|
| - NULL, // callback
|
| - NULL); // user_data
|
| -
|
| - UpdateUI(name.c_str());
|
| - return true;
|
| - }
|
| -
|
| - // IBusController override.
|
| - virtual bool SetInputMethodConfig(const std::string& section,
|
| - const std::string& config_name,
|
| - const InputMethodConfigValue& value) {
|
| - // See comments in GetImeConfig() where ibus_config_get_value() is used.
|
| - if (!IBusConnectionsAreAlive()) {
|
| - LOG(ERROR) << "SetInputMethodConfig: IBus connection is not alive";
|
| - return false;
|
| - }
|
| -
|
| - bool is_preload_engines = false;
|
| -
|
| - // Sanity check: do not preload unknown/unsupported input methods.
|
| - std::vector<std::string> string_list;
|
| - if ((value.type == InputMethodConfigValue::kValueTypeStringList) &&
|
| - (section == kGeneralSectionName) &&
|
| - (config_name == kPreloadEnginesConfigName)) {
|
| - FilterInputMethods(value.string_list_value, &string_list);
|
| - if (string_list.empty()) {
|
| - return true;
|
| - }
|
| - is_preload_engines = true;
|
| - } else {
|
| - string_list = value.string_list_value;
|
| - }
|
| -
|
| - // Convert the type of |value| from our structure to GVariant.
|
| - GVariant* variant = NULL;
|
| - switch (value.type) {
|
| - case InputMethodConfigValue::kValueTypeString:
|
| - variant = g_variant_new_string(value.string_value.c_str());
|
| - break;
|
| - case InputMethodConfigValue::kValueTypeInt:
|
| - variant = g_variant_new_int32(value.int_value);
|
| - break;
|
| - case InputMethodConfigValue::kValueTypeBool:
|
| - variant = g_variant_new_boolean(value.bool_value);
|
| - break;
|
| - case InputMethodConfigValue::kValueTypeStringList:
|
| - GVariantBuilder variant_builder;
|
| - g_variant_builder_init(&variant_builder, G_VARIANT_TYPE("as"));
|
| - DCHECK(!string_list.empty());
|
| - const size_t size = string_list.size(); // don't use string_list_value.
|
| - for (size_t i = 0; i < size; ++i) {
|
| - g_variant_builder_add(&variant_builder, "s", string_list[i].c_str());
|
| - }
|
| - variant = g_variant_builder_end(&variant_builder);
|
| - break;
|
| - }
|
| -
|
| - if (!variant) {
|
| - LOG(ERROR) << "SetInputMethodConfig: variant is NULL";
|
| - return false;
|
| - }
|
| - DCHECK(g_variant_is_floating(variant));
|
| -
|
| - // Set an ibus configuration value *asynchronously*.
|
| - ibus_config_set_value_async(ibus_config_,
|
| - section.c_str(),
|
| - config_name.c_str(),
|
| - variant,
|
| - -1, // use the default ibus timeout
|
| - NULL, // cancellable
|
| - SetInputMethodConfigCallback,
|
| - g_object_ref(ibus_config_));
|
| -
|
| - // Since |variant| is floating, ibus_config_set_value_async consumes
|
| - // (takes ownership of) the variable.
|
| -
|
| - if (is_preload_engines) {
|
| - VLOG(1) << "SetInputMethodConfig: " << section << "/" << config_name
|
| - << ": " << value.ToString();
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - // IBusController override.
|
| - virtual void SendHandwritingStroke(const HandwritingStroke& stroke) {
|
| - if (stroke.size() < 2) {
|
| - LOG(WARNING) << "Empty stroke data or a single dot is passed.";
|
| - return;
|
| - }
|
| -
|
| - IBusInputContext* context = GetInputContext(input_context_path_, ibus_);
|
| - if (!context) {
|
| - return;
|
| - }
|
| -
|
| - const size_t raw_stroke_size = stroke.size() * 2;
|
| - scoped_array<double> raw_stroke(new double[raw_stroke_size]);
|
| - for (size_t n = 0; n < stroke.size(); ++n) {
|
| - raw_stroke[n * 2] = stroke[n].first; // x
|
| - raw_stroke[n * 2 + 1] = stroke[n].second; // y
|
| - }
|
| - ibus_input_context_process_hand_writing_event(
|
| - context, raw_stroke.get(), raw_stroke_size);
|
| - g_object_unref(context);
|
| - }
|
| -
|
| - // IBusController override.
|
| - virtual void CancelHandwriting(int n_strokes) {
|
| - IBusInputContext* context = GetInputContext(input_context_path_, ibus_);
|
| - if (!context) {
|
| - return;
|
| - }
|
| - ibus_input_context_cancel_hand_writing(context, n_strokes);
|
| - g_object_unref(context);
|
| - }
|
| -
|
| - virtual bool InputMethodIdIsWhitelisted(const std::string& input_method_id) {
|
| - return whitelist_.InputMethodIdIsWhitelisted(input_method_id);
|
| - }
|
| -
|
| - virtual bool XkbLayoutIsSupported(const std::string& xkb_layout) {
|
| - return whitelist_.XkbLayoutIsSupported(xkb_layout);
|
| - }
|
| -
|
| - virtual InputMethodDescriptor CreateInputMethodDescriptor(
|
| - const std::string& id,
|
| - const std::string& name,
|
| - const std::string& raw_layout,
|
| - const std::string& language_code) {
|
| - return InputMethodDescriptor(whitelist_, id, name, raw_layout,
|
| - language_code);
|
| - }
|
| -
|
| - // IBusController override.
|
| - virtual void AddObserver(Observer* observer) {
|
| - observers_.AddObserver(observer);
|
| - }
|
| -
|
| - // IBusController override.
|
| - virtual void RemoveObserver(Observer* observer) {
|
| - observers_.RemoveObserver(observer);
|
| - }
|
| -
|
| - private:
|
| - // Functions that end with Thunk are used to deal with glib callbacks.
|
| - //
|
| - // Note that we cannot use CHROMEG_CALLBACK_0() here as we'll define
|
| - // IBusBusConnected() inline. If we are to define the function outside
|
| - // of the class definition, we should use CHROMEG_CALLBACK_0() here.
|
| - //
|
| - // CHROMEG_CALLBACK_0(Impl,
|
| - // void, IBusBusConnected, IBusBus*);
|
| - static void IBusBusConnectedThunk(IBusBus* sender, gpointer userdata) {
|
| - return reinterpret_cast<IBusControllerImpl*>(userdata)
|
| - ->IBusBusConnected(sender);
|
| - }
|
| - static void IBusBusDisconnectedThunk(IBusBus* sender, gpointer userdata) {
|
| - return reinterpret_cast<IBusControllerImpl*>(userdata)
|
| - ->IBusBusDisconnected(sender);
|
| - }
|
| -
|
| - static void IBusBusNameOwnerChangedThunk(IBusBus* sender,
|
| - const gchar* name,
|
| - const gchar* old_name,
|
| - const gchar* new_name,
|
| - gpointer userdata) {
|
| - return reinterpret_cast<IBusControllerImpl*>(userdata)
|
| - ->IBusBusNameOwnerChanged(sender, name, old_name, new_name);
|
| - }
|
| - static void FocusInThunk(IBusPanelService* sender,
|
| - const gchar* input_context_path,
|
| - gpointer userdata) {
|
| - return reinterpret_cast<IBusControllerImpl*>(userdata)
|
| - ->FocusIn(sender, input_context_path);
|
| - }
|
| - static void RegisterPropertiesThunk(IBusPanelService* sender,
|
| - IBusPropList* prop_list,
|
| - gpointer userdata) {
|
| - return reinterpret_cast<IBusControllerImpl*>(userdata)
|
| - ->RegisterProperties(sender, prop_list);
|
| - }
|
| - static void UpdatePropertyThunk(IBusPanelService* sender,
|
| - IBusProperty* ibus_prop,
|
| - gpointer userdata) {
|
| - return reinterpret_cast<IBusControllerImpl*>(userdata)
|
| - ->UpdateProperty(sender, ibus_prop);
|
| - }
|
| -
|
| - // Checks if |ibus_| and |ibus_config_| connections are alive.
|
| - bool IBusConnectionsAreAlive() {
|
| - return ibus_ && ibus_bus_is_connected(ibus_) && ibus_config_;
|
| - }
|
| -
|
| - // Restores connections to ibus-daemon and ibus-memconf if they are not ready.
|
| - // If both |ibus_| and |ibus_config_| become ready, the function sends a
|
| - // notification to Chrome.
|
| - void MaybeRestoreConnections() {
|
| - if (IBusConnectionsAreAlive()) {
|
| - return;
|
| - }
|
| - MaybeCreateIBus();
|
| - MaybeRestoreIBusConfig();
|
| - if (IBusConnectionsAreAlive()) {
|
| - ConnectPanelServiceSignals();
|
| - VLOG(1) << "Notifying Chrome that IBus is ready.";
|
| - FOR_EACH_OBSERVER(Observer, observers_, OnConnectionChange(true));
|
| - }
|
| - }
|
| -
|
| - // Creates IBusBus object if it's not created yet.
|
| - void MaybeCreateIBus() {
|
| - if (ibus_) {
|
| - return;
|
| - }
|
| -
|
| - ibus_init();
|
| - // Establish IBus connection between ibus-daemon to retrieve the list of
|
| - // available input method engines, change the current input method engine,
|
| - // and so on.
|
| - ibus_ = ibus_bus_new();
|
| -
|
| - // Check the IBus connection status.
|
| - if (!ibus_) {
|
| - LOG(ERROR) << "ibus_bus_new() failed";
|
| - return;
|
| - }
|
| - // Register callback functions for IBusBus signals.
|
| - ConnectIBusSignals();
|
| -
|
| - // Ask libibus to watch the NameOwnerChanged signal *asynchronously*.
|
| - ibus_bus_set_watch_dbus_signal(ibus_, TRUE);
|
| -
|
| - if (ibus_bus_is_connected(ibus_)) {
|
| - VLOG(1) << "IBus connection is ready.";
|
| - }
|
| - }
|
| -
|
| - // Creates IBusConfig object if it's not created yet AND |ibus_| connection
|
| - // is ready.
|
| - void MaybeRestoreIBusConfig() {
|
| - if (!ibus_) {
|
| - return;
|
| - }
|
| -
|
| - // Destroy the current |ibus_config_| object. No-op if it's NULL.
|
| - MaybeDestroyIBusConfig();
|
| -
|
| - if (!ibus_config_) {
|
| - GDBusConnection* ibus_connection = ibus_bus_get_connection(ibus_);
|
| - if (!ibus_connection) {
|
| - VLOG(1) << "Couldn't create an ibus config object since "
|
| - << "IBus connection is not ready.";
|
| - return;
|
| - }
|
| - const gboolean disconnected
|
| - = g_dbus_connection_is_closed(ibus_connection);
|
| - if (disconnected) {
|
| - // |ibus_| object is not NULL, but the connection between ibus-daemon
|
| - // is not yet established. In this case, we don't create |ibus_config_|
|
| - // object.
|
| - LOG(ERROR) << "Couldn't create an ibus config object since "
|
| - << "IBus connection is closed.";
|
| - return;
|
| - }
|
| - // If memconf is not successfully started yet, ibus_config_new() will
|
| - // return NULL. Otherwise, it returns a transfer-none and non-floating
|
| - // object. ibus_config_new() sometimes issues a D-Bus *synchronous* IPC
|
| - // to check if the org.freedesktop.IBus.Config service is available.
|
| - ibus_config_ = ibus_config_new(ibus_connection,
|
| - NULL /* do not cancel the operation */,
|
| - NULL /* do not get error information */);
|
| - if (!ibus_config_) {
|
| - LOG(ERROR) << "ibus_config_new() failed. ibus-memconf is not ready?";
|
| - return;
|
| - }
|
| -
|
| - // TODO(yusukes): g_object_weak_ref might be better since it allows
|
| - // libcros to detect the delivery of the "destroy" glib signal the
|
| - // |ibus_config_| object.
|
| - g_object_ref(ibus_config_);
|
| - VLOG(1) << "ibus_config_ is ready.";
|
| - }
|
| - }
|
| -
|
| - // Destroys IBusConfig object if |ibus_| connection is not ready. This
|
| - // function does nothing if |ibus_config_| is NULL or |ibus_| connection is
|
| - // still alive. Note that the IBusConfig object can't be used when |ibus_|
|
| - // connection is not ready.
|
| - void MaybeDestroyIBusConfig() {
|
| - if (!ibus_) {
|
| - LOG(ERROR) << "MaybeDestroyIBusConfig: ibus_ is NULL";
|
| - return;
|
| - }
|
| - if (ibus_config_ && !ibus_bus_is_connected(ibus_)) {
|
| - g_object_unref(ibus_config_);
|
| - ibus_config_ = NULL;
|
| - }
|
| - }
|
| -
|
| - // Handles "RegisterProperties" signal from chromeos_input_method_ui.
|
| - void DoRegisterProperties(IBusPropList* ibus_prop_list) {
|
| - VLOG(1) << "RegisterProperties" << (ibus_prop_list ? "" : " (clear)");
|
| -
|
| - InputMethodPropertyList prop_list; // our representation.
|
| - if (ibus_prop_list) {
|
| - // You can call
|
| - // LOG(INFO) << "\n" << PrintPropList(ibus_prop_list, 0);
|
| - // here to dump |ibus_prop_list|.
|
| - if (!FlattenPropertyList(ibus_prop_list, &prop_list)) {
|
| - // Clear properties on errors.
|
| - DoRegisterProperties(NULL);
|
| - return;
|
| - }
|
| - }
|
| - VLOG(1) << "RegisterProperties" << (ibus_prop_list ? "" : " (clear)");
|
| - // Notify the change.
|
| - FOR_EACH_OBSERVER(Observer, observers_,
|
| - OnRegisterImeProperties(prop_list));
|
| - }
|
| -
|
| - // Retrieves input method status and notifies them to the UI.
|
| - // |current_global_engine_id| is the current global engine id such as "mozc"
|
| - // and "xkb:us::eng". If the id is unknown, an empty string "" can be passed.
|
| - // Warning: you can call this function only from ibus callback functions
|
| - // like FocusIn(). See http://crosbug.com/5217#c9 for details.
|
| - void UpdateUI(const char* current_global_engine_id) {
|
| - DCHECK(current_global_engine_id);
|
| -
|
| - const InputMethodsInfo* engine_info = NULL;
|
| - for (size_t i = 0; i < arraysize(kInputMethods); ++i) {
|
| - if (kInputMethods[i].input_method_id ==
|
| - std::string(current_global_engine_id)) {
|
| - engine_info = &kInputMethods[i];
|
| - break;
|
| - }
|
| - }
|
| -
|
| - InputMethodDescriptor current_input_method;
|
| - if (engine_info) {
|
| - current_input_method = CreateInputMethodDescriptor(
|
| - engine_info->input_method_id,
|
| - "",
|
| - engine_info->xkb_layout_id,
|
| - engine_info->language_code);
|
| - } else {
|
| - if (!InputMethodManager::GetInstance()->GetExtraDescriptor(
|
| - current_global_engine_id, ¤t_input_method)) {
|
| - LOG(ERROR) << current_global_engine_id
|
| - << " is not found in the input method white-list.";
|
| - return;
|
| - }
|
| - }
|
| -
|
| -
|
| - VLOG(1) << "Updating the UI. ID:" << current_input_method.id()
|
| - << ", keyboard_layout:" << current_input_method.keyboard_layout();
|
| -
|
| - // Notify the change to update UI.
|
| - FOR_EACH_OBSERVER(Observer, observers_,
|
| - OnCurrentInputMethodChanged(current_input_method));
|
| - }
|
| -
|
| - // Installs gobject signal handlers to |ibus_|.
|
| - void ConnectIBusSignals() {
|
| - if (!ibus_) {
|
| - return;
|
| - }
|
| -
|
| - // We use g_signal_connect_after here since the callback should be called
|
| - // *after* the IBusBusDisconnectedCallback in chromeos_input_method_ui.cc
|
| - // is called. chromeos_input_method_ui.cc attaches the panel service object
|
| - // to |ibus_|, and the callback in this file use the attached object.
|
| - g_signal_connect_after(ibus_,
|
| - "connected",
|
| - G_CALLBACK(IBusBusConnectedThunk),
|
| - this);
|
| -
|
| - g_signal_connect(ibus_,
|
| - "disconnected",
|
| - G_CALLBACK(IBusBusDisconnectedThunk),
|
| - this);
|
| -
|
| - g_signal_connect(ibus_,
|
| - "name-owner-changed",
|
| - G_CALLBACK(IBusBusNameOwnerChangedThunk),
|
| - this);
|
| - }
|
| -
|
| - // Installs gobject signal handlers to the panel service.
|
| - void ConnectPanelServiceSignals() {
|
| - if (!ibus_) {
|
| - return;
|
| - }
|
| -
|
| - IBusPanelService* ibus_panel_service = IBUS_PANEL_SERVICE(
|
| - g_object_get_data(G_OBJECT(ibus_), kPanelObjectKey));
|
| - if (!ibus_panel_service) {
|
| - LOG(ERROR) << "IBusPanelService is NOT available.";
|
| - return;
|
| - }
|
| - // We don't _ref() or _weak_ref() the panel service object, since we're not
|
| - // interested in the life time of the object.
|
| -
|
| - g_signal_connect(ibus_panel_service,
|
| - "focus-in",
|
| - G_CALLBACK(FocusInThunk),
|
| - this);
|
| - g_signal_connect(ibus_panel_service,
|
| - "register-properties",
|
| - G_CALLBACK(RegisterPropertiesThunk),
|
| - this);
|
| - g_signal_connect(ibus_panel_service,
|
| - "update-property",
|
| - G_CALLBACK(UpdatePropertyThunk),
|
| - this);
|
| - }
|
| -
|
| - // Handles "connected" signal from ibus-daemon.
|
| - void IBusBusConnected(IBusBus* bus) {
|
| - LOG(WARNING) << "IBus connection is recovered.";
|
| - MaybeRestoreConnections();
|
| - }
|
| -
|
| - // Handles "disconnected" signal from ibus-daemon.
|
| - void IBusBusDisconnected(IBusBus* bus) {
|
| - LOG(WARNING) << "IBus connection is terminated.";
|
| - // ibus-daemon might be terminated. Since |ibus_| object will automatically
|
| - // connect to the daemon if it restarts, we don't have to set NULL on ibus_.
|
| - // Call MaybeDestroyIBusConfig() to set |ibus_config_| to NULL temporarily.
|
| - MaybeDestroyIBusConfig();
|
| - VLOG(1) << "Notifying Chrome that IBus is terminated.";
|
| - FOR_EACH_OBSERVER(Observer, observers_, OnConnectionChange(false));
|
| - }
|
| -
|
| - // Handles "name-owner-changed" signal from ibus-daemon. The signal is sent
|
| - // to libcros when an IBus component such as ibus-memconf, ibus-engine-*, ..
|
| - // is started.
|
| - void IBusBusNameOwnerChanged(IBusBus* bus,
|
| - const gchar* name,
|
| - const gchar* old_name,
|
| - const gchar* new_name) {
|
| - DCHECK(name);
|
| - DCHECK(old_name);
|
| - DCHECK(new_name);
|
| - VLOG(1) << "Name owner is changed: name=" << name
|
| - << ", old_name=" << old_name << ", new_name=" << new_name;
|
| -
|
| - if (name != std::string("org.freedesktop.IBus.Config")) {
|
| - // Not a signal for ibus-memconf.
|
| - return;
|
| - }
|
| -
|
| - const std::string empty_string;
|
| - if (old_name != empty_string || new_name == empty_string) {
|
| - // ibus-memconf died?
|
| - LOG(WARNING) << "Unexpected name owner change: name=" << name
|
| - << ", old_name=" << old_name << ", new_name=" << new_name;
|
| - // TODO(yusukes): it might be nice to set |ibus_config_| to NULL and call
|
| - // |OnConnectionChange| with false here to allow Chrome to
|
| - // recover all input method configurations when ibus-memconf is
|
| - // automatically restarted by ibus-daemon. Though ibus-memconf is pretty
|
| - // stable and unlikely crashes.
|
| - return;
|
| - }
|
| -
|
| - VLOG(1) << "IBus config daemon is started. Recovering ibus_config_";
|
| -
|
| - // Try to recover |ibus_config_|. If the |ibus_config_| object is
|
| - // successfully created, |OnConnectionChange| will be called to
|
| - // notify Chrome that IBus is ready.
|
| - MaybeRestoreConnections();
|
| - }
|
| -
|
| - // Handles "FocusIn" signal from chromeos_input_method_ui.
|
| - void FocusIn(IBusPanelService* panel, const gchar* input_context_path) {
|
| - if (!input_context_path) {
|
| - LOG(ERROR) << "NULL context passed";
|
| - } else {
|
| - VLOG(1) << "FocusIn: " << input_context_path;
|
| - }
|
| - // Remember the current ic path.
|
| - input_context_path_ = Or(input_context_path, "");
|
| - }
|
| -
|
| - // Handles "RegisterProperties" signal from chromeos_input_method_ui.
|
| - void RegisterProperties(IBusPanelService* panel, IBusPropList* prop_list) {
|
| - DoRegisterProperties(prop_list);
|
| - }
|
| -
|
| - // Handles "UpdateProperty" signal from chromeos_input_method_ui.
|
| - void UpdateProperty(IBusPanelService* panel, IBusProperty* ibus_prop) {
|
| - VLOG(1) << "UpdateProperty";
|
| - DCHECK(ibus_prop);
|
| -
|
| - // You can call
|
| - // LOG(INFO) << "\n" << PrintProp(ibus_prop, 0);
|
| - // here to dump |ibus_prop|.
|
| -
|
| - InputMethodPropertyList prop_list; // our representation.
|
| - if (!FlattenProperty(ibus_prop, &prop_list)) {
|
| - // Don't update the UI on errors.
|
| - LOG(ERROR) << "Malformed properties are detected";
|
| - return;
|
| - }
|
| - // Notify the change.
|
| - if (!prop_list.empty()) {
|
| - FOR_EACH_OBSERVER(Observer, observers_,
|
| - OnUpdateImeProperty(prop_list));
|
| - }
|
| - }
|
| -
|
| - // Removes input methods that are not whitelisted from
|
| - // |requested_input_methods| and stores them on |out_filtered_input_methods|.
|
| - // TODO(yusukes): Write unittest.
|
| - void FilterInputMethods(
|
| - const std::vector<std::string>& requested_input_methods,
|
| - std::vector<std::string>* out_filtered_input_methods) {
|
| - out_filtered_input_methods->clear();
|
| - for (size_t i = 0; i < requested_input_methods.size(); ++i) {
|
| - const std::string& input_method = requested_input_methods[i];
|
| - if (whitelist_.InputMethodIdIsWhitelisted(input_method.c_str())) {
|
| - if (!InputMethodUtil::IsKeyboardLayout(input_method)) {
|
| - out_filtered_input_methods->push_back(input_method);
|
| - }
|
| - } else {
|
| - LOG(ERROR) << "Unsupported input method: " << input_method;
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Frees input method names in |engines| and the list itself. Please make sure
|
| - // that |engines| points the head of the list.
|
| - void FreeInputMethodNames(GList* engines) {
|
| - if (engines) {
|
| - for (GList* cursor = engines; cursor; cursor = g_list_next(cursor)) {
|
| - g_object_unref(IBUS_ENGINE_DESC(cursor->data));
|
| - }
|
| - g_list_free(engines);
|
| - }
|
| - }
|
| -
|
| - // Copies input method names in |engines| to |out|.
|
| - // TODO(yusukes): Write unittest.
|
| - void AddInputMethodNames(const GList* engines, InputMethodDescriptors* out) {
|
| - DCHECK(out);
|
| - for (; engines; engines = g_list_next(engines)) {
|
| - IBusEngineDesc* engine_desc = IBUS_ENGINE_DESC(engines->data);
|
| - const gchar* name = ibus_engine_desc_get_name(engine_desc);
|
| - const gchar* layout = ibus_engine_desc_get_layout(engine_desc);
|
| - const gchar* language = ibus_engine_desc_get_language(engine_desc);
|
| - if (whitelist_.InputMethodIdIsWhitelisted(name)) {
|
| - out->push_back(CreateInputMethodDescriptor(name, "", layout, language));
|
| - VLOG(1) << name << " (preloaded)";
|
| - }
|
| - }
|
| - }
|
| -
|
| - // A callback function that will be called when ibus_config_set_value_async()
|
| - // request is finished.
|
| - static void SetInputMethodConfigCallback(GObject* source_object,
|
| - GAsyncResult* res,
|
| - gpointer user_data) {
|
| - IBusConfig* config = IBUS_CONFIG(user_data);
|
| - g_return_if_fail(config);
|
| -
|
| - GError* error = NULL;
|
| - const gboolean result =
|
| - ibus_config_set_value_async_finish(config, res, &error);
|
| -
|
| - if (!result) {
|
| - std::string message = "(unknown error)";
|
| - if (error && error->message) {
|
| - message = error->message;
|
| - }
|
| - LOG(ERROR) << "ibus_config_set_value_async failed: " << message;
|
| - }
|
| -
|
| - if (error) {
|
| - g_error_free(error);
|
| - }
|
| - g_object_unref(config);
|
| - }
|
| -
|
| - // Connection to the ibus-daemon via IBus API. These objects are used to
|
| - // call ibus-daemon's API (e.g. activate input methods, set config, ...)
|
| - IBusBus* ibus_;
|
| - IBusConfig* ibus_config_;
|
| -
|
| - // Current input context path.
|
| - std::string input_context_path_;
|
| -
|
| - ObserverList<Observer> observers_;
|
| -
|
| - // An object which knows all valid input method and layout IDs.
|
| - InputMethodWhitelist whitelist_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(IBusControllerImpl);
|
| -};
|
| -
|
| -#endif // defined(HAVE_IBUS)
|
| -
|
| -// The stub implementation is used if IBus is not present.
|
| -//
|
| -// Note that this class is intentionally built even if HAVE_IBUS is
|
| -// defined so that we can easily tell build breakage when we change the
|
| -// IBusControllerImpl but forget to update the stub implementation.
|
| -class IBusControllerStubImpl : public IBusController {
|
| - public:
|
| - IBusControllerStubImpl() {
|
| - }
|
| -
|
| - virtual void Connect() {
|
| - };
|
| -
|
| - virtual void AddObserver(Observer* observer) {
|
| - }
|
| -
|
| - virtual void RemoveObserver(Observer* observer) {
|
| - }
|
| -
|
| - virtual bool StopInputMethodProcess() {
|
| - return true;
|
| - }
|
| -
|
| - virtual bool ChangeInputMethod(const std::string& name) {
|
| - return true;
|
| - }
|
| -
|
| - virtual void SetImePropertyActivated(const std::string& key,
|
| - bool activated) {
|
| - }
|
| -
|
| - virtual bool SetInputMethodConfig(const std::string& section,
|
| - const std::string& config_name,
|
| - const InputMethodConfigValue& value) {
|
| - return true;
|
| - }
|
| -
|
| - virtual void SendHandwritingStroke(const HandwritingStroke& stroke) {
|
| - }
|
| -
|
| - virtual void CancelHandwriting(int n_strokes) {
|
| - }
|
| -
|
| - // This is for ibus_controller_unittest.cc. Since the test is usually compiled
|
| - // without HAVE_IBUS, we have to provide the same implementation as
|
| - // IBusControllerImpl to test the whitelist class.
|
| - virtual bool InputMethodIdIsWhitelisted(const std::string& input_method_id) {
|
| - return whitelist_.InputMethodIdIsWhitelisted(input_method_id);
|
| - }
|
| - // See the comment above. We have to keep the implementation the same as
|
| - // IBusControllerImpl.
|
| - virtual bool XkbLayoutIsSupported(const std::string& xkb_layout) {
|
| - return whitelist_.XkbLayoutIsSupported(xkb_layout);
|
| - }
|
| - // See the comment above. We have to keep the implementation the same as
|
| - // IBusControllerImpl.
|
| - virtual InputMethodDescriptor CreateInputMethodDescriptor(
|
| - const std::string& id,
|
| - const std::string& name,
|
| - const std::string& raw_layout,
|
| - const std::string& language_code) {
|
| - return InputMethodDescriptor(whitelist_, id, name, raw_layout,
|
| - language_code);
|
| - }
|
| -
|
| - private:
|
| - InputMethodWhitelist whitelist_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(IBusControllerStubImpl);
|
| -};
|
| -
|
| +// static
|
| IBusController* IBusController::Create() {
|
| #if defined(HAVE_IBUS)
|
| return new IBusControllerImpl;
|
| #else
|
| - return new IBusControllerStubImpl;
|
| + return new MockIBusController;
|
| #endif
|
| }
|
|
|
| -IBusController::~IBusController() {
|
| -}
|
| -
|
| } // namespace input_method
|
| } // namespace chromeos
|
|
|