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 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
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(); |
863 | 594 |
595 DBusThreadManager::Get()->GetIBusPanelService()->SetUpPropertyHandler( | |
596 controller); | |
597 | |
864 // Restores previous input method at the beggining of connection. | 598 // Restores previous input method at the beggining of connection. |
satorux1
2012/12/02 23:14:24
Restores -> Restore
Seigo Nonaka
2012/12/03 09:53:49
Done.
| |
865 if (!controller->current_input_method_id_.empty()) { | 599 if (!controller->current_input_method_id_.empty()) { |
866 controller->SendChangeInputMethodRequest( | 600 controller->SendChangeInputMethodRequest( |
867 controller->current_input_method_id_); | 601 controller->current_input_method_id_); |
868 } | 602 } |
869 | 603 |
870 DBusThreadManager::Get()->GetIBusConfigClient()->InitializeAsync( | 604 DBusThreadManager::Get()->GetIBusConfigClient()->InitializeAsync( |
871 base::Bind(&IBusControllerImpl::OnIBusConfigClientInitialized, | 605 base::Bind(&IBusControllerImpl::OnIBusConfigClientInitialized, |
872 controller->weak_ptr_factory_.GetWeakPtr())); | 606 controller->weak_ptr_factory_.GetWeakPtr())); |
873 | 607 |
874 FOR_EACH_OBSERVER(Observer, controller->observers_, OnConnected()); | 608 FOR_EACH_OBSERVER(Observer, controller->observers_, OnConnected()); |
(...skipping 30 matching lines...) Expand all Loading... | |
905 FOR_EACH_OBSERVER(Observer, controller->observers_, OnDisconnected()); | 639 FOR_EACH_OBSERVER(Observer, controller->observers_, OnDisconnected()); |
906 | 640 |
907 if (on_exit_state == IBUS_DAEMON_SHUTTING_DOWN) { | 641 if (on_exit_state == IBUS_DAEMON_SHUTTING_DOWN) { |
908 // Normal exitting, so do nothing. | 642 // Normal exitting, so do nothing. |
909 return; | 643 return; |
910 } | 644 } |
911 | 645 |
912 LOG(ERROR) << "The ibus-daemon crashed. Re-launching..."; | 646 LOG(ERROR) << "The ibus-daemon crashed. Re-launching..."; |
913 controller->StartIBusDaemon(); | 647 controller->StartIBusDaemon(); |
914 } | 648 } |
915 #endif // defined(HAVE_IBUS) | |
916 | 649 |
917 // static | 650 // static |
918 bool IBusControllerImpl::FindAndUpdatePropertyForTesting( | 651 bool IBusControllerImpl::FindAndUpdatePropertyForTesting( |
919 const chromeos::input_method::InputMethodProperty& new_prop, | 652 const chromeos::input_method::InputMethodProperty& new_prop, |
920 chromeos::input_method::InputMethodPropertyList* prop_list) { | 653 chromeos::input_method::InputMethodPropertyList* prop_list) { |
921 return FindAndUpdateProperty(new_prop, prop_list); | 654 return FindAndUpdateProperty(new_prop, prop_list); |
922 } | 655 } |
923 | 656 |
924 } // namespace input_method | 657 } // namespace input_method |
925 } // namespace chromeos | 658 } // namespace chromeos |
OLD | NEW |