Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(537)

Side by Side Diff: chrome/browser/chromeos/input_method/ibus_ui_controller.cc

Issue 11280159: Remove ibus dependency from PanelService. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address comments Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698