Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/chromeos/input_method/ibus_controller_impl.h" | 5 #include "chrome/browser/chromeos/input_method/ibus_controller_impl.h" |
| 6 | 6 |
| 7 #include <algorithm> // for std::reverse. | 7 #include <algorithm> // for std::reverse. |
| 8 #include <cstdio> | 8 #include <cstdio> |
| 9 #include <cstring> // for std::strcmp. | 9 #include <cstring> // for std::strcmp. |
| 10 #include <set> | 10 #include <set> |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 22 #include "base/string_split.h" | 22 #include "base/string_split.h" |
| 23 #include "base/stringprintf.h" | 23 #include "base/stringprintf.h" |
| 24 #include "chrome/browser/chromeos/input_method/input_method_config.h" | 24 #include "chrome/browser/chromeos/input_method/input_method_config.h" |
| 25 #include "chrome/browser/chromeos/input_method/input_method_property.h" | 25 #include "chrome/browser/chromeos/input_method/input_method_property.h" |
| 26 #include "chrome/browser/chromeos/input_method/input_method_util.h" | 26 #include "chrome/browser/chromeos/input_method/input_method_util.h" |
| 27 #include "chromeos/dbus/dbus_thread_manager.h" | 27 #include "chromeos/dbus/dbus_thread_manager.h" |
| 28 #include "chromeos/dbus/ibus/ibus_client.h" | 28 #include "chromeos/dbus/ibus/ibus_client.h" |
| 29 #include "chromeos/dbus/ibus/ibus_config_client.h" | 29 #include "chromeos/dbus/ibus/ibus_config_client.h" |
| 30 #include "chromeos/dbus/ibus/ibus_constants.h" | 30 #include "chromeos/dbus/ibus/ibus_constants.h" |
| 31 #include "chromeos/dbus/ibus/ibus_input_context_client.h" | 31 #include "chromeos/dbus/ibus/ibus_input_context_client.h" |
| 32 #include "chromeos/dbus/ibus/ibus_panel_service.h" | |
| 33 #include "chromeos/dbus/ibus/ibus_property.h" | |
| 32 #include "content/public/browser/browser_thread.h" | 34 #include "content/public/browser/browser_thread.h" |
| 33 #include "ui/aura/client/aura_constants.h" | 35 #include "ui/aura/client/aura_constants.h" |
| 34 #include "ui/aura/root_window.h" | 36 #include "ui/aura/root_window.h" |
| 35 #include "ui/base/ime/input_method_ibus.h" | 37 #include "ui/base/ime/input_method_ibus.h" |
| 36 | 38 |
| 37 // TODO(nona): Remove libibus dependency from this file. Then, write unit tests | |
| 38 // for all functions in this file. crbug.com/26334 | |
| 39 #if defined(HAVE_IBUS) | |
| 40 #include <ibus.h> | |
| 41 #endif | |
| 42 | |
| 43 namespace { | 39 namespace { |
| 44 | 40 |
| 45 // Finds a property which has |new_prop.key| from |prop_list|, and replaces the | 41 // Finds a property which has |new_prop.key| from |prop_list|, and replaces the |
| 46 // property with |new_prop|. Returns true if such a property is found. | 42 // property with |new_prop|. Returns true if such a property is found. |
| 47 bool FindAndUpdateProperty( | 43 bool FindAndUpdateProperty( |
| 48 const chromeos::input_method::InputMethodProperty& new_prop, | 44 const chromeos::input_method::InputMethodProperty& new_prop, |
| 49 chromeos::input_method::InputMethodPropertyList* prop_list) { | 45 chromeos::input_method::InputMethodPropertyList* prop_list) { |
| 50 for (size_t i = 0; i < prop_list->size(); ++i) { | 46 for (size_t i = 0; i < prop_list->size(); ++i) { |
| 51 chromeos::input_method::InputMethodProperty& prop = prop_list->at(i); | 47 chromeos::input_method::InputMethodProperty& prop = prop_list->at(i); |
| 52 if (prop.key == new_prop.key) { | 48 if (prop.key == new_prop.key) { |
| 53 prop = new_prop; | 49 prop = new_prop; |
| 54 return true; | 50 return true; |
| 55 } | 51 } |
| 56 } | 52 } |
| 57 return false; | 53 return false; |
| 58 } | 54 } |
| 59 | 55 |
| 60 void ConfigSetValueErrorCallback() { | 56 void ConfigSetValueErrorCallback() { |
| 61 DVLOG(1) << "IBusConfig: SetValue is failed."; | 57 DVLOG(1) << "IBusConfig: SetValue is failed."; |
| 62 } | 58 } |
| 63 | 59 |
| 64 } // namespace | 60 } // namespace |
| 65 | 61 |
| 66 namespace chromeos { | 62 namespace chromeos { |
| 67 namespace input_method { | 63 namespace input_method { |
| 68 | 64 |
| 69 #if defined(HAVE_IBUS) | |
| 70 const char kPanelObjectKey[] = "panel-object"; | |
| 71 | |
| 72 namespace { | 65 namespace { |
| 73 | 66 |
| 74 const char* Or(const char* str1, const char* str2) { | |
| 75 return str1 ? str1 : str2; | |
| 76 } | |
| 77 | |
| 78 // Returns true if |key| is blacklisted. | 67 // Returns true if |key| is blacklisted. |
| 79 bool PropertyKeyIsBlacklisted(const char* key) { | 68 bool PropertyKeyIsBlacklisted(const std::string& key) { |
| 80 // The list of input method property keys that we don't handle. | 69 // The list of input method property keys that we don't handle. |
| 81 static const char* kInputMethodPropertyKeysBlacklist[] = { | 70 static const char* kInputMethodPropertyKeysBlacklist[] = { |
| 82 "setup", // used in ibus-m17n. | 71 "setup", // used in ibus-m17n. |
| 83 "status", // used in ibus-m17n. | 72 "status", // used in ibus-m17n. |
| 84 }; | 73 }; |
| 85 for (size_t i = 0; i < arraysize(kInputMethodPropertyKeysBlacklist); ++i) { | 74 for (size_t i = 0; i < arraysize(kInputMethodPropertyKeysBlacklist); ++i) { |
| 86 if (!std::strcmp(key, kInputMethodPropertyKeysBlacklist[i])) | 75 if (key == kInputMethodPropertyKeysBlacklist[i]) |
| 87 return true; | 76 return true; |
| 88 } | 77 } |
| 89 return false; | 78 return false; |
| 90 } | 79 } |
| 91 | 80 |
| 92 // Returns true if |prop| has children. | |
| 93 bool PropertyHasChildren(IBusProperty* prop) { | |
| 94 return prop && ibus_property_get_sub_props(prop) && | |
| 95 ibus_prop_list_get(ibus_property_get_sub_props(prop), 0); | |
| 96 } | |
| 97 | |
| 98 // This function is called by and FlattenProperty() and converts IBus | 81 // This function is called by and FlattenProperty() and converts IBus |
| 99 // representation of a property, |ibus_prop|, to our own and push_back the | 82 // representation of a property, |ibus_prop|, to our own and push_back the |
| 100 // result to |out_prop_list|. This function returns true on success, and | 83 // result to |out_prop_list|. This function returns true on success, and |
| 101 // returns false if sanity checks for |ibus_prop| fail. | 84 // returns false if sanity checks for |ibus_prop| fail. |
| 102 bool ConvertProperty(IBusProperty* ibus_prop, | 85 bool ConvertProperty(const ibus::IBusProperty& ibus_prop, |
| 103 InputMethodPropertyList* out_prop_list) { | 86 InputMethodPropertyList* out_prop_list) { |
| 104 DCHECK(ibus_prop); | |
| 105 DCHECK(out_prop_list); | 87 DCHECK(out_prop_list); |
| 106 | 88 DCHECK(ibus_prop.key().empty()); |
| 107 const IBusPropType type = ibus_property_get_prop_type(ibus_prop); | 89 ibus::IBusProperty::IBusPropertyType type = ibus_prop.type(); |
| 108 const IBusPropState state = ibus_property_get_state(ibus_prop); | |
| 109 const IBusText* tooltip = ibus_property_get_tooltip(ibus_prop); | |
| 110 const IBusText* label = ibus_property_get_label(ibus_prop); | |
| 111 const gchar* key = ibus_property_get_key(ibus_prop); | |
| 112 DCHECK(key); | |
| 113 | 90 |
| 114 // Sanity checks. | 91 // Sanity checks. |
| 115 const bool has_sub_props = PropertyHasChildren(ibus_prop); | 92 const bool has_sub_props = !ibus_prop.sub_properties().empty(); |
| 116 if (has_sub_props && (type != PROP_TYPE_MENU)) { | 93 if (has_sub_props && (type != ibus::IBusProperty::IBUS_PROPERTY_TYPE_MENU)) { |
| 117 DVLOG(1) << "The property has sub properties, " | 94 DVLOG(1) << "The property has sub properties, " |
| 118 << "but the type of the property is not PROP_TYPE_MENU"; | 95 << "but the type of the property is not PROP_TYPE_MENU"; |
| 119 return false; | 96 return false; |
| 120 } | 97 } |
| 121 if ((!has_sub_props) && (type == PROP_TYPE_MENU)) { | 98 if ((!has_sub_props) && |
| 99 (type == ibus::IBusProperty::IBUS_PROPERTY_TYPE_MENU)) { | |
| 122 // This is usually not an error. ibus-daemon sometimes sends empty props. | 100 // This is usually not an error. ibus-daemon sometimes sends empty props. |
| 123 DVLOG(1) << "Property list is empty"; | 101 DVLOG(1) << "Property list is empty"; |
| 124 return false; | 102 return false; |
| 125 } | 103 } |
| 126 if (type == PROP_TYPE_SEPARATOR || type == PROP_TYPE_MENU) { | 104 if (type == ibus::IBusProperty::IBUS_PROPERTY_TYPE_SEPARATOR || |
| 105 type == ibus::IBusProperty::IBUS_PROPERTY_TYPE_MENU) { | |
| 127 // This is not an error, but we don't push an item for these types. | 106 // This is not an error, but we don't push an item for these types. |
| 128 return true; | 107 return true; |
| 129 } | 108 } |
| 130 | 109 |
| 131 const bool is_selection_item = (type == PROP_TYPE_RADIO); | |
| 132 | |
| 133 bool is_selection_item_checked = false; | |
| 134 if (state == PROP_STATE_INCONSISTENT) { | |
| 135 DVLOG(1) << "The property is in PROP_STATE_INCONSISTENT, " | |
| 136 << "which is not supported."; | |
| 137 } else if ((!is_selection_item) && (state == PROP_STATE_CHECKED)) { | |
| 138 DVLOG(1) << "PROP_STATE_CHECKED is meaningful only if the type is " | |
| 139 << "PROP_TYPE_RADIO."; | |
| 140 } else { | |
| 141 is_selection_item_checked = (state == PROP_STATE_CHECKED); | |
| 142 } | |
| 143 | |
| 144 if (!key) | |
| 145 DVLOG(1) << "key is NULL"; | |
| 146 if (tooltip && !tooltip->text) { | |
| 147 DVLOG(1) << "tooltip is NOT NULL, but tooltip->text IS NULL: key=" | |
| 148 << Or(key, ""); | |
| 149 } | |
| 150 if (label && !label->text) { | |
| 151 DVLOG(1) << "label is NOT NULL, but label->text IS NULL: key=" | |
| 152 << Or(key, ""); | |
| 153 } | |
| 154 | |
| 155 // This label will be localized later. | 110 // This label will be localized later. |
| 156 // See chrome/browser/chromeos/input_method/input_method_util.cc. | 111 // See chrome/browser/chromeos/input_method/input_method_util.cc. |
| 157 std::string label_to_use = (tooltip && tooltip->text) ? tooltip->text : ""; | 112 std::string label_to_use; |
| 158 if (label_to_use.empty()) { | 113 if (!ibus_prop.tooltip().empty()) |
| 159 // Usually tooltips are more descriptive than labels. | 114 label_to_use = ibus_prop.tooltip(); |
| 160 label_to_use = (label && label->text) ? label->text : ""; | 115 else if (!ibus_prop.label().empty()) |
| 161 } | 116 label_to_use = ibus_prop.label(); |
| 162 if (label_to_use.empty()) { | 117 else |
| 163 DVLOG(1) << "The tooltip and label are both empty. Use " << key; | 118 label_to_use = ibus_prop.key(); |
| 164 label_to_use = Or(key, ""); | |
| 165 } | |
| 166 | 119 |
| 167 out_prop_list->push_back(InputMethodProperty(key, | 120 out_prop_list->push_back(InputMethodProperty( |
| 168 label_to_use, | 121 ibus_prop.key(), |
| 169 is_selection_item, | 122 label_to_use, |
| 170 is_selection_item_checked)); | 123 (type == ibus::IBusProperty::IBUS_PROPERTY_TYPE_RADIO), |
| 124 ibus_prop.checked())); | |
| 171 return true; | 125 return true; |
| 172 } | 126 } |
| 173 | 127 |
| 174 // Converts |ibus_prop| to |out_prop_list|. Please note that |ibus_prop| | 128 // Converts |ibus_prop| to |out_prop_list|. Please note that |ibus_prop| |
| 175 // may or may not have children. See the comment for FlattenPropertyList | 129 // may or may not have children. See the comment for FlattenPropertyList |
| 176 // for details. Returns true if no error is found. | 130 // for details. Returns true if no error is found. |
| 177 bool FlattenProperty(IBusProperty* ibus_prop, | 131 bool FlattenProperty(const ibus::IBusProperty& ibus_prop, |
| 178 InputMethodPropertyList* out_prop_list) { | 132 InputMethodPropertyList* out_prop_list) { |
| 179 DCHECK(ibus_prop); | |
| 180 DCHECK(out_prop_list); | 133 DCHECK(out_prop_list); |
| 181 | 134 |
| 182 const gchar* key = ibus_property_get_key(ibus_prop); | |
| 183 | |
| 184 // Filter out unnecessary properties. | 135 // Filter out unnecessary properties. |
| 185 if (PropertyKeyIsBlacklisted(key)) | 136 if (PropertyKeyIsBlacklisted(ibus_prop.key())) |
| 186 return true; | 137 return true; |
| 187 | 138 |
| 188 // Convert |prop| to InputMethodProperty and push it to |out_prop_list|. | 139 // Convert |prop| to InputMethodProperty and push it to |out_prop_list|. |
| 189 if (!ConvertProperty(ibus_prop, out_prop_list)) | 140 if (!ConvertProperty(ibus_prop, out_prop_list)) |
| 190 return false; | 141 return false; |
| 191 | 142 |
| 192 // Process childrens iteratively (if any). Push all sub properties to the | 143 // Process childrens iteratively (if any). Push all sub properties to the |
| 193 // stack. | 144 // stack. |
| 194 if (PropertyHasChildren(ibus_prop)) { | 145 if (!ibus_prop.sub_properties().empty()) { |
| 195 for (int i = 0;; ++i) { | 146 const ibus::IBusPropertyList& sub_props = ibus_prop.sub_properties(); |
| 196 IBusProperty* sub_prop = | 147 for (size_t i = 0; i < sub_props.size(); ++i) { |
| 197 ibus_prop_list_get(ibus_property_get_sub_props(ibus_prop), i); | 148 if (!FlattenProperty(*sub_props[i], out_prop_list)) |
| 198 if (!sub_prop) | |
| 199 break; | |
| 200 if (!FlattenProperty(sub_prop, out_prop_list)) | |
| 201 return false; | 149 return false; |
| 202 } | 150 } |
| 203 } | 151 } |
| 204 return true; | 152 return true; |
| 205 } | 153 } |
| 206 | 154 |
| 207 // Converts IBus representation of a property list, |ibus_prop_list| to our | 155 // Converts IBus representation of a property list, |ibus_prop_list| to our |
| 208 // own. This function also flatten the original list (actually it's a tree). | 156 // own. This function also flatten the original list (actually it's a tree). |
| 209 // Returns true if no error is found. The conversion to our own type is | 157 // Returns true if no error is found. The conversion to our own type is |
| 210 // necessary since our language switcher in Chrome tree don't (or can't) know | 158 // necessary since our language switcher in Chrome tree don't (or can't) know |
| 211 // IBus types. Here is an example: | 159 // IBus types. Here is an example: |
| 212 // | 160 // |
| 213 // ====================================================================== | 161 // ====================================================================== |
| 214 // Input: | 162 // Input: |
| 215 // | 163 // |
| 216 // --- Item-1 | 164 // --- Item-1 |
| 217 // |- Item-2 | 165 // |- Item-2 |
| 218 // |- SubMenuRoot --- Item-3-1 | 166 // |- SubMenuRoot --- Item-3-1 |
| 219 // | |- Item-3-2 | 167 // | |- Item-3-2 |
| 220 // | |- Item-3-3 | 168 // | |- Item-3-3 |
| 221 // |- Item-4 | 169 // |- Item-4 |
| 222 // | 170 // |
| 223 // (Note: Item-3-X is a selection item since they're on a sub menu.) | 171 // (Note: Item-3-X is a selection item since they're on a sub menu.) |
| 224 // | 172 // |
| 225 // Output: | 173 // Output: |
| 226 // | 174 // |
| 227 // Item-1, Item-2, Item-3-1, Item-3-2, Item-3-3, Item-4 | 175 // Item-1, Item-2, Item-3-1, Item-3-2, Item-3-3, Item-4 |
| 228 // (Note: SubMenuRoot does not appear in the output.) | 176 // (Note: SubMenuRoot does not appear in the output.) |
| 229 // ====================================================================== | 177 // ====================================================================== |
| 230 bool FlattenPropertyList(IBusPropList* ibus_prop_list, | 178 bool FlattenPropertyList(const ibus::IBusPropertyList& ibus_prop_list, |
| 231 InputMethodPropertyList* out_prop_list) { | 179 InputMethodPropertyList* out_prop_list) { |
| 232 DCHECK(ibus_prop_list); | |
| 233 DCHECK(out_prop_list); | 180 DCHECK(out_prop_list); |
| 234 | 181 |
| 235 IBusProperty* fake_root_prop = ibus_property_new("Dummy.Key", | 182 bool result = true; |
| 236 PROP_TYPE_MENU, | 183 for (size_t i = 0; i < ibus_prop_list.size(); ++i) { |
| 237 NULL, /* label */ | 184 result &= FlattenProperty(*ibus_prop_list[i], out_prop_list); |
| 238 "", /* icon */ | 185 } |
| 239 NULL, /* tooltip */ | |
| 240 FALSE, /* sensitive */ | |
| 241 FALSE, /* visible */ | |
| 242 PROP_STATE_UNCHECKED, | |
| 243 ibus_prop_list); | |
| 244 g_return_val_if_fail(fake_root_prop, false); | |
| 245 // Increase the ref count so it won't get deleted when |fake_root_prop| | |
| 246 // is deleted. | |
| 247 g_object_ref(ibus_prop_list); | |
| 248 const bool result = FlattenProperty(fake_root_prop, out_prop_list); | |
| 249 g_object_unref(fake_root_prop); | |
| 250 | |
| 251 return result; | 186 return result; |
| 252 } | 187 } |
| 253 | 188 |
| 254 // Debug print function. | |
| 255 const char* PropTypeToString(int prop_type) { | |
| 256 switch (static_cast<IBusPropType>(prop_type)) { | |
| 257 case PROP_TYPE_NORMAL: | |
| 258 return "NORMAL"; | |
| 259 case PROP_TYPE_TOGGLE: | |
| 260 return "TOGGLE"; | |
| 261 case PROP_TYPE_RADIO: | |
| 262 return "RADIO"; | |
| 263 case PROP_TYPE_MENU: | |
| 264 return "MENU"; | |
| 265 case PROP_TYPE_SEPARATOR: | |
| 266 return "SEPARATOR"; | |
| 267 } | |
| 268 return "UNKNOWN"; | |
| 269 } | |
| 270 | |
| 271 // Debug print function. | |
| 272 const char* PropStateToString(int prop_state) { | |
| 273 switch (static_cast<IBusPropState>(prop_state)) { | |
| 274 case PROP_STATE_UNCHECKED: | |
| 275 return "UNCHECKED"; | |
| 276 case PROP_STATE_CHECKED: | |
| 277 return "CHECKED"; | |
| 278 case PROP_STATE_INCONSISTENT: | |
| 279 return "INCONSISTENT"; | |
| 280 } | |
| 281 return "UNKNOWN"; | |
| 282 } | |
| 283 | |
| 284 // Debug print function. | |
| 285 std::string Spacer(int n) { | |
| 286 return std::string(n, ' '); | |
| 287 } | |
| 288 | |
| 289 std::string PrintPropList(IBusPropList *prop_list, int tree_level); | |
| 290 | |
| 291 // Debug print function. | |
| 292 std::string PrintProp(IBusProperty *prop, int tree_level) { | |
| 293 if (!prop) | |
| 294 return ""; | |
| 295 | |
| 296 const IBusPropType type = ibus_property_get_prop_type(prop); | |
| 297 const IBusPropState state = ibus_property_get_state(prop); | |
| 298 const IBusText* tooltip = ibus_property_get_tooltip(prop); | |
| 299 const IBusText* label = ibus_property_get_label(prop); | |
| 300 const gchar* key = ibus_property_get_key(prop); | |
| 301 | |
| 302 std::stringstream stream; | |
| 303 stream << Spacer(tree_level) << "=========================" << std::endl; | |
| 304 stream << Spacer(tree_level) << "key: " << Or(key, "<none>") | |
| 305 << std::endl; | |
| 306 stream << Spacer(tree_level) << "label: " | |
| 307 << ((label && label->text) ? label->text : "<none>") | |
| 308 << std::endl; | |
| 309 stream << Spacer(tree_level) << "tooptip: " | |
| 310 << ((tooltip && tooltip->text) | |
| 311 ? tooltip->text : "<none>") << std::endl; | |
| 312 stream << Spacer(tree_level) << "sensitive: " | |
| 313 << (ibus_property_get_sensitive(prop) ? "YES" : "NO") << std::endl; | |
| 314 stream << Spacer(tree_level) << "visible: " | |
| 315 << (ibus_property_get_visible(prop) ? "YES" : "NO") << std::endl; | |
| 316 stream << Spacer(tree_level) << "type: " << PropTypeToString(type) | |
| 317 << std::endl; | |
| 318 stream << Spacer(tree_level) << "state: " << PropStateToString(state) | |
| 319 << std::endl; | |
| 320 stream << Spacer(tree_level) << "sub_props: " | |
| 321 << (PropertyHasChildren(prop) ? "" : "<none>") << std::endl; | |
| 322 stream << PrintPropList(ibus_property_get_sub_props(prop), tree_level + 1); | |
| 323 stream << Spacer(tree_level) << "=========================" << std::endl; | |
| 324 | |
| 325 return stream.str(); | |
| 326 } | |
| 327 | |
| 328 // Debug print function. | |
| 329 std::string PrintPropList(IBusPropList *prop_list, int tree_level) { | |
| 330 if (!prop_list) | |
| 331 return ""; | |
| 332 | |
| 333 std::stringstream stream; | |
| 334 for (int i = 0;; ++i) { | |
| 335 IBusProperty* prop = ibus_prop_list_get(prop_list, i); | |
| 336 if (!prop) | |
| 337 break; | |
| 338 stream << PrintProp(prop, tree_level); | |
| 339 } | |
| 340 return stream.str(); | |
| 341 } | |
| 342 | |
| 343 class IBusAddressWatcher { | 189 class IBusAddressWatcher { |
| 344 public: | 190 public: |
| 345 class IBusAddressFileWatcherDelegate | 191 class IBusAddressFileWatcherDelegate |
| 346 : public base::files::FilePathWatcher::Delegate { | 192 : public base::files::FilePathWatcher::Delegate { |
| 347 public: | 193 public: |
| 348 IBusAddressFileWatcherDelegate( | 194 IBusAddressFileWatcherDelegate( |
| 349 const std::string& ibus_address, | 195 const std::string& ibus_address, |
| 350 IBusControllerImpl* controller, | 196 IBusControllerImpl* controller, |
| 351 IBusAddressWatcher* watcher) | 197 IBusAddressWatcher* watcher) |
| 352 : ibus_address_(ibus_address), | 198 : ibus_address_(ibus_address), |
| 353 controller_(controller), | 199 controller_(controller), |
| 354 watcher_(watcher) { | 200 watcher_(watcher) { |
| 355 DCHECK(watcher); | 201 DCHECK(watcher); |
| 356 DCHECK(!ibus_address.empty()); | 202 DCHECK(!ibus_address.empty()); |
| 357 } | 203 } |
| 358 | 204 |
| 359 virtual ~IBusAddressFileWatcherDelegate() {} | |
| 360 | |
| 361 virtual void OnFilePathChanged(const FilePath& file_path) OVERRIDE { | 205 virtual void OnFilePathChanged(const FilePath& file_path) OVERRIDE { |
| 362 if (!watcher_->IsWatching()) | 206 if (!watcher_->IsWatching()) |
| 363 return; | 207 return; |
| 364 bool success = content::BrowserThread::PostTask( | 208 bool success = content::BrowserThread::PostTask( |
| 365 content::BrowserThread::UI, | 209 content::BrowserThread::UI, |
| 366 FROM_HERE, | 210 FROM_HERE, |
| 367 base::Bind( | 211 base::Bind( |
| 368 &IBusControllerImpl::IBusDaemonInitializationDone, | 212 &IBusControllerImpl::IBusDaemonInitializationDone, |
| 369 controller_, | 213 controller_, |
| 370 ibus_address_)); | 214 ibus_address_)); |
| 371 DCHECK(success); | 215 DCHECK(success); |
| 372 watcher_->StopSoon(); | 216 watcher_->StopSoon(); |
| 373 } | 217 } |
| 374 | 218 |
| 219 protected: | |
| 220 virtual ~IBusAddressFileWatcherDelegate() {} | |
| 221 | |
| 375 private: | 222 private: |
| 376 // The ibus-daemon address. | 223 // The ibus-daemon address. |
| 377 const std::string ibus_address_; | 224 const std::string ibus_address_; |
| 378 IBusControllerImpl* controller_; | 225 IBusControllerImpl* controller_; |
| 379 IBusAddressWatcher* watcher_; | 226 IBusAddressWatcher* watcher_; |
| 380 | 227 |
| 381 DISALLOW_COPY_AND_ASSIGN(IBusAddressFileWatcherDelegate); | 228 DISALLOW_COPY_AND_ASSIGN(IBusAddressFileWatcherDelegate); |
| 382 }; | 229 }; |
| 383 | 230 |
| 384 static void Start(const std::string& ibus_address, | 231 static void Start(const std::string& ibus_address, |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 429 IBusAddressWatcher() | 276 IBusAddressWatcher() |
| 430 : watcher_(NULL) {} | 277 : watcher_(NULL) {} |
| 431 base::files::FilePathWatcher* watcher_; | 278 base::files::FilePathWatcher* watcher_; |
| 432 | 279 |
| 433 DISALLOW_COPY_AND_ASSIGN(IBusAddressWatcher); | 280 DISALLOW_COPY_AND_ASSIGN(IBusAddressWatcher); |
| 434 }; | 281 }; |
| 435 | 282 |
| 436 } // namespace | 283 } // namespace |
| 437 | 284 |
| 438 IBusControllerImpl::IBusControllerImpl() | 285 IBusControllerImpl::IBusControllerImpl() |
| 439 : ibus_(NULL), | 286 : process_handle_(base::kNullProcessHandle), |
| 440 process_handle_(base::kNullProcessHandle), | |
| 441 ibus_daemon_status_(IBUS_DAEMON_STOP), | 287 ibus_daemon_status_(IBUS_DAEMON_STOP), |
| 442 input_method_(NULL), | 288 input_method_(NULL), |
| 443 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | 289 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { |
| 444 } | 290 } |
| 445 | 291 |
| 446 IBusControllerImpl::~IBusControllerImpl() { | 292 IBusControllerImpl::~IBusControllerImpl() { |
| 447 // Disconnect signals so the handler functions will not be called with | |
| 448 // |this| which is already freed. | |
| 449 if (ibus_) { | |
| 450 g_signal_handlers_disconnect_by_func( | |
| 451 ibus_, | |
| 452 reinterpret_cast<gpointer>(G_CALLBACK(BusConnectedThunk)), | |
| 453 this); | |
| 454 // Disconnect signals for the panel service as well. | |
| 455 // When Chrome is shutting down, g_object_get_data fails and returns NULL. | |
| 456 // TODO(nona): Investigate the reason of failure(crosbug.com/129142). | |
| 457 void* attached_data = g_object_get_data(G_OBJECT(ibus_), kPanelObjectKey); | |
| 458 if (!attached_data) | |
| 459 return; | |
| 460 if (!G_TYPE_CHECK_INSTANCE_TYPE(attached_data, IBUS_TYPE_PANEL_SERVICE)) | |
| 461 return; | |
| 462 IBusPanelService* ibus_panel_service = IBUS_PANEL_SERVICE(attached_data); | |
| 463 if (ibus_panel_service) { | |
| 464 g_signal_handlers_disconnect_by_func( | |
| 465 ibus_panel_service, | |
| 466 reinterpret_cast<gpointer>(G_CALLBACK(RegisterPropertiesThunk)), | |
| 467 this); | |
| 468 g_signal_handlers_disconnect_by_func( | |
| 469 ibus_panel_service, | |
| 470 reinterpret_cast<gpointer>(G_CALLBACK(UpdatePropertyThunk)), | |
| 471 this); | |
| 472 } | |
| 473 } | |
| 474 } | 293 } |
| 475 | 294 |
| 476 bool IBusControllerImpl::Start() { | 295 bool IBusControllerImpl::Start() { |
| 477 MaybeInitializeIBusBus(); | |
| 478 if (IBusConnectionsAreAlive()) | 296 if (IBusConnectionsAreAlive()) |
| 479 return true; | 297 return true; |
| 480 if (ibus_daemon_status_ == IBUS_DAEMON_STOP || | 298 if (ibus_daemon_status_ == IBUS_DAEMON_STOP || |
| 481 ibus_daemon_status_ == IBUS_DAEMON_SHUTTING_DOWN) { | 299 ibus_daemon_status_ == IBUS_DAEMON_SHUTTING_DOWN) { |
| 482 return StartIBusDaemon(); | 300 return StartIBusDaemon(); |
| 483 } | 301 } |
| 484 return true; | 302 return true; |
| 485 } | 303 } |
| 486 | 304 |
| 487 void IBusControllerImpl::Reset() { | 305 void IBusControllerImpl::Reset() { |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 539 // | 357 // |
| 540 // When |id| and |current_input_method_id_| are the same, the properties | 358 // When |id| and |current_input_method_id_| are the same, the properties |
| 541 // shouldn't be cleared. If we do that, something wrong happens in step #4 | 359 // shouldn't be cleared. If we do that, something wrong happens in step #4 |
| 542 // below: | 360 // below: |
| 543 // 1. Enable "xkb:us::eng" and "mozc". Switch to "mozc". | 361 // 1. Enable "xkb:us::eng" and "mozc". Switch to "mozc". |
| 544 // 2. Focus Omnibox. IME properties for mozc are sent to Chrome. | 362 // 2. Focus Omnibox. IME properties for mozc are sent to Chrome. |
| 545 // 3. Switch to "xkb:us::eng". No function in this file is called. | 363 // 3. Switch to "xkb:us::eng". No function in this file is called. |
| 546 // 4. Switch back to "mozc". ChangeInputMethod("mozc") is called, but it's | 364 // 4. Switch back to "mozc". ChangeInputMethod("mozc") is called, but it's |
| 547 // basically NOP since ibus-daemon's current IME is already "mozc". | 365 // basically NOP since ibus-daemon's current IME is already "mozc". |
| 548 // IME properties are not sent to Chrome for the same reason. | 366 // IME properties are not sent to Chrome for the same reason. |
| 549 if (id != current_input_method_id_) | 367 if (id != current_input_method_id_) { |
| 550 RegisterProperties(NULL, NULL); | 368 const ibus::IBusPropertyList empty_list; |
| 369 RegisterProperties(empty_list); | |
| 370 } | |
| 551 | 371 |
| 552 current_input_method_id_ = id; | 372 current_input_method_id_ = id; |
| 553 | 373 |
| 554 if (!IBusConnectionsAreAlive()) { | 374 if (!IBusConnectionsAreAlive()) { |
| 555 DVLOG(1) << "ChangeInputMethod: IBus connection is not alive (yet)."; | 375 DVLOG(1) << "ChangeInputMethod: IBus connection is not alive (yet)."; |
| 556 // |id| will become usable shortly since Start() has already been called. | 376 // |id| will become usable shortly since Start() has already been called. |
| 557 // Just return true. | 377 // Just return true. |
| 558 } else { | 378 } else { |
| 559 SendChangeInputMethodRequest(id); | 379 SendChangeInputMethodRequest(id); |
| 560 } | 380 } |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 585 | 405 |
| 586 IBusInputContextClient* client | 406 IBusInputContextClient* client |
| 587 = DBusThreadManager::Get()->GetIBusInputContextClient(); | 407 = DBusThreadManager::Get()->GetIBusInputContextClient(); |
| 588 if (client) | 408 if (client) |
| 589 client->PropertyActivate(key, | 409 client->PropertyActivate(key, |
| 590 static_cast<ibus::IBusPropertyState>(is_radio)); | 410 static_cast<ibus::IBusPropertyState>(is_radio)); |
| 591 return true; | 411 return true; |
| 592 } | 412 } |
| 593 | 413 |
| 594 bool IBusControllerImpl::IBusConnectionsAreAlive() { | 414 bool IBusControllerImpl::IBusConnectionsAreAlive() { |
| 595 return (ibus_daemon_status_ == IBUS_DAEMON_RUNNING) && | 415 return ibus_daemon_status_ == IBUS_DAEMON_RUNNING; |
| 596 ibus_ && ibus_bus_is_connected(ibus_); | |
| 597 } | |
| 598 | |
| 599 void IBusControllerImpl::MaybeRestoreConnections() { | |
| 600 if (IBusConnectionsAreAlive()) | |
| 601 return; | |
| 602 if (IBusConnectionsAreAlive()) { | |
| 603 DVLOG(1) << "ibus-daemon and ibus-memconf processes are ready."; | |
| 604 ConnectPanelServiceSignals(); | |
| 605 if (!current_input_method_id_.empty()) | |
| 606 SendChangeInputMethodRequest(current_input_method_id_); | |
| 607 } | |
| 608 } | |
| 609 | |
| 610 void IBusControllerImpl::MaybeInitializeIBusBus() { | |
| 611 if (ibus_) | |
| 612 return; | |
| 613 | |
| 614 ibus_init(); | |
| 615 // Establish IBus connection between ibus-daemon to change the current input | |
| 616 // method engine, properties, and so on. | |
| 617 ibus_ = ibus_bus_new(); | |
| 618 DCHECK(ibus_); | |
| 619 | |
| 620 // Register callback functions for IBusBus signals. | |
| 621 ConnectBusSignals(); | |
| 622 | |
| 623 if (ibus_bus_is_connected(ibus_)) { | |
| 624 DVLOG(1) << "IBus connection is ready: ibus-daemon is already running?"; | |
| 625 BusConnected(ibus_); | |
| 626 } | |
| 627 } | 416 } |
| 628 | 417 |
| 629 void IBusControllerImpl::SendChangeInputMethodRequest(const std::string& id) { | 418 void IBusControllerImpl::SendChangeInputMethodRequest(const std::string& id) { |
| 630 // Change the global engine *asynchronously*. | 419 // Change the global engine *asynchronously*. |
| 631 IBusClient* client = DBusThreadManager::Get()->GetIBusClient(); | 420 IBusClient* client = DBusThreadManager::Get()->GetIBusClient(); |
| 632 if (client) | 421 if (client) |
| 633 client->SetGlobalEngine(id.c_str(), base::Bind(&base::DoNothing)); | 422 client->SetGlobalEngine(id.c_str(), base::Bind(&base::DoNothing)); |
| 634 } | 423 } |
| 635 | 424 |
| 636 bool IBusControllerImpl::SetInputMethodConfigInternal( | 425 bool IBusControllerImpl::SetInputMethodConfigInternal( |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 664 key.second, | 453 key.second, |
| 665 value.string_list_value, | 454 value.string_list_value, |
| 666 base::Bind(&ConfigSetValueErrorCallback)); | 455 base::Bind(&ConfigSetValueErrorCallback)); |
| 667 return true; | 456 return true; |
| 668 default: | 457 default: |
| 669 DVLOG(1) << "SendInputMethodConfig: unknown value.type"; | 458 DVLOG(1) << "SendInputMethodConfig: unknown value.type"; |
| 670 return false; | 459 return false; |
| 671 } | 460 } |
| 672 } | 461 } |
| 673 | 462 |
| 674 void IBusControllerImpl::ConnectBusSignals() { | 463 void IBusControllerImpl::RegisterProperties( |
| 675 if (!ibus_) | 464 const ibus::IBusPropertyList& ibus_prop_list) { |
| 676 return; | |
| 677 | |
| 678 // We use g_signal_connect_after here since the callback should be called | |
| 679 // *after* the IBusBusDisconnectedCallback in chromeos_input_method_ui.cc | |
| 680 // is called. chromeos_input_method_ui.cc attaches the panel service object | |
| 681 // to |ibus_|, and the callback in this file use the attached object. | |
| 682 g_signal_connect_after(ibus_, | |
| 683 "connected", | |
| 684 G_CALLBACK(BusConnectedThunk), | |
| 685 this); | |
| 686 } | |
| 687 | |
| 688 void IBusControllerImpl::ConnectPanelServiceSignals() { | |
| 689 if (!ibus_) | |
| 690 return; | |
| 691 | |
| 692 IBusPanelService* ibus_panel_service = IBUS_PANEL_SERVICE( | |
| 693 g_object_get_data(G_OBJECT(ibus_), kPanelObjectKey)); | |
| 694 if (!ibus_panel_service) { | |
| 695 DVLOG(1) << "IBusPanelService is NOT available."; | |
| 696 return; | |
| 697 } | |
| 698 // We don't _ref() or _weak_ref() the panel service object, since we're not | |
| 699 // interested in the life time of the object. | |
| 700 g_signal_connect(ibus_panel_service, | |
| 701 "register-properties", | |
| 702 G_CALLBACK(RegisterPropertiesThunk), | |
| 703 this); | |
| 704 g_signal_connect(ibus_panel_service, | |
| 705 "update-property", | |
| 706 G_CALLBACK(UpdatePropertyThunk), | |
| 707 this); | |
| 708 } | |
| 709 | |
| 710 void IBusControllerImpl::BusConnected(IBusBus* bus) { | |
| 711 DVLOG(1) << "IBus connection is established."; | |
| 712 MaybeRestoreConnections(); | |
| 713 } | |
| 714 | |
| 715 void IBusControllerImpl::RegisterProperties(IBusPanelService* panel, | |
| 716 IBusPropList* ibus_prop_list) { | |
| 717 // Note: |panel| can be NULL. See ChangeInputMethod(). | 465 // Note: |panel| can be NULL. See ChangeInputMethod(). |
| 718 DVLOG(1) << "RegisterProperties" << (ibus_prop_list ? "" : " (clear)"); | |
| 719 | |
| 720 current_property_list_.clear(); | 466 current_property_list_.clear(); |
| 721 if (ibus_prop_list) { | 467 if (!FlattenPropertyList(ibus_prop_list, ¤t_property_list_)) |
| 722 // You can call | 468 current_property_list_.clear(); // Clear properties on errors. |
| 723 // DVLOG(1) << "\n" << PrintPropList(ibus_prop_list, 0); | |
| 724 // here to dump |ibus_prop_list|. | |
| 725 if (!FlattenPropertyList(ibus_prop_list, ¤t_property_list_)) { | |
| 726 // Clear properties on errors. | |
| 727 current_property_list_.clear(); | |
| 728 } | |
| 729 } | |
| 730 FOR_EACH_OBSERVER(Observer, observers_, PropertyChanged()); | 469 FOR_EACH_OBSERVER(Observer, observers_, PropertyChanged()); |
| 731 } | 470 } |
| 732 | 471 |
| 733 void IBusControllerImpl::UpdateProperty(IBusPanelService* panel, | 472 void IBusControllerImpl::UpdateProperty(const ibus::IBusProperty& ibus_prop) { |
| 734 IBusProperty* ibus_prop) { | |
| 735 DVLOG(1) << "UpdateProperty"; | |
| 736 DCHECK(ibus_prop); | |
| 737 | |
| 738 // You can call | |
| 739 // DVLOG(1) << "\n" << PrintProp(ibus_prop, 0); | |
| 740 // here to dump |ibus_prop|. | |
| 741 | |
| 742 InputMethodPropertyList prop_list; // our representation. | 473 InputMethodPropertyList prop_list; // our representation. |
| 743 if (!FlattenProperty(ibus_prop, &prop_list)) { | 474 if (!FlattenProperty(ibus_prop, &prop_list)) { |
| 744 // Don't update the UI on errors. | 475 // Don't update the UI on errors. |
| 745 DVLOG(1) << "Malformed properties are detected"; | 476 DVLOG(1) << "Malformed properties are detected"; |
| 746 return; | 477 return; |
| 747 } | 478 } |
| 748 | 479 |
| 749 // Notify the change. | 480 // Notify the change. |
| 750 if (!prop_list.empty()) { | 481 if (!prop_list.empty()) { |
| 751 for (size_t i = 0; i < prop_list.size(); ++i) { | 482 for (size_t i = 0; i < prop_list.size(); ++i) { |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 853 if (controller->ibus_daemon_status_ != IBUS_DAEMON_INITIALIZING) { | 584 if (controller->ibus_daemon_status_ != IBUS_DAEMON_INITIALIZING) { |
| 854 // Stop() or OnIBusDaemonExit() has already been called. | 585 // Stop() or OnIBusDaemonExit() has already been called. |
| 855 return; | 586 return; |
| 856 } | 587 } |
| 857 chromeos::DBusThreadManager::Get()->InitIBusBus(ibus_address); | 588 chromeos::DBusThreadManager::Get()->InitIBusBus(ibus_address); |
| 858 controller->ibus_daemon_status_ = IBUS_DAEMON_RUNNING; | 589 controller->ibus_daemon_status_ = IBUS_DAEMON_RUNNING; |
| 859 | 590 |
| 860 ui::InputMethodIBus* input_method_ibus = controller->GetInputMethod(); | 591 ui::InputMethodIBus* input_method_ibus = controller->GetInputMethod(); |
| 861 DCHECK(input_method_ibus); | 592 DCHECK(input_method_ibus); |
| 862 input_method_ibus->OnConnected(); | 593 input_method_ibus->OnConnected(); |
| 594 DBusThreadManager::Get()->GetIBusPanelService()->SetUpPropertyHandler( | |
| 595 controller); | |
| 596 if (!controller->current_input_method_id_.empty()) { | |
| 597 controller->SendChangeInputMethodRequest( | |
| 598 controller->current_input_method_id_); | |
| 599 } | |
| 863 | 600 |
| 864 DBusThreadManager::Get()->GetIBusConfigClient()->InitializeAsync( | 601 DBusThreadManager::Get()->GetIBusConfigClient()->InitializeAsync( |
| 865 base::Bind(&IBusControllerImpl::OnIBusConfigClientInitialized, | 602 base::Bind(&IBusControllerImpl::OnIBusConfigClientInitialized, |
| 866 controller->weak_ptr_factory_.GetWeakPtr())); | 603 controller->weak_ptr_factory_.GetWeakPtr())); |
| 867 | 604 |
| 868 FOR_EACH_OBSERVER(Observer, controller->observers_, OnConnected()); | 605 FOR_EACH_OBSERVER(Observer, controller->observers_, OnConnected()); |
| 869 | 606 |
| 870 VLOG(1) << "The ibus-daemon initialization is done."; | 607 VLOG(1) << "The ibus-daemon initialization is done."; |
| 871 } | 608 } |
| 872 | 609 |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 899 FOR_EACH_OBSERVER(Observer, controller->observers_, OnDisconnected()); | 636 FOR_EACH_OBSERVER(Observer, controller->observers_, OnDisconnected()); |
| 900 | 637 |
| 901 if (on_exit_state == IBUS_DAEMON_SHUTTING_DOWN) { | 638 if (on_exit_state == IBUS_DAEMON_SHUTTING_DOWN) { |
| 902 // Normal exitting, so do nothing. | 639 // Normal exitting, so do nothing. |
| 903 return; | 640 return; |
| 904 } | 641 } |
| 905 | 642 |
| 906 LOG(ERROR) << "The ibus-daemon crashed. Re-launching..."; | 643 LOG(ERROR) << "The ibus-daemon crashed. Re-launching..."; |
| 907 controller->StartIBusDaemon(); | 644 controller->StartIBusDaemon(); |
| 908 } | 645 } |
| 909 #endif // defined(HAVE_IBUS) | |
|
satorux1
2012/11/30 05:37:34
awesome!
| |
| 910 | 646 |
| 911 // static | 647 // static |
| 912 bool IBusControllerImpl::FindAndUpdatePropertyForTesting( | 648 bool IBusControllerImpl::FindAndUpdatePropertyForTesting( |
| 913 const chromeos::input_method::InputMethodProperty& new_prop, | 649 const chromeos::input_method::InputMethodProperty& new_prop, |
| 914 chromeos::input_method::InputMethodPropertyList* prop_list) { | 650 chromeos::input_method::InputMethodPropertyList* prop_list) { |
| 915 return FindAndUpdateProperty(new_prop, prop_list); | 651 return FindAndUpdateProperty(new_prop, prop_list); |
| 916 } | 652 } |
| 917 | 653 |
| 918 } // namespace input_method | 654 } // namespace input_method |
| 919 } // namespace chromeos | 655 } // namespace chromeos |
| OLD | NEW |