OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/chromeos/input_method/ibus_engine_controller.h" | |
6 | |
7 #if defined(HAVE_IBUS) | |
8 #include <ibus.h> | |
9 #endif | |
10 | |
11 #include <map> | |
12 #include <set> | |
13 #include <string> | |
14 #include <vector> | |
15 | |
16 #include "base/logging.h" | |
17 #include "base/stl_util.h" | |
18 | |
19 namespace chromeos { | |
20 namespace input_method { | |
21 | |
22 IBusEngineController::EngineProperty::EngineProperty() | |
23 : sensitive(false), visible(false), type(PROPERTY_TYPE_NORMAL), | |
24 checked(false), modified(0) { | |
25 } | |
26 | |
27 IBusEngineController::EngineProperty::~EngineProperty() { | |
28 STLDeleteContainerPointers(children.begin(), children.end()); | |
29 } | |
30 | |
31 #if defined(HAVE_IBUS) | |
32 | |
33 #define IBUS_TYPE_CHROMEOS_ENGINE (ibus_chromeos_engine_get_type()) | |
34 #define IBUS_CHROMEOS_ENGINE(obj) \ | |
35 (G_TYPE_CHECK_INSTANCE_CAST((obj), IBUS_TYPE_CHROMEOS_ENGINE, \ | |
36 IBusChromeOSEngine)) | |
37 #define IBUS_CHROMEOS_ENGINE_CLASS(klass) \ | |
38 (G_TYPE_CHECK_CLASS_CAST((klass), IBUS_TYPE_CHROMEOS_ENGINE, \ | |
39 IBusChromeOSEngineClass)) | |
40 #define CHROMEOS_IS_EXTENSION_ENGINE(obj) \ | |
41 (G_TYPE_CHECK_INSTANCE_TYPE((obj), IBUS_TYPE_CHROMEOS_ENGINE)) | |
42 #define CHROMEOS_IS_EXTENSION_ENGINE_CLASS(klass) \ | |
43 (G_TYPE_CHECK_CLASS_TYPE((klass), IBUS_TYPE_CHROMEOS_ENGINE)) | |
44 #define IBUS_CHROMEOS_ENGINE_GET_CLASS(obj) \ | |
45 (G_TYPE_INSTANCE_GET_CLASS((obj), IBUS_TYPE_CHROMEOS_ENGINE, \ | |
46 IBusChromeOSEngineClass)) | |
47 | |
48 class IBusEngineControllerImpl; | |
49 struct IBusChromeOSEngine { | |
50 IBusEngine parent; | |
51 IBusEngineControllerImpl* connection; | |
52 IBusLookupTable* table; | |
53 }; | |
54 | |
55 struct IBusChromeOSEngineClass { | |
56 IBusEngineClass parent_class; | |
57 }; | |
58 | |
59 G_DEFINE_TYPE(IBusChromeOSEngine, ibus_chromeos_engine, IBUS_TYPE_ENGINE) | |
60 | |
61 const int kDefaultCandidatePageSize = 9; | |
62 | |
63 class IBusEngineControllerImpl : public IBusEngineController { | |
64 public: | |
65 typedef std::map<std::string, IBusEngineControllerImpl*> ConnectionMap; | |
66 explicit IBusEngineControllerImpl(IBusEngineController::Observer* observer) | |
67 : observer_(observer), | |
68 ibus_(NULL), | |
69 active_engine_(NULL), | |
70 preedit_text_(NULL), | |
71 preedit_cursor_(0), | |
72 table_visible_(false), | |
73 cursor_visible_(false), | |
74 vertical_(false), | |
75 page_size_(kDefaultCandidatePageSize), | |
76 cursor_position_(0), | |
77 aux_text_visible_(false), | |
78 active_(false), | |
79 focused_(false) { | |
80 if (!g_connections_) { | |
81 g_connections_ = new ConnectionMap; | |
82 } | |
83 } | |
84 | |
85 ~IBusEngineControllerImpl() { | |
86 for (std::set<IBusChromeOSEngine*>::iterator ix = engine_instances_.begin(); | |
87 ix != engine_instances_.end(); ++ix) { | |
88 (*ix)->connection = NULL; | |
89 } | |
90 if (preedit_text_) { | |
91 g_object_unref(preedit_text_); | |
92 preedit_text_ = NULL; | |
93 } | |
94 if (ibus_) { | |
95 DisconnectIBusSignals(); | |
96 g_object_unref(ibus_); | |
97 } | |
98 g_connections_->erase(engine_id_); | |
99 ClearProperties(); | |
100 DVLOG(1) << "Removing engine: " << engine_id_; | |
101 } | |
102 | |
103 // Initializes the object. Returns true on success. | |
104 bool Init(const char* engine_id, const char* engine_name, | |
105 const char* description, const char* language, | |
106 const char* layout) { | |
107 engine_id_ = engine_id; | |
108 engine_name_ = engine_name; | |
109 description_ = description; | |
110 language_ = language; | |
111 layout_ = layout; | |
112 | |
113 bus_name_ = "org.freedesktop.IBus."; | |
114 bus_name_ += engine_id; | |
115 | |
116 // engine_id_ must be unique. | |
117 if (g_connections_->find(engine_id_) != g_connections_->end()) { | |
118 DVLOG(1) << "ibus engine name already in use: " << engine_id_; | |
119 return false; | |
120 } | |
121 | |
122 // Initialize an IBus bus. | |
123 ibus_init(); | |
124 ibus_ = ibus_bus_new(); | |
125 | |
126 // Check the IBus connection status. | |
127 if (!ibus_) { | |
128 DVLOG(1) << "ibus_bus_new() failed"; | |
129 return false; | |
130 } | |
131 | |
132 (*g_connections_)[engine_id_] = this; | |
133 DVLOG(1) << "Adding engine: " << engine_id_; | |
134 | |
135 // Check the IBus connection status. | |
136 bool result = true; | |
137 if (ibus_bus_is_connected(ibus_)) { | |
138 DVLOG(1) << "ibus_bus_is_connected(). IBus connection is ready."; | |
139 result = MaybeCreateComponent(); | |
140 } | |
141 | |
142 // Start listening the gobject signals regardless of the bus connection | |
143 // status. | |
144 ConnectIBusSignals(); | |
145 return result; | |
146 } | |
147 | |
148 virtual void SetPreeditText(const char* text, int cursor) { | |
149 if (active_engine_) { | |
150 DVLOG(1) << "SetPreeditText"; | |
151 if (preedit_text_) { | |
152 g_object_unref(preedit_text_); | |
153 preedit_text_ = NULL; | |
154 } | |
155 preedit_cursor_ = cursor; | |
156 preedit_text_ = static_cast<IBusText*>( | |
157 g_object_ref_sink(ibus_text_new_from_string(text))); | |
158 ibus_engine_update_preedit_text(IBUS_ENGINE(active_engine_), | |
159 preedit_text_, | |
160 preedit_cursor_, TRUE); | |
161 } | |
162 } | |
163 | |
164 virtual void SetPreeditUnderline(int start, int end, int type) { | |
165 DVLOG(1) << "SetPreeditUnderline"; | |
166 if (active_engine_ && preedit_text_) { | |
167 // Translate the type to ibus's constants. | |
168 int underline_type; | |
169 switch (type) { | |
170 case UNDERLINE_NONE: | |
171 underline_type = IBUS_ATTR_UNDERLINE_NONE; | |
172 break; | |
173 | |
174 case UNDERLINE_SINGLE: | |
175 underline_type = IBUS_ATTR_UNDERLINE_SINGLE; | |
176 break; | |
177 | |
178 case UNDERLINE_DOUBLE: | |
179 underline_type = IBUS_ATTR_UNDERLINE_DOUBLE; | |
180 break; | |
181 | |
182 case UNDERLINE_LOW: | |
183 underline_type = IBUS_ATTR_UNDERLINE_LOW; | |
184 break; | |
185 | |
186 case UNDERLINE_ERROR: | |
187 underline_type = IBUS_ATTR_UNDERLINE_ERROR; | |
188 break; | |
189 | |
190 default: | |
191 return; | |
192 } | |
193 | |
194 ibus_text_append_attribute(preedit_text_, IBUS_ATTR_TYPE_UNDERLINE, | |
195 underline_type, start, end); | |
196 ibus_engine_update_preedit_text(IBUS_ENGINE(active_engine_), | |
197 preedit_text_, | |
198 preedit_cursor_, TRUE); | |
199 } | |
200 } | |
201 | |
202 virtual void CommitText(const char* text) { | |
203 if (active_engine_) { | |
204 DVLOG(1) << "CommitText"; | |
205 // Reset the preedit text when a commit occurs. | |
206 SetPreeditText("", 0); | |
207 if (preedit_text_) { | |
208 g_object_unref(preedit_text_); | |
209 preedit_text_ = NULL; | |
210 } | |
211 IBusText* ibus_text = static_cast<IBusText*>( | |
212 ibus_text_new_from_string(text)); | |
213 ibus_engine_commit_text(IBUS_ENGINE(active_engine_), ibus_text); | |
214 } | |
215 } | |
216 | |
217 virtual void SetTableVisible(bool visible) { | |
218 table_visible_ = visible; | |
219 if (active_engine_) { | |
220 DVLOG(1) << "SetTableVisible"; | |
221 ibus_engine_update_lookup_table(IBUS_ENGINE(active_engine_), | |
222 active_engine_->table, | |
223 table_visible_); | |
224 if (visible) { | |
225 ibus_engine_show_lookup_table(IBUS_ENGINE(active_engine_)); | |
226 } else { | |
227 ibus_engine_hide_lookup_table(IBUS_ENGINE(active_engine_)); | |
228 } | |
229 } | |
230 } | |
231 | |
232 virtual void SetCursorVisible(bool visible) { | |
233 cursor_visible_ = visible; | |
234 if (active_engine_) { | |
235 DVLOG(1) << "SetCursorVisible"; | |
236 ibus_lookup_table_set_cursor_visible(active_engine_->table, visible); | |
237 ibus_engine_update_lookup_table(IBUS_ENGINE(active_engine_), | |
238 active_engine_->table, | |
239 table_visible_); | |
240 } | |
241 } | |
242 | |
243 virtual void SetOrientationVertical(bool vertical) { | |
244 vertical_ = vertical; | |
245 if (active_engine_) { | |
246 DVLOG(1) << "SetOrientationVertical"; | |
247 ibus_lookup_table_set_orientation(active_engine_->table, | |
248 vertical ? IBUS_ORIENTATION_VERTICAL : | |
249 IBUS_ORIENTATION_HORIZONTAL); | |
250 ibus_engine_update_lookup_table(IBUS_ENGINE(active_engine_), | |
251 active_engine_->table, | |
252 table_visible_); | |
253 } | |
254 } | |
255 | |
256 virtual void SetPageSize(unsigned int size) { | |
257 page_size_ = size; | |
258 if (active_engine_) { | |
259 DVLOG(1) << "SetPageSize"; | |
260 ibus_lookup_table_set_page_size(active_engine_->table, size); | |
261 ibus_engine_update_lookup_table(IBUS_ENGINE(active_engine_), | |
262 active_engine_->table, | |
263 table_visible_); | |
264 } | |
265 } | |
266 | |
267 virtual void ClearCandidates() { | |
268 if (active_engine_) { | |
269 DVLOG(1) << "ClearCandidates"; | |
270 ibus_lookup_table_clear(active_engine_->table); | |
271 ibus_engine_update_lookup_table(IBUS_ENGINE(active_engine_), | |
272 active_engine_->table, | |
273 table_visible_); | |
274 } | |
275 } | |
276 | |
277 virtual void SetCandidates(std::vector<Candidate> candidates) { | |
278 // Text with this foreground color will be treated as an annotation. | |
279 const guint kAnnotationForegroundColor = 0x888888; | |
280 if (active_engine_) { | |
281 DVLOG(1) << "SetCandidates"; | |
282 ibus_lookup_table_clear(active_engine_->table); | |
283 | |
284 for (std::vector<Candidate>::iterator ix = candidates.begin(); | |
285 ix != candidates.end(); ++ix ) { | |
286 if (!ix->annotation.empty()) { | |
287 // If there's an annotation, append it to the value. | |
288 std::string candidate(ix->value); | |
289 candidate += " "; | |
290 size_t start = g_utf8_strlen(candidate.c_str(), -1); | |
291 candidate += ix->annotation; | |
292 | |
293 // Set the foreground color of the annotation text so that the | |
294 // candidate window will treat it properly. | |
295 size_t end = start + g_utf8_strlen(ix->annotation.c_str(), -1); | |
296 IBusText* ibus_text = static_cast<IBusText*>( | |
297 ibus_text_new_from_string(candidate.c_str())); | |
298 ibus_text_append_attribute(ibus_text, | |
299 IBUS_ATTR_TYPE_FOREGROUND, | |
300 kAnnotationForegroundColor, | |
301 start, | |
302 end); | |
303 ibus_lookup_table_append_candidate(active_engine_->table, ibus_text); | |
304 } else { | |
305 IBusText* ibus_text = static_cast<IBusText*>( | |
306 ibus_text_new_from_string(ix->value.c_str())); | |
307 ibus_lookup_table_append_candidate(active_engine_->table, ibus_text); | |
308 } | |
309 | |
310 // Add the label if it's set. | |
311 if (!ix->label.empty()) { | |
312 IBusText* ibus_label = static_cast<IBusText*>( | |
313 ibus_text_new_from_string(ix->label.c_str())); | |
314 ibus_lookup_table_set_label(active_engine_->table, | |
315 std::distance(candidates.begin(), ix), | |
316 ibus_label); | |
317 } | |
318 } | |
319 ibus_engine_update_lookup_table(IBUS_ENGINE(active_engine_), | |
320 active_engine_->table, | |
321 table_visible_); | |
322 } | |
323 } | |
324 | |
325 virtual void SetCandidateAuxText(const char* text) { | |
326 aux_text_ = text; | |
327 if (active_engine_) { | |
328 DVLOG(1) << "SetCandidateAuxText"; | |
329 IBusText* ibus_text = static_cast<IBusText*>( | |
330 ibus_text_new_from_string(aux_text_.c_str())); | |
331 ibus_engine_update_auxiliary_text(IBUS_ENGINE(active_engine_), ibus_text, | |
332 aux_text_visible_); | |
333 ibus_engine_update_lookup_table(IBUS_ENGINE(active_engine_), | |
334 active_engine_->table, | |
335 table_visible_); | |
336 } | |
337 } | |
338 | |
339 virtual void SetCandidateAuxTextVisible(bool visible) { | |
340 aux_text_visible_ = visible; | |
341 if (active_engine_) { | |
342 DVLOG(1) << "SetCandidateAuxTextVisible"; | |
343 IBusText* ibus_text = static_cast<IBusText*>( | |
344 ibus_text_new_from_string(aux_text_.c_str())); | |
345 ibus_engine_update_auxiliary_text(IBUS_ENGINE(active_engine_), ibus_text, | |
346 aux_text_visible_); | |
347 ibus_engine_update_lookup_table(IBUS_ENGINE(active_engine_), | |
348 active_engine_->table, | |
349 table_visible_); | |
350 } | |
351 } | |
352 | |
353 virtual void SetCursorPosition(unsigned int position) { | |
354 cursor_position_ = position; | |
355 if (active_engine_) { | |
356 DVLOG(1) << "SetCursorPosition"; | |
357 ibus_lookup_table_set_cursor_pos(active_engine_->table, position); | |
358 ibus_engine_update_lookup_table(IBUS_ENGINE(active_engine_), | |
359 active_engine_->table, | |
360 table_visible_); | |
361 } | |
362 } | |
363 | |
364 virtual bool RegisterProperties( | |
365 const std::vector<EngineProperty*>& properties) { | |
366 DVLOG(1) << "RegisterProperties"; | |
367 ClearProperties(); | |
368 CopyProperties(properties, &properties_); | |
369 | |
370 if (active_engine_) { | |
371 if (!SetProperties()) { | |
372 return false; | |
373 } | |
374 } | |
375 return true; | |
376 } | |
377 | |
378 void ClearProperties() { | |
379 STLDeleteContainerPointers(properties_.begin(), properties_.end()); | |
380 properties_.clear(); | |
381 property_map_.clear(); | |
382 } | |
383 | |
384 virtual bool UpdateProperties( | |
385 const std::vector<EngineProperty*>& properties) { | |
386 DVLOG(1) << "UpdateProperties"; | |
387 return UpdatePropertyList(properties); | |
388 } | |
389 | |
390 void CopyProperties(const std::vector<EngineProperty*>& properties, | |
391 std::vector<EngineProperty*>* dest) { | |
392 for (std::vector<EngineProperty*>::const_iterator property = | |
393 properties.begin(); property != properties.end(); ++property) { | |
394 if (property_map_.find((*property)->key) != property_map_.end()) { | |
395 DVLOG(1) << "Property collision on name: " << (*property)->key; | |
396 return; | |
397 } | |
398 | |
399 EngineProperty* new_property = new EngineProperty; | |
400 dest->push_back(new_property); | |
401 property_map_[(*property)->key] = new_property; | |
402 | |
403 new_property->key = (*property)->key; | |
404 new_property->label = (*property)->label; | |
405 new_property->tooltip = (*property)->tooltip; | |
406 new_property->sensitive = (*property)->sensitive; | |
407 new_property->visible = (*property)->visible; | |
408 new_property->type = (*property)->type; | |
409 new_property->checked = (*property)->checked; | |
410 | |
411 CopyProperties((*property)->children, &(new_property->children)); | |
412 } | |
413 } | |
414 | |
415 IBusPropList *MakePropertyList( | |
416 const std::vector<EngineProperty*>& properties) { | |
417 IBusPropList *prop_list = ibus_prop_list_new(); | |
418 for (std::vector<EngineProperty*>::const_iterator property = | |
419 properties.begin(); property != properties.end(); ++property) { | |
420 IBusPropList *children = NULL; | |
421 if (!(*property)->children.empty()) { | |
422 children = MakePropertyList((*property)->children); | |
423 } | |
424 | |
425 IBusPropType type = PROP_TYPE_NORMAL; | |
426 switch ((*property)->type) { | |
427 case PROPERTY_TYPE_NORMAL: | |
428 type = PROP_TYPE_NORMAL; | |
429 break; | |
430 case PROPERTY_TYPE_TOGGLE: | |
431 type = PROP_TYPE_TOGGLE; | |
432 break; | |
433 case PROPERTY_TYPE_RADIO: | |
434 type = PROP_TYPE_RADIO; | |
435 break; | |
436 case PROPERTY_TYPE_SEPARATOR: | |
437 type = PROP_TYPE_SEPARATOR; | |
438 break; | |
439 case PROPERTY_TYPE_MENU: | |
440 type = PROP_TYPE_MENU; | |
441 break; | |
442 default: | |
443 NOTREACHED(); | |
444 break; | |
445 } | |
446 | |
447 IBusPropState state = (*property)->checked ? PROP_STATE_CHECKED : | |
448 PROP_STATE_UNCHECKED; | |
449 | |
450 IBusProperty *ibus_property = | |
451 ibus_property_new((*property)->key.c_str(), | |
452 type, | |
453 static_cast<IBusText*>( | |
454 ibus_text_new_from_string( | |
455 (*property)->label.c_str())), | |
456 NULL, | |
457 static_cast<IBusText*>( | |
458 ibus_text_new_from_string( | |
459 (*property)->tooltip.c_str())), | |
460 (*property)->sensitive, | |
461 (*property)->visible, | |
462 state, | |
463 children); | |
464 | |
465 ibus_prop_list_append(prop_list, ibus_property); | |
466 } | |
467 return prop_list; | |
468 } | |
469 | |
470 bool SetProperties() { | |
471 IBusPropList *prop_list = MakePropertyList(properties_); | |
472 ibus_engine_register_properties(IBUS_ENGINE(active_engine_), prop_list); | |
473 return true; | |
474 } | |
475 | |
476 bool UpdatePropertyList(const std::vector<EngineProperty*>& properties) { | |
477 for (std::vector<EngineProperty*>::const_iterator property = | |
478 properties.begin(); property != properties.end(); ++property) { | |
479 std::map<std::string, EngineProperty*>::iterator cur_property = | |
480 property_map_.find((*property)->key); | |
481 if (cur_property == property_map_.end()) { | |
482 DVLOG(1) << "Missing property: " << (*property)->key; | |
483 return false; | |
484 } | |
485 | |
486 if ((*property)->modified & PROPERTY_MODIFIED_LABEL) { | |
487 cur_property->second->label = (*property)->label; | |
488 } | |
489 if ((*property)->modified & PROPERTY_MODIFIED_TOOLTIP) { | |
490 cur_property->second->tooltip = (*property)->tooltip; | |
491 } | |
492 if ((*property)->modified & PROPERTY_MODIFIED_SENSITIVE) { | |
493 cur_property->second->sensitive = (*property)->sensitive; | |
494 } | |
495 if ((*property)->modified & PROPERTY_MODIFIED_VISIBLE) { | |
496 cur_property->second->visible = (*property)->visible; | |
497 } | |
498 if ((*property)->modified & PROPERTY_MODIFIED_TYPE) { | |
499 cur_property->second->type = (*property)->type; | |
500 } | |
501 if ((*property)->modified & PROPERTY_MODIFIED_CHECKED) { | |
502 cur_property->second->checked = (*property)->checked; | |
503 } | |
504 | |
505 if (active_engine_) { | |
506 IBusPropType type = PROP_TYPE_NORMAL; | |
507 switch (cur_property->second->type) { | |
508 case PROPERTY_TYPE_NORMAL: | |
509 type = PROP_TYPE_NORMAL; | |
510 break; | |
511 case PROPERTY_TYPE_TOGGLE: | |
512 type = PROP_TYPE_TOGGLE; | |
513 break; | |
514 case PROPERTY_TYPE_RADIO: | |
515 type = PROP_TYPE_RADIO; | |
516 break; | |
517 case PROPERTY_TYPE_SEPARATOR: | |
518 type = PROP_TYPE_SEPARATOR; | |
519 break; | |
520 case PROPERTY_TYPE_MENU: | |
521 type = PROP_TYPE_MENU; | |
522 break; | |
523 default: | |
524 NOTREACHED(); | |
525 break; | |
526 } | |
527 | |
528 IBusPropState state = cur_property->second->checked ? | |
529 PROP_STATE_CHECKED : PROP_STATE_UNCHECKED; | |
530 | |
531 IBusProperty *ibus_property = | |
532 ibus_property_new(cur_property->second->key.c_str(), | |
533 type, | |
534 static_cast<IBusText*>( | |
535 ibus_text_new_from_string( | |
536 cur_property->second->label.c_str())), | |
537 NULL, | |
538 static_cast<IBusText*>( | |
539 ibus_text_new_from_string( | |
540 cur_property->second->tooltip.c_str())), | |
541 cur_property->second->sensitive, | |
542 cur_property->second->visible, | |
543 state, | |
544 NULL); | |
545 | |
546 ibus_engine_update_property(IBUS_ENGINE(active_engine_), ibus_property); | |
547 } | |
548 if (!UpdatePropertyList((*property)->children)) { | |
549 return false; | |
550 } | |
551 } | |
552 return true; | |
553 } | |
554 | |
555 virtual void KeyEventDone(KeyEventHandle* key_data, bool handled) { | |
556 GDBusMethodInvocation* invocation = | |
557 reinterpret_cast<GDBusMethodInvocation*>(key_data); | |
558 | |
559 g_dbus_method_invocation_return_value(invocation, | |
560 g_variant_new("(b)", handled)); | |
561 } | |
562 | |
563 | |
564 static void InitEngineClass(IBusChromeOSEngineClass *klass) { | |
565 GObjectClass *object_class = G_OBJECT_CLASS(klass); | |
566 IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS(klass); | |
567 IBusEngineClass *engine_class = IBUS_ENGINE_CLASS(klass); | |
568 | |
569 object_class->constructor = EngineConstructor; | |
570 ibus_object_class->destroy = (IBusObjectDestroyFunc) OnDestroy; | |
571 | |
572 IBUS_SERVICE_CLASS(klass)->service_method_call = OnServiceMethodCall; | |
573 | |
574 engine_class->process_key_event = NULL; | |
575 engine_class->reset = OnReset; | |
576 engine_class->enable = OnEnable; | |
577 engine_class->disable = OnDisable; | |
578 engine_class->focus_in = OnFocusIn; | |
579 engine_class->focus_out = OnFocusOut; | |
580 engine_class->page_up = OnPageUp; | |
581 engine_class->page_down = OnPageDown; | |
582 engine_class->cursor_up = OnCursorUp; | |
583 engine_class->cursor_down = OnCursorDown; | |
584 engine_class->property_activate = OnPropertyActivate; | |
585 engine_class->candidate_clicked = OnCandidateClicked; | |
586 } | |
587 | |
588 static void InitEngine(IBusChromeOSEngine* engine) { | |
589 } | |
590 | |
591 private: | |
592 // Installs gobject signal handlers to |ibus_|. | |
593 void ConnectIBusSignals() { | |
594 if (!ibus_) { | |
595 return; | |
596 } | |
597 DVLOG(1) << "ConnectIBusSignals"; | |
598 g_signal_connect(ibus_, | |
599 "connected", | |
600 G_CALLBACK(IBusBusConnectedCallback), | |
601 this); | |
602 g_signal_connect(ibus_, | |
603 "disconnected", | |
604 G_CALLBACK(IBusBusDisconnectedCallback), | |
605 this); | |
606 } | |
607 | |
608 // Removes gobject signal handlers from |ibus_|. | |
609 void DisconnectIBusSignals() { | |
610 if (!ibus_) { | |
611 return; | |
612 } | |
613 g_signal_handlers_disconnect_by_func( | |
614 ibus_, | |
615 reinterpret_cast<gpointer>(G_CALLBACK(IBusBusConnectedCallback)), | |
616 this); | |
617 g_signal_handlers_disconnect_by_func( | |
618 ibus_, | |
619 reinterpret_cast<gpointer>(G_CALLBACK(IBusBusDisconnectedCallback)), | |
620 this); | |
621 } | |
622 | |
623 // Handles "connected" signal from ibus-daemon. | |
624 static void IBusBusConnectedCallback(IBusBus* bus, gpointer user_data) { | |
625 DVLOG(1) << "IBus connection is recovered."; | |
626 g_return_if_fail(user_data); | |
627 IBusEngineControllerImpl* self | |
628 = static_cast<IBusEngineControllerImpl*>(user_data); | |
629 DCHECK(ibus_bus_is_connected(self->ibus_)); | |
630 if (!self->MaybeCreateComponent()) { | |
631 DVLOG(1) << "MaybeCreateComponent() failed"; | |
632 return; | |
633 } | |
634 } | |
635 | |
636 // Handles "disconnected" signal from ibus-daemon. | |
637 static void IBusBusDisconnectedCallback(IBusBus* bus, gpointer user_data) { | |
638 DVLOG(1) << "IBus connection is terminated."; | |
639 if (g_factory_) { | |
640 g_object_unref(g_factory_); | |
641 g_factory_ = NULL; | |
642 } | |
643 } | |
644 | |
645 bool MaybeCreateComponent() { | |
646 if (!ibus_ || !ibus_bus_is_connected(ibus_)) { | |
647 return false; | |
648 } | |
649 | |
650 DVLOG(1) << "MaybeCreateComponent"; | |
651 IBusComponent* component = ibus_component_new(bus_name_.c_str(), | |
652 description_.c_str(), | |
653 "", | |
654 "", | |
655 engine_name_.c_str(), | |
656 "", | |
657 "", | |
658 ""); | |
659 g_object_ref_sink(component); | |
660 ibus_component_add_engine(component, | |
661 ibus_engine_desc_new(engine_id_.c_str(), | |
662 description_.c_str(), | |
663 description_.c_str(), | |
664 language_.c_str(), | |
665 "", | |
666 engine_name_.c_str(), | |
667 "", | |
668 layout_.c_str())); | |
669 | |
670 if (!g_factory_) { | |
671 g_factory_ = ibus_factory_new(ibus_bus_get_connection(ibus_)); | |
672 g_object_ref_sink(g_factory_); | |
673 } | |
674 | |
675 ibus_factory_add_engine(g_factory_, engine_id_.c_str(), | |
676 IBUS_TYPE_CHROMEOS_ENGINE); | |
677 ibus_bus_register_component(ibus_, component); | |
678 g_object_unref(component); | |
679 | |
680 return true; | |
681 } | |
682 | |
683 static void OnServiceMethodCall(IBusService* service, | |
684 GDBusConnection* connection, | |
685 const gchar* sender, | |
686 const gchar* object_path, | |
687 const gchar* interface_name, | |
688 const gchar* method_name, | |
689 GVariant* parameters, | |
690 GDBusMethodInvocation* invocation) { | |
691 if (g_strcmp0(method_name, "ProcessKeyEvent") == 0) { | |
692 // Override the default ProcessKeyEvent handler so that we can send the | |
693 // response asynchronously. | |
694 IBusEngine *engine = IBUS_ENGINE(service); | |
695 | |
696 guint keyval; | |
697 guint keycode; | |
698 guint state; | |
699 g_variant_get(parameters, "(uuu)", &keyval, &keycode, &state); | |
700 | |
701 OnProcessKeyEvent(engine, keyval, keycode, state, invocation); | |
702 } else { | |
703 IBUS_SERVICE_CLASS( | |
704 ibus_chromeos_engine_parent_class)->service_method_call( | |
705 service, | |
706 connection, | |
707 sender, | |
708 object_path, | |
709 interface_name, | |
710 method_name, | |
711 parameters, | |
712 invocation); | |
713 } | |
714 } | |
715 | |
716 static void OnProcessKeyEvent(IBusEngine* ibus_engine, guint keyval, | |
717 guint keycode, guint modifiers, | |
718 GDBusMethodInvocation* key_data) { | |
719 DVLOG(1) << "OnProcessKeyEvent"; | |
720 IBusChromeOSEngine* engine = IBUS_CHROMEOS_ENGINE(ibus_engine); | |
721 if (engine->connection) { | |
722 engine->connection->observer_->OnKeyEvent( | |
723 !(modifiers & IBUS_RELEASE_MASK), | |
724 keyval, keycode, | |
725 modifiers & IBUS_MOD1_MASK, | |
726 modifiers & IBUS_CONTROL_MASK, | |
727 modifiers & IBUS_SHIFT_MASK, | |
728 reinterpret_cast<KeyEventHandle*>(key_data)); | |
729 } | |
730 } | |
731 | |
732 static void OnReset(IBusEngine* ibus_engine) { | |
733 DVLOG(1) << "OnReset"; | |
734 IBusChromeOSEngine* engine = IBUS_CHROMEOS_ENGINE(ibus_engine); | |
735 if (engine->connection) { | |
736 engine->connection->observer_->OnReset(); | |
737 } | |
738 } | |
739 | |
740 static void OnEnable(IBusEngine* ibus_engine) { | |
741 DVLOG(1) << "OnEnable"; | |
742 IBusChromeOSEngine* engine = IBUS_CHROMEOS_ENGINE(ibus_engine); | |
743 if (engine->connection) { | |
744 engine->connection->active_engine_ = engine; | |
745 engine->connection->active_ = true; | |
746 engine->connection->observer_->OnEnable(); | |
747 if (engine->connection->focused_) { | |
748 engine->connection->observer_->OnFocusIn(); | |
749 } | |
750 engine->connection->SetProperties(); | |
751 } | |
752 } | |
753 | |
754 static void OnDisable(IBusEngine* ibus_engine) { | |
755 DVLOG(1) << "OnDisable"; | |
756 IBusChromeOSEngine* engine = IBUS_CHROMEOS_ENGINE(ibus_engine); | |
757 if (engine->connection) { | |
758 engine->connection->active_ = false; | |
759 engine->connection->observer_->OnDisable(); | |
760 if (engine->connection->active_engine_ == engine) { | |
761 engine->connection->active_engine_ = NULL; | |
762 } | |
763 } | |
764 } | |
765 | |
766 static void OnFocusIn(IBusEngine* ibus_engine) { | |
767 DVLOG(1) << "OnFocusIn"; | |
768 IBusChromeOSEngine* engine = IBUS_CHROMEOS_ENGINE(ibus_engine); | |
769 if (engine->connection) { | |
770 engine->connection->focused_ = true; | |
771 if (engine->connection->active_) { | |
772 engine->connection->observer_->OnFocusIn(); | |
773 } | |
774 engine->connection->SetProperties(); | |
775 } | |
776 } | |
777 | |
778 static void OnFocusOut(IBusEngine* ibus_engine) { | |
779 DVLOG(1) << "OnFocusOut"; | |
780 IBusChromeOSEngine* engine = IBUS_CHROMEOS_ENGINE(ibus_engine); | |
781 if (engine->connection) { | |
782 engine->connection->focused_ = false; | |
783 if (engine->connection->active_) { | |
784 engine->connection->observer_->OnFocusOut(); | |
785 } | |
786 } | |
787 } | |
788 | |
789 static void OnPageUp(IBusEngine* ibus_engine) { | |
790 // Not implemented | |
791 } | |
792 | |
793 static void OnPageDown(IBusEngine* ibus_engine) { | |
794 // Not implemented | |
795 } | |
796 | |
797 static void OnCursorUp(IBusEngine* ibus_engine) { | |
798 // Not implemented | |
799 } | |
800 | |
801 static void OnCursorDown(IBusEngine* ibus_engine) { | |
802 // Not implemented | |
803 } | |
804 | |
805 static void OnPropertyActivate(IBusEngine* ibus_engine, | |
806 const gchar *prop_name, guint prop_state) { | |
807 DVLOG(1) << "OnPropertyActivate"; | |
808 IBusChromeOSEngine* engine = IBUS_CHROMEOS_ENGINE(ibus_engine); | |
809 if (engine->connection) { | |
810 engine->connection->observer_->OnPropertyActivate(prop_name, prop_state); | |
811 } | |
812 } | |
813 | |
814 static void OnCandidateClicked(IBusEngine* ibus_engine, guint index, | |
815 guint button, guint state) { | |
816 DVLOG(1) << "OnCandidateClicked"; | |
817 IBusChromeOSEngine* engine = IBUS_CHROMEOS_ENGINE(ibus_engine); | |
818 if (engine->connection) { | |
819 int pressed_button = 0; | |
820 if (button & IBUS_BUTTON1_MASK) { | |
821 pressed_button |= MOUSE_BUTTON_1_MASK; | |
822 } | |
823 if (button & IBUS_BUTTON2_MASK) { | |
824 pressed_button |= MOUSE_BUTTON_2_MASK; | |
825 } | |
826 if (button & IBUS_BUTTON3_MASK) { | |
827 pressed_button |= MOUSE_BUTTON_3_MASK; | |
828 } | |
829 engine->connection->observer_->OnCandidateClicked(index, pressed_button, | |
830 state); | |
831 } | |
832 } | |
833 | |
834 static GObject* EngineConstructor(GType type, guint n_construct_params, | |
835 GObjectConstructParam *construct_params) { | |
836 IBusChromeOSEngine* engine = (IBusChromeOSEngine*) | |
837 G_OBJECT_CLASS(ibus_chromeos_engine_parent_class) | |
838 ->constructor(type, n_construct_params, construct_params); | |
839 const gchar* name = ibus_engine_get_name((IBusEngine*)engine); | |
840 ConnectionMap::iterator connection = g_connections_->find(name); | |
841 if (connection == g_connections_->end()) { | |
842 DVLOG(1) << "Connection never created: " << name; | |
843 engine->connection = NULL; | |
844 engine->table = NULL; | |
845 return (GObject *) engine; | |
846 } | |
847 | |
848 connection->second->engine_instances_.insert(engine); | |
849 | |
850 if (!connection->second->active_engine_) { | |
851 connection->second->active_engine_ = engine; | |
852 } | |
853 | |
854 engine->connection = connection->second; | |
855 | |
856 engine->table = ibus_lookup_table_new(connection->second->page_size_, | |
857 connection->second->cursor_position_, | |
858 connection->second->cursor_visible_, | |
859 false); // table_round | |
860 g_object_ref_sink(engine->table); | |
861 ibus_lookup_table_set_orientation(engine->table, | |
862 connection->second->vertical_ ? | |
863 IBUS_ORIENTATION_VERTICAL : | |
864 IBUS_ORIENTATION_HORIZONTAL); | |
865 ibus_engine_update_lookup_table(IBUS_ENGINE(engine), engine->table, | |
866 connection->second->table_visible_); | |
867 | |
868 | |
869 connection->second->SetProperties(); | |
870 | |
871 return (GObject *) engine; | |
872 } | |
873 | |
874 static void OnDestroy(IBusChromeOSEngine* chromeos_engine) { | |
875 const gchar* name = ibus_engine_get_name((IBusEngine*)chromeos_engine); | |
876 ConnectionMap::iterator connection = g_connections_->find(name); | |
877 if (connection == g_connections_->end()) { | |
878 DVLOG(1) << "Connection already destroyed, or never created: " << name; | |
879 } else { | |
880 connection->second->engine_instances_.erase(chromeos_engine); | |
881 if (connection->second->active_engine_ == chromeos_engine) { | |
882 connection->second->active_engine_ = NULL; | |
883 } | |
884 } | |
885 if (chromeos_engine->table) { | |
886 g_object_unref(chromeos_engine->table); | |
887 chromeos_engine->table = NULL; | |
888 } | |
889 if (chromeos_engine->connection && | |
890 ibus_bus_is_connected(chromeos_engine->connection->ibus_)) { | |
891 // Connection may be NULL since extension IME can be uninstalled before | |
892 // |OnDestroy| is called. | |
893 // We can't call |destroy| function without ibus-daemon connection, | |
894 // otherwise browser goes into deadlock state. | |
895 // TODO(nona): investigate the reason of dead-lock. | |
896 IBUS_OBJECT_CLASS(ibus_chromeos_engine_parent_class) | |
897 ->destroy(IBUS_OBJECT(chromeos_engine)); | |
898 } | |
899 } | |
900 | |
901 IBusEngineController::Observer* observer_; | |
902 IBusBus* ibus_; | |
903 IBusChromeOSEngine* active_engine_; | |
904 | |
905 std::string engine_id_; | |
906 std::string engine_name_; | |
907 std::string description_; | |
908 std::string language_; | |
909 std::string layout_; | |
910 std::string bus_name_; | |
911 | |
912 IBusText* preedit_text_; | |
913 int preedit_cursor_; | |
914 bool table_visible_; | |
915 bool cursor_visible_; | |
916 bool vertical_; | |
917 unsigned int page_size_; | |
918 unsigned int cursor_position_; | |
919 std::string aux_text_; | |
920 bool aux_text_visible_; | |
921 std::vector<EngineProperty*> properties_; | |
922 std::map<std::string, EngineProperty*> property_map_; | |
923 | |
924 bool active_; | |
925 bool focused_; | |
926 | |
927 std::set<IBusChromeOSEngine*> engine_instances_; | |
928 | |
929 static ConnectionMap* g_connections_; | |
930 static IBusFactory* g_factory_; | |
931 }; | |
932 | |
933 IBusEngineControllerImpl::ConnectionMap* | |
934 IBusEngineControllerImpl::g_connections_ = NULL; | |
935 | |
936 IBusFactory* IBusEngineControllerImpl::g_factory_ = NULL; | |
937 | |
938 void ibus_chromeos_engine_class_init(IBusChromeOSEngineClass *klass) { | |
939 IBusEngineControllerImpl::InitEngineClass(klass); | |
940 } | |
941 | |
942 void ibus_chromeos_engine_init(IBusChromeOSEngine* engine) { | |
943 IBusEngineControllerImpl::InitEngine(engine); | |
944 } | |
945 | |
946 #endif // HAVE_IBUS | |
947 | |
948 // static | |
949 IBusEngineController* IBusEngineController::Create(Observer* observer, | |
950 const char* engine_id, | |
951 const char* engine_name, | |
952 const char* description, | |
953 const char* language, | |
954 const char* layout) { | |
955 #if defined(HAVE_IBUS) | |
956 IBusEngineControllerImpl* connection = new IBusEngineControllerImpl(observer); | |
957 if (!connection->Init(engine_id, engine_name, description, language, | |
958 layout)) { | |
959 DVLOG(1) << "Failed to Init() IBusEngineControllerImpl. " | |
960 << "Returning NULL"; | |
961 delete connection; | |
962 connection = NULL; | |
963 } | |
964 return connection; | |
965 #else | |
966 return NULL; | |
967 #endif | |
968 } | |
969 | |
970 IBusEngineController::~IBusEngineController() { | |
971 } | |
972 | |
973 } // namespace input_method | |
974 } // namespace chromeos | |
OLD | NEW |