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 // TODO(nona): Remvoe IBusUiController |
4 | 5 |
5 #include "chrome/browser/chromeos/input_method/ibus_ui_controller.h" | 6 #include "chrome/browser/chromeos/input_method/ibus_ui_controller.h" |
6 | 7 |
7 #if defined(HAVE_IBUS) | |
8 #include <ibus.h> | |
9 #endif | |
10 | |
11 #include <sstream> | 8 #include <sstream> |
12 | 9 |
13 #include "ash/shell.h" | 10 #include "ash/shell.h" |
14 #include "base/logging.h" | 11 #include "base/logging.h" |
15 #include "base/memory/scoped_ptr.h" | 12 #include "base/memory/scoped_ptr.h" |
16 #include "base/string_util.h" | 13 #include "base/string_util.h" |
17 #include "chrome/browser/chromeos/input_method/input_method_descriptor.h" | 14 #include "chrome/browser/chromeos/input_method/input_method_descriptor.h" |
18 #include "chrome/browser/chromeos/input_method/input_method_manager.h" | 15 #include "chrome/browser/chromeos/input_method/input_method_manager.h" |
19 #include "chrome/browser/chromeos/input_method/input_method_util.h" | 16 #include "chrome/browser/chromeos/input_method/input_method_util.h" |
| 17 #include "chromeos/dbus/dbus_thread_manager.h" |
| 18 #include "chromeos/dbus/ibus/ibus_lookup_table.h" |
20 #include "ui/aura/client/aura_constants.h" | 19 #include "ui/aura/client/aura_constants.h" |
21 #include "ui/aura/root_window.h" | 20 #include "ui/aura/root_window.h" |
22 #include "ui/base/ime/input_method_ibus.h" | 21 #include "ui/base/ime/input_method_ibus.h" |
23 | 22 |
24 namespace chromeos { | 23 namespace chromeos { |
25 namespace input_method { | 24 namespace input_method { |
26 namespace { | 25 namespace { |
27 | 26 |
28 bool IsActive(const std::string& input_method_id, | 27 // Returns pointer of IBusPanelService. This function returns NULL if it is not |
29 const InputMethodDescriptors* descriptors) { | 28 // ready. |
30 for (size_t i = 0; i < descriptors->size(); ++i) { | 29 ibus::IBusPanelService* GetIBusPanelService() { |
31 if (descriptors->at(i).id() == input_method_id) { | 30 return DBusThreadManager::Get()->GetIBusPanelService(); |
32 return true; | 31 } |
33 } | 32 |
34 } | 33 // Returns a ui::InputMethodIBus object which is associated with the root |
35 return false; | 34 // window. Returns NULL if the Ash shell has already been destructed. |
| 35 static ui::InputMethodIBus* GetChromeInputMethod() { |
| 36 if (!ash::Shell::HasInstance()) |
| 37 return NULL; |
| 38 aura::Window* root_window = ash::Shell::GetPrimaryRootWindow(); |
| 39 if (!root_window) |
| 40 return NULL; |
| 41 return static_cast<ui::InputMethodIBus*>(root_window->GetProperty( |
| 42 aura::client::kRootWindowInputMethodKey)); |
36 } | 43 } |
37 | 44 |
38 } // namespace | 45 } // namespace |
39 | 46 |
| 47 // A class for customizing the behavior of ui::InputMethodIBus for Chrome OS. |
| 48 class IBusChromeOSClientImpl : public ui::internal::IBusClient { |
| 49 public: |
| 50 explicit IBusChromeOSClientImpl(IBusUiController* ui) : ui_(ui) {} |
| 51 |
| 52 // ui::IBusClient override. |
| 53 virtual InputMethodType GetInputMethodType() OVERRIDE { |
| 54 InputMethodManager* manager = InputMethodManager::GetInstance(); |
| 55 DCHECK(manager); |
| 56 return InputMethodUtil::IsKeyboardLayout( |
| 57 manager->GetCurrentInputMethod().id()) ? |
| 58 INPUT_METHOD_XKB_LAYOUT : INPUT_METHOD_NORMAL; |
| 59 } |
| 60 |
| 61 virtual void SetCursorLocation(const gfx::Rect& cursor_location, |
| 62 const gfx::Rect& composition_head) OVERRIDE { |
| 63 if (!ui_) |
| 64 return; |
| 65 // We don't have to call ibus_input_context_set_cursor_location() on |
| 66 // Chrome OS because the candidate window for IBus is integrated with |
| 67 // Chrome. |
| 68 ui_->SetCursorLocation(cursor_location, composition_head); |
| 69 } |
| 70 |
| 71 void set_ui(IBusUiController* ui) { |
| 72 ui_ = ui; |
| 73 } |
| 74 |
| 75 private: |
| 76 IBusUiController* ui_; |
| 77 DISALLOW_COPY_AND_ASSIGN(IBusChromeOSClientImpl); |
| 78 }; |
| 79 |
40 InputMethodLookupTable::InputMethodLookupTable() | 80 InputMethodLookupTable::InputMethodLookupTable() |
41 : visible(false), | 81 : visible(false), |
42 cursor_absolute_index(0), | 82 cursor_absolute_index(0), |
43 page_size(0), | 83 page_size(0), |
44 orientation(kHorizontal) { | 84 orientation(kHorizontal) { |
45 } | 85 } |
46 | 86 |
47 InputMethodLookupTable::~InputMethodLookupTable() { | 87 InputMethodLookupTable::~InputMethodLookupTable() { |
48 } | 88 } |
49 | 89 |
50 std::string InputMethodLookupTable::ToString() const { | 90 std::string InputMethodLookupTable::ToString() const { |
51 std::stringstream stream; | 91 std::stringstream stream; |
52 stream << "visible: " << visible << "\n"; | 92 stream << "visible: " << visible << "\n"; |
53 stream << "cursor_absolute_index: " << cursor_absolute_index << "\n"; | 93 stream << "cursor_absolute_index: " << cursor_absolute_index << "\n"; |
54 stream << "page_size: " << page_size << "\n"; | 94 stream << "page_size: " << page_size << "\n"; |
55 stream << "orientation: " << orientation << "\n"; | 95 stream << "orientation: " << orientation << "\n"; |
56 stream << "candidates:"; | 96 stream << "candidates:"; |
57 for (size_t i = 0; i < candidates.size(); ++i) { | 97 for (size_t i = 0; i < candidates.size(); ++i) { |
58 stream << " [" << candidates[i] << "]"; | 98 stream << " [" << candidates[i] << "]"; |
59 } | 99 } |
60 stream << "\nlabels:"; | 100 stream << "\nlabels:"; |
61 for (size_t i = 0; i < labels.size(); ++i) { | 101 for (size_t i = 0; i < labels.size(); ++i) { |
62 stream << " [" << labels[i] << "]"; | 102 stream << " [" << labels[i] << "]"; |
63 } | 103 } |
64 return stream.str(); | 104 return stream.str(); |
65 } | 105 } |
66 | 106 |
67 #if defined(HAVE_IBUS) | |
68 | |
69 // Returns an string representation of |table| for debugging. | |
70 std::string IBusLookupTableToString(IBusLookupTable* table) { | |
71 std::stringstream stream; | |
72 stream << "page_size: " << table->page_size << "\n"; | |
73 stream << "cursor_pos: " << table->cursor_pos << "\n"; | |
74 stream << "cursor_visible: " << table->cursor_visible << "\n"; | |
75 stream << "round: " << table->round << "\n"; | |
76 stream << "orientation: " << table->orientation << "\n"; | |
77 stream << "candidates:"; | |
78 for (int i = 0; ; i++) { | |
79 IBusText *text = ibus_lookup_table_get_candidate(table, i); | |
80 if (!text) { | |
81 break; | |
82 } | |
83 stream << " " << text->text; | |
84 } | |
85 return stream.str(); | |
86 } | |
87 | |
88 // The real implementation of the IBusUiController. | 107 // The real implementation of the IBusUiController. |
89 class IBusUiControllerImpl : public IBusUiController { | 108 IBusUiController::IBusUiController() { |
90 public: | 109 ui::InputMethodIBus* input_method = GetChromeInputMethod(); |
91 IBusUiControllerImpl() | 110 DCHECK(input_method); |
92 : ibus_(NULL), | 111 input_method->set_ibus_client(scoped_ptr<ui::internal::IBusClient>( |
93 ibus_panel_service_(NULL) { | 112 new IBusChromeOSClientImpl(this)).Pass()); |
94 ui::InputMethodIBus* input_method = GetChromeInputMethod(); | |
95 DCHECK(input_method); | |
96 input_method->set_ibus_client(scoped_ptr<ui::internal::IBusClient>( | |
97 new IBusChromeOSClientImpl(this)).Pass()); | |
98 } | |
99 | |
100 ~IBusUiControllerImpl() { | |
101 ui::InputMethodIBus* input_method = GetChromeInputMethod(); | |
102 if (input_method) { | |
103 ui::internal::IBusClient* client = input_method->ibus_client(); | |
104 // We assume that no objects other than |this| set an IBus client. | |
105 DCHECK(client); | |
106 static_cast<IBusChromeOSClientImpl*>(client)->set_ui(NULL); | |
107 } | |
108 // ibus_panel_service_ depends on ibus_, thus unref it first. | |
109 if (ibus_panel_service_) { | |
110 DisconnectPanelServiceSignals(); | |
111 g_object_unref(ibus_panel_service_); | |
112 } | |
113 if (ibus_) { | |
114 DisconnectIBusSignals(); | |
115 g_object_unref(ibus_); | |
116 } | |
117 } | |
118 | |
119 // Creates IBusBus object if it's not created yet, and tries to connect to | |
120 // ibus-daemon. Returns true if IBusBus is successfully connected to the | |
121 // daemon. | |
122 bool ConnectToIBus() { | |
123 if (ibus_) { | |
124 return true; | |
125 } | |
126 ibus_init(); | |
127 ibus_ = ibus_bus_new(); | |
128 CHECK(ibus_) << "ibus_bus_new() failed. Out of memory?"; | |
129 | |
130 bool result = false; | |
131 // Check the IBus connection status. | |
132 if (ibus_bus_is_connected(ibus_)) { | |
133 DVLOG(1) << "ibus_bus_is_connected(). IBus connection is ready."; | |
134 FOR_EACH_OBSERVER(Observer, observers_, OnConnectionChange(true)); | |
135 result = true; | |
136 } | |
137 | |
138 // Start listening the gobject signals regardless of the bus connection | |
139 // status. | |
140 ConnectIBusSignals(); | |
141 return result; | |
142 } | |
143 | |
144 // Creates IBusPanelService object if |ibus_| is already connected. | |
145 bool MaybeRestorePanelService() { | |
146 if (!ibus_ || !ibus_bus_is_connected(ibus_)) { | |
147 return false; | |
148 } | |
149 | |
150 if (ibus_panel_service_) { | |
151 DVLOG(1) << "IBusPanelService is already available. Remove it first."; | |
152 g_object_set_data(G_OBJECT(ibus_), kPanelObjectKey, NULL); | |
153 g_object_unref(ibus_panel_service_); | |
154 ibus_panel_service_ = NULL; | |
155 } | |
156 | |
157 // Create an IBusPanelService object. | |
158 GDBusConnection* ibus_connection = ibus_bus_get_connection(ibus_); | |
159 if (!ibus_connection) { | |
160 DVLOG(1) << "ibus_bus_get_connection() failed"; | |
161 return false; | |
162 } | |
163 ibus_panel_service_ = ibus_panel_service_new(ibus_connection); | |
164 if (!ibus_panel_service_) { | |
165 DVLOG(1) << "ibus_chromeos_panel_service_new() failed"; | |
166 return false; | |
167 } | |
168 ConnectPanelServiceSignals(); | |
169 g_object_set_data(G_OBJECT(ibus_), kPanelObjectKey, ibus_panel_service_); | |
170 DVLOG(1) << "IBusPanelService object is successfully (re-)created."; | |
171 | |
172 // Request the well-known name *asynchronously*. | |
173 ibus_bus_request_name_async(ibus_, | |
174 IBUS_SERVICE_PANEL, | |
175 0 /* flags */, | |
176 -1 /* timeout */, | |
177 NULL /* cancellable */, | |
178 RequestNameCallback, | |
179 g_object_ref(ibus_)); | |
180 return true; | |
181 } | |
182 | |
183 // IBusUiController override. | |
184 virtual void NotifyCandidateClicked(int index, | |
185 int button, | |
186 int flags) OVERRIDE { | |
187 if (!ibus_ || !ibus_bus_is_connected(ibus_)) { | |
188 DVLOG(1) << "NotifyCandidateClicked: bus is not connected."; | |
189 return; | |
190 } | |
191 if (!ibus_panel_service_) { | |
192 DVLOG(1) << "NotifyCandidateClicked: panel service is not available."; | |
193 return; | |
194 } | |
195 | |
196 /* Send a D-Bus signal to ibus-daemon *asynchronously*. */ | |
197 ibus_panel_service_candidate_clicked(ibus_panel_service_, | |
198 index, | |
199 button, | |
200 flags); | |
201 } | |
202 | |
203 // IBusUiController override. | |
204 virtual void NotifyCursorUp() OVERRIDE { | |
205 if (!ibus_ || !ibus_bus_is_connected(ibus_)) { | |
206 DVLOG(1) << "NotifyCursorUp: bus is not connected."; | |
207 return; | |
208 } | |
209 if (!ibus_panel_service_) { | |
210 DVLOG(1) << "NotifyCursorUp: panel service is not available."; | |
211 return; | |
212 } | |
213 | |
214 /* Send a D-Bus signal to ibus-daemon *asynchronously*. */ | |
215 ibus_panel_service_cursor_up(ibus_panel_service_); | |
216 } | |
217 | |
218 // IBusUiController override. | |
219 virtual void NotifyCursorDown() OVERRIDE { | |
220 if (!ibus_ || !ibus_bus_is_connected(ibus_)) { | |
221 DVLOG(1) << "NotifyCursorDown: bus is not connected."; | |
222 return; | |
223 } | |
224 if (!ibus_panel_service_) { | |
225 DVLOG(1) << "NotifyCursorDown: panel service is not available."; | |
226 return; | |
227 } | |
228 /* Send a D-Bus signal to ibus-daemon *asynchronously*. */ | |
229 ibus_panel_service_cursor_down(ibus_panel_service_); | |
230 } | |
231 | |
232 // IBusUiController override. | |
233 virtual void NotifyPageUp() OVERRIDE { | |
234 if (!ibus_ || !ibus_bus_is_connected(ibus_)) { | |
235 DVLOG(1) << "NotifyPageUp: bus is not connected."; | |
236 return; | |
237 } | |
238 if (!ibus_panel_service_) { | |
239 DVLOG(1) << "NotifyPageUp: panel service is not available."; | |
240 return; | |
241 } | |
242 | |
243 /* Send a D-Bus signal to ibus-daemon *asynchronously*. */ | |
244 ibus_panel_service_page_up(ibus_panel_service_); | |
245 } | |
246 | |
247 // IBusUiController override. | |
248 virtual void NotifyPageDown() OVERRIDE { | |
249 if (!ibus_ || !ibus_bus_is_connected(ibus_)) { | |
250 DVLOG(1) << "NotifyPageDown: bus is not connected."; | |
251 return; | |
252 } | |
253 if (!ibus_panel_service_) { | |
254 DVLOG(1) << "NotifyPageDown: panel service is not available."; | |
255 return; | |
256 } | |
257 | |
258 /* Send a D-Bus signal to ibus-daemon *asynchronously*. */ | |
259 ibus_panel_service_page_down(ibus_panel_service_); | |
260 } | |
261 | |
262 // IBusUiController override. | |
263 virtual void Connect() OVERRIDE { | |
264 // It's totally fine if ConnectToIBus() fails here, as we'll get | |
265 // "connected" gobject signal once the connection becomes ready. | |
266 if (ConnectToIBus()) | |
267 MaybeRestorePanelService(); | |
268 } | |
269 | |
270 // IBusUiController override. | |
271 virtual void AddObserver(Observer* observer) OVERRIDE { | |
272 observers_.AddObserver(observer); | |
273 } | |
274 | |
275 // IBusUiController override. | |
276 virtual void RemoveObserver(Observer* observer) OVERRIDE { | |
277 observers_.RemoveObserver(observer); | |
278 } | |
279 | |
280 private: | |
281 // A class for customizing the behavior of ui::InputMethodIBus for Chrome OS. | |
282 class IBusChromeOSClientImpl : public ui::internal::IBusClient { | |
283 public: | |
284 explicit IBusChromeOSClientImpl(IBusUiControllerImpl* ui) | |
285 : ui_(ui) { | |
286 } | |
287 | |
288 // ui::IBusClient override. | |
289 virtual InputMethodType GetInputMethodType() OVERRIDE { | |
290 const std::string current_input_method_id = GetCurrentInputMethodId(); | |
291 return InputMethodUtil::IsKeyboardLayout(current_input_method_id) ? | |
292 INPUT_METHOD_XKB_LAYOUT : INPUT_METHOD_NORMAL; | |
293 } | |
294 | |
295 virtual void SetCursorLocation(const gfx::Rect& cursor_location, | |
296 const gfx::Rect& composition_head) OVERRIDE { | |
297 if (!ui_) | |
298 return; | |
299 // We don't have to call ibus_input_context_set_cursor_location() on | |
300 // Chrome OS because the candidate window for IBus is integrated with | |
301 // Chrome. | |
302 ui_->SetCursorLocation(NULL, cursor_location, composition_head); | |
303 } | |
304 | |
305 void set_ui(IBusUiControllerImpl* ui) { | |
306 ui_ = ui; | |
307 } | |
308 | |
309 private: | |
310 std::string GetCurrentInputMethodId() { | |
311 InputMethodManager* manager = InputMethodManager::GetInstance(); | |
312 return manager->GetCurrentInputMethod().id(); | |
313 } | |
314 | |
315 IBusUiControllerImpl* ui_; | |
316 }; | |
317 | |
318 // Returns a ui::InputMethodIBus object which is associated with the root | |
319 // window. Returns NULL if the Ash shell has already been destructed. | |
320 static ui::InputMethodIBus* GetChromeInputMethod() { | |
321 if (!ash::Shell::HasInstance()) | |
322 return NULL; | |
323 aura::Window* root_window = ash::Shell::GetPrimaryRootWindow(); | |
324 if (!root_window) | |
325 return NULL; | |
326 return static_cast<ui::InputMethodIBus*>(root_window->GetProperty( | |
327 aura::client::kRootWindowInputMethodKey)); | |
328 } | |
329 | |
330 // Functions that end with Thunk are used to deal with glib callbacks. | |
331 // | |
332 // Note that we cannot use CHROMEG_CALLBACK_0() here as we'll define | |
333 // IBusBusConnected() inline. If we are to define the function outside | |
334 // of the class definition, we should use CHROMEG_CALLBACK_0() here. | |
335 // | |
336 // CHROMEG_CALLBACK_0(Impl, | |
337 // void, IBusBusConnected, IBusBus*); | |
338 static void IBusBusConnectedThunk(IBusBus* sender, gpointer userdata) { | |
339 return reinterpret_cast<IBusUiControllerImpl*>(userdata) | |
340 ->IBusBusConnected(sender); | |
341 } | |
342 static void IBusBusDisconnectedThunk(IBusBus* sender, gpointer userdata) { | |
343 return reinterpret_cast<IBusUiControllerImpl*>(userdata) | |
344 ->IBusBusDisconnected(sender); | |
345 } | |
346 static void HideAuxiliaryTextThunk(IBusPanelService* sender, | |
347 gpointer userdata) { | |
348 return reinterpret_cast<IBusUiControllerImpl*>(userdata) | |
349 ->HideAuxiliaryText(sender); | |
350 } | |
351 static void HideLookupTableThunk(IBusPanelService* sender, | |
352 gpointer userdata) { | |
353 return reinterpret_cast<IBusUiControllerImpl*>(userdata) | |
354 ->HideLookupTable(sender); | |
355 } | |
356 static void UpdateAuxiliaryTextThunk(IBusPanelService* sender, | |
357 IBusText* text, gboolean visible, | |
358 gpointer userdata) { | |
359 return reinterpret_cast<IBusUiControllerImpl*>(userdata) | |
360 ->UpdateAuxiliaryText(sender, text, visible); | |
361 } | |
362 static void SetCursorLocationThunk(IBusPanelService* sender, | |
363 gint x, gint y, gint width, gint height, | |
364 gpointer userdata) { | |
365 return reinterpret_cast<IBusUiControllerImpl*>(userdata) | |
366 ->SetCursorLocation(sender, gfx::Rect(x, y, width, height), | |
367 gfx::Rect()); | |
368 } | |
369 static void UpdateLookupTableThunk(IBusPanelService* sender, | |
370 IBusLookupTable* table, gboolean visible, | |
371 gpointer userdata) { | |
372 return reinterpret_cast<IBusUiControllerImpl*>(userdata) | |
373 ->UpdateLookupTable(sender, table, visible); | |
374 } | |
375 static void UpdatePreeditTextThunk(IBusPanelService* sender, | |
376 IBusText* text, | |
377 guint cursor_pos, | |
378 gboolean visible, | |
379 gpointer userdata) { | |
380 return reinterpret_cast<IBusUiControllerImpl*>(userdata) | |
381 ->UpdatePreeditText(sender, text, cursor_pos, visible); | |
382 } | |
383 static void HidePreeditTextThunk(IBusPanelService* sender, | |
384 gpointer userdata) { | |
385 return reinterpret_cast<IBusUiControllerImpl*>(userdata) | |
386 ->HidePreeditText(sender); | |
387 } | |
388 | |
389 | |
390 // Installs gobject signal handlers to |ibus_|. | |
391 void ConnectIBusSignals() { | |
392 if (!ibus_) { | |
393 return; | |
394 } | |
395 g_signal_connect(ibus_, | |
396 "connected", | |
397 G_CALLBACK(IBusBusConnectedThunk), | |
398 this); | |
399 g_signal_connect(ibus_, | |
400 "disconnected", | |
401 G_CALLBACK(IBusBusDisconnectedThunk), | |
402 this); | |
403 } | |
404 | |
405 // Removes gobject signal handlers from |ibus_|. | |
406 void DisconnectIBusSignals() { | |
407 if (!ibus_) { | |
408 return; | |
409 } | |
410 g_signal_handlers_disconnect_by_func( | |
411 ibus_, | |
412 reinterpret_cast<gpointer>(G_CALLBACK(IBusBusConnectedThunk)), | |
413 this); | |
414 g_signal_handlers_disconnect_by_func( | |
415 ibus_, | |
416 reinterpret_cast<gpointer>(G_CALLBACK(IBusBusDisconnectedThunk)), | |
417 this); | |
418 } | |
419 | |
420 // Installs gobject signal handlers to |ibus_panel_service_|. | |
421 void ConnectPanelServiceSignals() { | |
422 if (!ibus_panel_service_) { | |
423 return; | |
424 } | |
425 g_signal_connect(ibus_panel_service_, | |
426 "hide-auxiliary-text", | |
427 G_CALLBACK(HideAuxiliaryTextThunk), | |
428 this); | |
429 g_signal_connect(ibus_panel_service_, | |
430 "hide-lookup-table", | |
431 G_CALLBACK(HideLookupTableThunk), | |
432 this); | |
433 g_signal_connect(ibus_panel_service_, | |
434 "update-auxiliary-text", | |
435 G_CALLBACK(UpdateAuxiliaryTextThunk), | |
436 this); | |
437 g_signal_connect(ibus_panel_service_, | |
438 "set-cursor-location", | |
439 G_CALLBACK(SetCursorLocationThunk), | |
440 this); | |
441 g_signal_connect(ibus_panel_service_, | |
442 "update-lookup-table", | |
443 G_CALLBACK(UpdateLookupTableThunk), | |
444 this); | |
445 g_signal_connect(ibus_panel_service_, | |
446 "update-preedit-text", | |
447 G_CALLBACK(UpdatePreeditTextThunk), | |
448 this); | |
449 g_signal_connect(ibus_panel_service_, | |
450 "hide-preedit-text", | |
451 G_CALLBACK(HidePreeditTextThunk), | |
452 this); | |
453 } | |
454 | |
455 // Removes gobject signal handlers from |ibus_panel_service_|. | |
456 void DisconnectPanelServiceSignals() { | |
457 if (!ibus_panel_service_) { | |
458 return; | |
459 } | |
460 g_signal_handlers_disconnect_by_func( | |
461 ibus_panel_service_, | |
462 reinterpret_cast<gpointer>(HideAuxiliaryTextThunk), | |
463 this); | |
464 g_signal_handlers_disconnect_by_func( | |
465 ibus_panel_service_, | |
466 reinterpret_cast<gpointer>(HideLookupTableThunk), | |
467 this); | |
468 g_signal_handlers_disconnect_by_func( | |
469 ibus_panel_service_, | |
470 reinterpret_cast<gpointer>(UpdateAuxiliaryTextThunk), | |
471 this); | |
472 g_signal_handlers_disconnect_by_func( | |
473 ibus_panel_service_, | |
474 reinterpret_cast<gpointer>(SetCursorLocationThunk), | |
475 this); | |
476 g_signal_handlers_disconnect_by_func( | |
477 ibus_panel_service_, | |
478 reinterpret_cast<gpointer>(UpdateLookupTableThunk), | |
479 this); | |
480 g_signal_handlers_disconnect_by_func( | |
481 ibus_panel_service_, | |
482 reinterpret_cast<gpointer>(UpdatePreeditTextThunk), | |
483 this); | |
484 g_signal_handlers_disconnect_by_func( | |
485 ibus_panel_service_, | |
486 reinterpret_cast<gpointer>(HidePreeditTextThunk), | |
487 this); | |
488 } | |
489 | |
490 // Handles "connected" signal from ibus-daemon. | |
491 void IBusBusConnected(IBusBus* bus) { | |
492 DVLOG(1) << "IBus connection is recovered."; | |
493 if (!MaybeRestorePanelService()) { | |
494 DVLOG(1) << "MaybeRestorePanelService() failed"; | |
495 return; | |
496 } | |
497 | |
498 FOR_EACH_OBSERVER(Observer, observers_, OnConnectionChange(true)); | |
499 } | |
500 | |
501 // Handles "disconnected" signal from ibus-daemon. Releases the | |
502 // |ibus_panel_service_| object since the connection the service has will be | |
503 // destroyed soon. | |
504 void IBusBusDisconnected(IBusBus* bus) { | |
505 DVLOG(1) << "IBus connection is terminated."; | |
506 if (ibus_panel_service_) { | |
507 DisconnectPanelServiceSignals(); | |
508 // Since the connection being disconnected is currently mutex-locked, | |
509 // we can't unref the panel service object directly here. Because when the | |
510 // service object is deleted, the connection, which the service also has, | |
511 // will be locked again. To avoid deadlock, we use g_idle_add instead. | |
512 g_object_set_data(G_OBJECT(ibus_), kPanelObjectKey, NULL); | |
513 g_idle_add(ReleasePanelService, ibus_panel_service_); | |
514 ibus_panel_service_ = NULL; | |
515 } | |
516 | |
517 FOR_EACH_OBSERVER(Observer, observers_, OnConnectionChange(false)); | |
518 } | |
519 | |
520 // Releases |ibus_panel_service_|. See the comment above. | |
521 static gboolean ReleasePanelService(gpointer user_data) { | |
522 g_return_val_if_fail(IBUS_IS_PANEL_SERVICE(user_data), FALSE); | |
523 g_object_unref(user_data); | |
524 return FALSE; // stop the idle timer. | |
525 } | |
526 | |
527 // Handles IBusPanelService's |HideAuxiliaryText| method call. | |
528 void HideAuxiliaryText(IBusPanelService* panel) { | |
529 FOR_EACH_OBSERVER(Observer, observers_, OnHideAuxiliaryText()); | |
530 } | |
531 | |
532 // Handles IBusPanelService's |HideLookupTable| method call. | |
533 void HideLookupTable(IBusPanelService *panel) { | |
534 FOR_EACH_OBSERVER(Observer, observers_, OnHideLookupTable()); | |
535 } | |
536 | |
537 // Handles IBusPanelService's |UpdateAuxiliaryText| method call. | |
538 void UpdateAuxiliaryText(IBusPanelService* panel, | |
539 IBusText* text, | |
540 gboolean visible) { | |
541 g_return_if_fail(text); | |
542 g_return_if_fail(text->text); | |
543 // Convert IBusText to a std::string. IBusText is an attributed text, | |
544 const std::string simple_text = text->text; | |
545 FOR_EACH_OBSERVER(Observer, observers_, | |
546 OnUpdateAuxiliaryText(simple_text, visible == TRUE)); | |
547 } | |
548 | |
549 // Handles IBusPanelService's |SetCursorLocation| method call. | |
550 void SetCursorLocation(IBusPanelService *panel, | |
551 const gfx::Rect& cursor_location, | |
552 const gfx::Rect& composition_head) { | |
553 // Note: |panel| might be NULL. See IBusChromeOSClientImpl above. | |
554 FOR_EACH_OBSERVER(Observer, observers_, | |
555 OnSetCursorLocation(cursor_location, composition_head)); | |
556 } | |
557 | |
558 // Handles IBusPanelService's |UpdatePreeditText| method call. | |
559 void UpdatePreeditText(IBusPanelService *panel, | |
560 IBusText *text, | |
561 guint cursor_pos, | |
562 gboolean visible) { | |
563 FOR_EACH_OBSERVER(Observer, observers_, | |
564 OnUpdatePreeditText(text->text, cursor_pos, visible)); | |
565 } | |
566 | |
567 // Handles IBusPanelService's |UpdatePreeditText| method call. | |
568 void HidePreeditText(IBusPanelService *panel) { | |
569 FOR_EACH_OBSERVER(Observer, observers_, OnHidePreeditText()); | |
570 } | |
571 | |
572 // Handles IBusPanelService's |UpdateLookupTable| method call. | |
573 void UpdateLookupTable(IBusPanelService *panel, | |
574 IBusLookupTable *table, | |
575 gboolean visible) { | |
576 g_return_if_fail(table); | |
577 | |
578 InputMethodLookupTable lookup_table; | |
579 lookup_table.visible = (visible == TRUE); | |
580 | |
581 // Copy the orientation information. | |
582 const gint orientation = ibus_lookup_table_get_orientation(table); | |
583 if (orientation == IBUS_ORIENTATION_VERTICAL) { | |
584 lookup_table.orientation = InputMethodLookupTable::kVertical; | |
585 } else if (orientation == IBUS_ORIENTATION_HORIZONTAL) { | |
586 lookup_table.orientation = InputMethodLookupTable::kHorizontal; | |
587 } | |
588 | |
589 // The function ibus_serializable_get_attachment had been changed | |
590 // to use GVariant by the commit | |
591 // https://github.com/ibus/ibus/commit/ac9dfac13cef34288440a2ecdf067cd827fb2
f8f | |
592 GVariant* variant = ibus_serializable_get_attachment( | |
593 IBUS_SERIALIZABLE(table), "show_window_at_composition"); | |
594 if (variant) | |
595 lookup_table.show_at_composition_head = !!g_variant_get_boolean(variant); | |
596 else | |
597 lookup_table.show_at_composition_head = false; | |
598 | |
599 // Copy candidates and annotations to |lookup_table|. | |
600 for (int i = 0; ; i++) { | |
601 IBusText *text = ibus_lookup_table_get_candidate(table, i); | |
602 if (!text) | |
603 break; | |
604 lookup_table.candidates.push_back(text->text); | |
605 | |
606 IBusText *label = ibus_lookup_table_get_label(table, i); | |
607 if (label) | |
608 lookup_table.labels.push_back(label->text); | |
609 | |
610 GVariant* annotation_variant = | |
611 ibus_serializable_get_attachment(IBUS_SERIALIZABLE(text), | |
612 "annotation"); | |
613 lookup_table.annotations.push_back(""); | |
614 if (annotation_variant) { | |
615 const gchar* annotation = | |
616 g_variant_get_string(annotation_variant, NULL); | |
617 if (annotation) | |
618 lookup_table.annotations[i] = annotation; | |
619 } | |
620 | |
621 GVariant* description_title_variant = | |
622 ibus_serializable_get_attachment(IBUS_SERIALIZABLE(text), | |
623 "description_title"); | |
624 InputMethodLookupTable::Description description; | |
625 if (description_title_variant) { | |
626 const gchar* description_title = | |
627 g_variant_get_string(description_title_variant, NULL); | |
628 if (description_title) | |
629 description.title = description_title; | |
630 | |
631 } | |
632 | |
633 GVariant* description_body_variant = | |
634 ibus_serializable_get_attachment(IBUS_SERIALIZABLE(text), | |
635 "description_body"); | |
636 if (description_body_variant) { | |
637 const gchar* description_body = | |
638 g_variant_get_string(description_body_variant, NULL); | |
639 if (description_body) | |
640 description.body = description_body; | |
641 } | |
642 lookup_table.descriptions.push_back(description); | |
643 } | |
644 DCHECK_EQ(lookup_table.candidates.size(), | |
645 lookup_table.annotations.size()); | |
646 | |
647 lookup_table.cursor_absolute_index = | |
648 ibus_lookup_table_get_cursor_pos(table); | |
649 lookup_table.page_size = ibus_lookup_table_get_page_size(table); | |
650 // Ensure that the page_size is non-zero to avoid div-by-zero error. | |
651 if (lookup_table.page_size <= 0) { | |
652 DVLOG(1) << "Invalid page size: " << lookup_table.page_size; | |
653 lookup_table.page_size = 1; | |
654 } | |
655 | |
656 FOR_EACH_OBSERVER(Observer, observers_, | |
657 OnUpdateLookupTable(lookup_table)); | |
658 } | |
659 | |
660 // A callback function that will be called when ibus_bus_request_name_async() | |
661 // request is finished. | |
662 static void RequestNameCallback(GObject* source_object, | |
663 GAsyncResult* res, | |
664 gpointer user_data) { | |
665 IBusBus* bus = IBUS_BUS(user_data); | |
666 g_return_if_fail(bus); | |
667 | |
668 GError* error = NULL; | |
669 const guint service_id = | |
670 ibus_bus_request_name_async_finish(bus, res, &error); | |
671 | |
672 if (!service_id) { | |
673 std::string message = "(unknown error)"; | |
674 if (error && error->message) { | |
675 message = error->message; | |
676 } | |
677 DVLOG(1) << "Failed to register the panel service: " << message; | |
678 } else { | |
679 DVLOG(1) << "The panel service is registered: ID=" << service_id; | |
680 } | |
681 | |
682 if (error) { | |
683 g_error_free(error); | |
684 } | |
685 g_object_unref(bus); | |
686 } | |
687 | |
688 IBusBus* ibus_; | |
689 IBusPanelService* ibus_panel_service_; | |
690 ObserverList<Observer> observers_; | |
691 }; | |
692 #endif // defined(HAVE_IBUS) | |
693 | |
694 // The stub implementation is used if IBus is not present. | |
695 // | |
696 // Note that this class is intentionally built even if HAVE_IBUS is | |
697 // defined so that we can easily tell build breakage when we change the | |
698 // IBusUiControllerImpl but forget to update the stub implementation. | |
699 class IBusUiControllerStubImpl : public IBusUiController { | |
700 public: | |
701 IBusUiControllerStubImpl() { | |
702 } | |
703 | |
704 virtual void Connect() { | |
705 } | |
706 | |
707 virtual void AddObserver(Observer* observer) { | |
708 } | |
709 | |
710 virtual void RemoveObserver(Observer* observer) { | |
711 } | |
712 | |
713 virtual void NotifyCandidateClicked(int index, int button, int flags) { | |
714 } | |
715 | |
716 virtual void NotifyCursorUp() { | |
717 } | |
718 | |
719 virtual void NotifyCursorDown() { | |
720 } | |
721 | |
722 virtual void NotifyPageUp() { | |
723 } | |
724 | |
725 virtual void NotifyPageDown() { | |
726 } | |
727 }; | |
728 | |
729 IBusUiController* IBusUiController::Create() { | |
730 #if defined(HAVE_IBUS) | |
731 return new IBusUiControllerImpl; | |
732 #else | |
733 return new IBusUiControllerStubImpl; | |
734 #endif | |
735 } | 113 } |
736 | 114 |
737 IBusUiController::~IBusUiController() { | 115 IBusUiController::~IBusUiController() { |
| 116 ui::InputMethodIBus* input_method = GetChromeInputMethod(); |
| 117 if (input_method) { |
| 118 ui::internal::IBusClient* client = input_method->ibus_client(); |
| 119 // We assume that no objects other than |this| set an IBus client. |
| 120 DCHECK(client); |
| 121 static_cast<IBusChromeOSClientImpl*>(client)->set_ui(NULL); |
| 122 } |
738 } | 123 } |
739 | 124 |
740 bool IsActiveForTesting(const std::string& input_method_id, | 125 void IBusUiController::NotifyCandidateClicked(int index, int button, |
741 const InputMethodDescriptors* descriptors) { | 126 int flags) { |
742 return IsActive(input_method_id, descriptors); | 127 ibus::IBusPanelService* service = GetIBusPanelService(); |
| 128 if (service) { |
| 129 service->CandidateClicked( |
| 130 index, |
| 131 static_cast<ibus::IBusMouseButton>(button), |
| 132 flags); |
| 133 } |
| 134 } |
| 135 |
| 136 void IBusUiController::NotifyCursorUp() { |
| 137 ibus::IBusPanelService* service = GetIBusPanelService(); |
| 138 if (service) |
| 139 service->CursorUp(); |
| 140 } |
| 141 |
| 142 void IBusUiController::NotifyCursorDown() { |
| 143 ibus::IBusPanelService* service = GetIBusPanelService(); |
| 144 if (service) |
| 145 service->CursorDown(); |
| 146 } |
| 147 |
| 148 void IBusUiController::NotifyPageUp() { |
| 149 ibus::IBusPanelService* service = GetIBusPanelService(); |
| 150 if (service) |
| 151 service->PageUp(); |
| 152 } |
| 153 |
| 154 void IBusUiController::NotifyPageDown() { |
| 155 ibus::IBusPanelService* service = GetIBusPanelService(); |
| 156 if (service) |
| 157 service->PageDown(); |
| 158 } |
| 159 |
| 160 void IBusUiController::AddObserver(Observer* observer) { |
| 161 observers_.AddObserver(observer); |
| 162 } |
| 163 |
| 164 void IBusUiController::RemoveObserver(Observer* observer) { |
| 165 observers_.RemoveObserver(observer); |
| 166 } |
| 167 |
| 168 void IBusUiController::HideAuxiliaryText() { |
| 169 FOR_EACH_OBSERVER(Observer, observers_, OnHideAuxiliaryText()); |
| 170 } |
| 171 |
| 172 void IBusUiController::HideLookupTable() { |
| 173 FOR_EACH_OBSERVER(Observer, observers_, OnHideLookupTable()); |
| 174 } |
| 175 |
| 176 void IBusUiController::UpdateAuxiliaryText(const std::string& text, |
| 177 bool visible) { |
| 178 FOR_EACH_OBSERVER(Observer, observers_, |
| 179 OnUpdateAuxiliaryText(text, visible)); |
| 180 } |
| 181 |
| 182 void IBusUiController::SetCursorLocation(const gfx::Rect& cursor_location, |
| 183 const gfx::Rect& composition_head) { |
| 184 FOR_EACH_OBSERVER(Observer, observers_, |
| 185 OnSetCursorLocation(cursor_location, composition_head)); |
| 186 } |
| 187 |
| 188 void IBusUiController::UpdatePreeditText(const std::string& text, |
| 189 uint32 cursor_pos, |
| 190 bool visible) { |
| 191 FOR_EACH_OBSERVER(Observer, observers_, |
| 192 OnUpdatePreeditText(text, cursor_pos, visible)); |
| 193 } |
| 194 |
| 195 void IBusUiController::HidePreeditText() { |
| 196 FOR_EACH_OBSERVER(Observer, observers_, OnHidePreeditText()); |
| 197 } |
| 198 |
| 199 void IBusUiController::UpdateLookupTable(const ibus::IBusLookupTable& table, |
| 200 bool visible) { |
| 201 // TODO(nona): Use ibus::IBusLookupTable instead. |
| 202 InputMethodLookupTable lookup_table; |
| 203 lookup_table.visible = visible; |
| 204 |
| 205 // Copy the orientation information. |
| 206 if (table.orientation() == |
| 207 ibus::IBusLookupTable::IBUS_LOOKUP_TABLE_ORIENTATION_VERTICAL) { |
| 208 lookup_table.orientation = InputMethodLookupTable::kVertical; |
| 209 } else { |
| 210 lookup_table.orientation = InputMethodLookupTable::kHorizontal; |
| 211 } |
| 212 |
| 213 lookup_table.show_at_composition_head = table.show_window_at_composition(); |
| 214 |
| 215 // Copy candidates and annotations to |lookup_table|. |
| 216 for (size_t i = 0; i < table.candidates().size(); ++i) { |
| 217 const ibus::IBusLookupTable::Entry& entry = table.candidates()[i]; |
| 218 lookup_table.candidates.push_back(entry.value); |
| 219 lookup_table.labels.push_back(entry.label); |
| 220 lookup_table.annotations.push_back(entry.annotation); |
| 221 |
| 222 InputMethodLookupTable::Description description; |
| 223 description.title = entry.description_title; |
| 224 description.body = entry.description_body; |
| 225 lookup_table.descriptions.push_back(description); |
| 226 } |
| 227 |
| 228 lookup_table.cursor_absolute_index = table.cursor_position(); |
| 229 lookup_table.page_size = table.page_size(); |
| 230 // Ensure that the page_size is non-zero to avoid div-by-zero error. |
| 231 if (lookup_table.page_size <= 0) { |
| 232 DVLOG(1) << "Invalid page size: " << lookup_table.page_size; |
| 233 lookup_table.page_size = 1; |
| 234 } |
| 235 |
| 236 FOR_EACH_OBSERVER(Observer, observers_, |
| 237 OnUpdateLookupTable(lookup_table)); |
743 } | 238 } |
744 | 239 |
745 } // namespace input_method | 240 } // namespace input_method |
746 } // namespace chromeos | 241 } // namespace chromeos |
OLD | NEW |