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

Side by Side Diff: views/ime/input_method_ibus.cc

Issue 8581003: views: Move ime and test directories to ui/views. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix linux Created 9 years, 1 month 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
« no previous file with comments | « views/ime/input_method_ibus.h ('k') | views/ime/input_method_wayland.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 "views/ime/input_method_ibus.h"
6
7 #include <ibus.h>
8 #if defined(TOUCH_UI)
9 #include <X11/Xlib.h>
10 #include <X11/Xutil.h>
11 #endif
12
13 #include <algorithm>
14 #include <cstring>
15 #include <set>
16 #include <vector>
17
18 #include "base/command_line.h"
19 #include "base/basictypes.h"
20 #include "base/i18n/char_iterator.h"
21 #include "base/logging.h"
22 #include "base/string_util.h"
23 #include "base/third_party/icu/icu_utf.h"
24 #include "base/utf_string_conversions.h"
25 #include "ui/base/keycodes/keyboard_codes.h"
26 #include "ui/gfx/point.h"
27 #include "ui/gfx/rect.h"
28 #include "views/events/event.h"
29 #include "views/widget/widget.h"
30
31 #if defined(USE_AURA) || defined(TOUCH_UI)
32 #include "ui/base/keycodes/keyboard_code_conversion_x.h"
33 #elif defined(TOOLKIT_USES_GTK)
34 #include "ui/base/keycodes/keyboard_code_conversion_gtk.h"
35 #endif
36
37 namespace {
38
39 // A global flag to switch the InputMethod implementation to InputMethodIBus
40 bool inputmethod_ibus_enabled = false;
41
42 // Converts ibus key state flags to event flags.
43 int EventFlagsFromIBusState(guint32 state) {
44 return (state & IBUS_LOCK_MASK ? ui::EF_CAPS_LOCK_DOWN : 0) |
45 (state & IBUS_CONTROL_MASK ? ui::EF_CONTROL_DOWN : 0) |
46 (state & IBUS_SHIFT_MASK ? ui::EF_SHIFT_DOWN : 0) |
47 (state & IBUS_MOD1_MASK ? ui::EF_ALT_DOWN : 0) |
48 (state & IBUS_BUTTON1_MASK ? ui::EF_LEFT_BUTTON_DOWN : 0) |
49 (state & IBUS_BUTTON2_MASK ? ui::EF_MIDDLE_BUTTON_DOWN : 0) |
50 (state & IBUS_BUTTON3_MASK ? ui::EF_RIGHT_BUTTON_DOWN : 0);
51 }
52
53 // Converts event flags to ibus key state flags.
54 guint32 IBusStateFromEventFlags(int flags) {
55 return (flags & ui::EF_CAPS_LOCK_DOWN ? IBUS_LOCK_MASK : 0) |
56 (flags & ui::EF_CONTROL_DOWN ? IBUS_CONTROL_MASK : 0) |
57 (flags & ui::EF_SHIFT_DOWN ? IBUS_SHIFT_MASK : 0) |
58 (flags & ui::EF_ALT_DOWN ? IBUS_MOD1_MASK : 0) |
59 (flags & ui::EF_LEFT_BUTTON_DOWN ? IBUS_BUTTON1_MASK : 0) |
60 (flags & ui::EF_MIDDLE_BUTTON_DOWN ? IBUS_BUTTON2_MASK : 0) |
61 (flags & ui::EF_RIGHT_BUTTON_DOWN ? IBUS_BUTTON3_MASK : 0);
62 }
63
64 void IBusKeyEventFromViewsKeyEvent(const views::KeyEvent& key,
65 guint32* ibus_keyval,
66 guint32* ibus_keycode,
67 guint32* ibus_state) {
68 #if defined(USE_AURA)
69 // TODO(yusukes): Handle native_event()?
70 *ibus_keyval = ui::XKeysymForWindowsKeyCode(
71 key.key_code(), key.IsShiftDown() ^ key.IsCapsLockDown());
72 *ibus_keycode = 0;
73 #elif defined(TOUCH_UI)
74 if (key.native_event()) {
75 XKeyEvent* x_key = reinterpret_cast<XKeyEvent*>(key.native_event());
76 // Yes, ibus uses X11 keysym. We cannot use XLookupKeysym(), which doesn't
77 // translate Shift and CapsLock states.
78 KeySym keysym = NoSymbol;
79 ::XLookupString(x_key, NULL, 0, &keysym, NULL);
80 *ibus_keyval = keysym;
81 *ibus_keycode = x_key->keycode;
82 } else {
83 *ibus_keyval = ui::XKeysymForWindowsKeyCode(
84 key.key_code(), key.IsShiftDown() ^ key.IsCapsLockDown());
85 *ibus_keycode = 0;
86 }
87 #elif defined(TOOLKIT_USES_GTK)
88 if (key.gdk_event()) {
89 GdkEventKey* gdk_key = reinterpret_cast<GdkEventKey*>(key.gdk_event());
90 *ibus_keyval = gdk_key->keyval;
91 *ibus_keycode = gdk_key->hardware_keycode;
92 } else {
93 *ibus_keyval = ui::GdkKeyCodeForWindowsKeyCode(
94 key.key_code(), key.IsShiftDown() ^ key.IsCapsLockDown());
95 *ibus_keycode = 0;
96 }
97 #endif
98
99 *ibus_state = IBusStateFromEventFlags(key.flags());
100 if (key.type() == ui::ET_KEY_RELEASED)
101 *ibus_state |= IBUS_RELEASE_MASK;
102 }
103
104 void ExtractCompositionTextFromIBusPreedit(IBusText* text,
105 guint cursor_position,
106 ui::CompositionText* composition) {
107 composition->Clear();
108 composition->text = UTF8ToUTF16(text->text);
109
110 if (composition->text.empty())
111 return;
112
113 // ibus uses character index for cursor position and attribute range, but we
114 // use char16 offset for them. So we need to do conversion here.
115 std::vector<size_t> char16_offsets;
116 size_t length = composition->text.length();
117 base::i18n::UTF16CharIterator char_iterator(&composition->text);
118 do {
119 char16_offsets.push_back(char_iterator.array_pos());
120 } while (char_iterator.Advance());
121
122 // The text length in Unicode characters.
123 guint char_length = static_cast<guint>(char16_offsets.size());
124 // Make sure we can convert the value of |char_length| as well.
125 char16_offsets.push_back(length);
126
127 size_t cursor_offset =
128 char16_offsets[std::min(char_length, cursor_position)];
129
130 composition->selection = ui::Range(cursor_offset);
131 if (text->attrs) {
132 guint i = 0;
133 while (true) {
134 IBusAttribute* attr = ibus_attr_list_get(text->attrs, i++);
135 if (!attr)
136 break;
137 if (attr->type != IBUS_ATTR_TYPE_UNDERLINE &&
138 attr->type != IBUS_ATTR_TYPE_BACKGROUND) {
139 continue;
140 }
141 guint start = std::min(char_length, attr->start_index);
142 guint end = std::min(char_length, attr->end_index);
143 if (start >= end)
144 continue;
145 ui::CompositionUnderline underline(
146 char16_offsets[start], char16_offsets[end],
147 SK_ColorBLACK, false /* thick */);
148 if (attr->type == IBUS_ATTR_TYPE_BACKGROUND) {
149 underline.thick = true;
150 // If the cursor is at start or end of this underline, then we treat
151 // it as the selection range as well, but make sure to set the cursor
152 // position to the selection end.
153 if (underline.start_offset == cursor_offset) {
154 composition->selection.set_start(underline.end_offset);
155 composition->selection.set_end(cursor_offset);
156 } else if (underline.end_offset == cursor_offset) {
157 composition->selection.set_start(underline.start_offset);
158 composition->selection.set_end(cursor_offset);
159 }
160 } else if (attr->type == IBUS_ATTR_TYPE_UNDERLINE) {
161 if (attr->value == IBUS_ATTR_UNDERLINE_DOUBLE)
162 underline.thick = true;
163 else if (attr->value == IBUS_ATTR_UNDERLINE_ERROR)
164 underline.color = SK_ColorRED;
165 }
166 composition->underlines.push_back(underline);
167 }
168 }
169
170 // Use a black thin underline by default.
171 if (composition->underlines.empty()) {
172 composition->underlines.push_back(ui::CompositionUnderline(
173 0, length, SK_ColorBLACK, false /* thick */));
174 }
175 }
176
177 // A switch to enable InputMethodIBus
178 const char kEnableInputMethodIBusSwitch[] = "enable-inputmethod-ibus";
179
180 } // namespace
181
182 namespace views {
183
184 // InputMethodIBus::PendingKeyEvent implementation ----------------------------
185 class InputMethodIBus::PendingKeyEvent {
186 public:
187 PendingKeyEvent(InputMethodIBus* input_method, const KeyEvent& key,
188 guint32 ibus_keyval);
189 ~PendingKeyEvent();
190
191 // Abandon this pending key event. Its result will just be discarded.
192 void abandon() { input_method_ = NULL; }
193
194 InputMethodIBus* input_method() const { return input_method_; }
195
196 // Process this pending key event after we receive its result from the input
197 // method. It just call through InputMethodIBus::ProcessKeyEventPostIME().
198 void ProcessPostIME(bool handled);
199
200 private:
201 InputMethodIBus* input_method_;
202
203 // Complete information of a views::KeyEvent. Sadly, views::KeyEvent doesn't
204 // support copy.
205 ui::EventType type_;
206 int flags_;
207 ui::KeyboardCode key_code_;
208 uint16 character_;
209 uint16 unmodified_character_;
210
211 guint32 ibus_keyval_;
212
213 #if defined(TOUCH_UI)
214 // corresponding XEvent data of a views::KeyEvent. It's a plain struct so we
215 // can do bitwise copy.
216 XKeyEvent x_event_;
217 #endif
218
219 DISALLOW_COPY_AND_ASSIGN(PendingKeyEvent);
220 };
221
222 InputMethodIBus::PendingKeyEvent::PendingKeyEvent(InputMethodIBus* input_method,
223 const KeyEvent& key,
224 guint32 ibus_keyval)
225 : input_method_(input_method),
226 type_(key.type()),
227 flags_(key.flags()),
228 key_code_(key.key_code()),
229 character_(key.GetCharacter()),
230 unmodified_character_(key.GetUnmodifiedCharacter()),
231 ibus_keyval_(ibus_keyval) {
232 DCHECK(input_method_);
233
234 #if defined(TOUCH_UI)
235 if (key.native_event())
236 x_event_ = *reinterpret_cast<XKeyEvent*>(key.native_event());
237 else
238 memset(&x_event_, 0, sizeof(x_event_));
239 #endif
240 }
241
242 InputMethodIBus::PendingKeyEvent::~PendingKeyEvent() {
243 if (input_method_)
244 input_method_->FinishPendingKeyEvent(this);
245 }
246
247 void InputMethodIBus::PendingKeyEvent::ProcessPostIME(bool handled) {
248 if (!input_method_)
249 return;
250
251 #if defined(TOUCH_UI)
252 if (x_event_.type == KeyPress || x_event_.type == KeyRelease) {
253 KeyEvent key(reinterpret_cast<XEvent*>(&x_event_));
254 input_method_->ProcessKeyEventPostIME(key, ibus_keyval_, handled);
255 return;
256 }
257 #endif
258 KeyEvent key(type_, key_code_, flags_);
259 if (key_code_ == ui::VKEY_UNKNOWN) {
260 key.set_character(character_);
261 key.set_unmodified_character(unmodified_character_);
262 }
263 input_method_->ProcessKeyEventPostIME(key, ibus_keyval_, handled);
264 }
265
266 // InputMethodIBus::PendingCreateICRequest implementation ---------------------
267 class InputMethodIBus::PendingCreateICRequest {
268 public:
269 PendingCreateICRequest(InputMethodIBus* input_method,
270 PendingCreateICRequest** request_ptr);
271 ~PendingCreateICRequest();
272
273 // Abandon this pending key event. Its result will just be discarded.
274 void abandon() {
275 input_method_ = NULL;
276 request_ptr_ = NULL;
277 }
278
279 // Stores the result input context to |input_method_|, or abandon it if
280 // |input_method_| is NULL.
281 void StoreOrAbandonInputContext(IBusInputContext* ic);
282
283 private:
284 InputMethodIBus* input_method_;
285 PendingCreateICRequest** request_ptr_;
286 };
287
288 InputMethodIBus::PendingCreateICRequest::PendingCreateICRequest(
289 InputMethodIBus* input_method,
290 PendingCreateICRequest** request_ptr)
291 : input_method_(input_method),
292 request_ptr_(request_ptr) {
293 }
294
295 InputMethodIBus::PendingCreateICRequest::~PendingCreateICRequest() {
296 if (request_ptr_) {
297 DCHECK_EQ(*request_ptr_, this);
298 *request_ptr_ = NULL;
299 }
300 }
301
302 void InputMethodIBus::PendingCreateICRequest::StoreOrAbandonInputContext(
303 IBusInputContext* ic) {
304 if (input_method_) {
305 input_method_->SetContext(ic);
306 } else {
307 // ibus_proxy_destroy() will not really release the object, we still need
308 // to call g_object_unref() explicitly.
309 ibus_proxy_destroy(reinterpret_cast<IBusProxy *>(ic));
310 g_object_unref(ic);
311 }
312 }
313
314 // InputMethodIBus implementation ---------------------------------------------
315 InputMethodIBus::InputMethodIBus(internal::InputMethodDelegate* delegate)
316 : context_(NULL),
317 pending_create_ic_request_(NULL),
318 context_focused_(false),
319 composing_text_(false),
320 composition_changed_(false),
321 suppress_next_result_(false) {
322 set_delegate(delegate);
323 }
324
325 InputMethodIBus::~InputMethodIBus() {
326 AbandonAllPendingKeyEvents();
327 DestroyContext();
328
329 // Disconnect bus signals
330 g_signal_handlers_disconnect_by_func(
331 GetIBus(), reinterpret_cast<gpointer>(OnIBusConnectedThunk), this);
332 g_signal_handlers_disconnect_by_func(
333 GetIBus(), reinterpret_cast<gpointer>(OnIBusDisconnectedThunk), this);
334 }
335
336 void InputMethodIBus::OnFocus() {
337 DCHECK(!widget_focused());
338 InputMethodBase::OnFocus();
339 UpdateContextFocusState();
340 }
341
342 void InputMethodIBus::OnBlur() {
343 DCHECK(widget_focused());
344 ConfirmCompositionText();
345 InputMethodBase::OnBlur();
346 UpdateContextFocusState();
347 }
348
349 void InputMethodIBus::Init(Widget* widget) {
350 // Initializes the connection to ibus daemon. It may happen asynchronously,
351 // and as soon as the connection is established, the |context_| will be
352 // created automatically.
353 IBusBus* bus = GetIBus();
354
355 // connect bus signals
356 g_signal_connect(bus, "connected",
357 G_CALLBACK(OnIBusConnectedThunk), this);
358 g_signal_connect(bus, "disconnected",
359 G_CALLBACK(OnIBusDisconnectedThunk), this);
360
361 // Creates the |context_| if the connection is already established. In such
362 // case, we will not get "connected" signal.
363 if (ibus_bus_is_connected(bus))
364 CreateContext();
365
366 InputMethodBase::Init(widget);
367 }
368
369 void InputMethodIBus::DispatchKeyEvent(const KeyEvent& key) {
370 DCHECK(key.type() == ui::ET_KEY_PRESSED || key.type() == ui::ET_KEY_RELEASED);
371 DCHECK(widget_focused());
372
373 guint32 ibus_keyval = 0;
374 guint32 ibus_keycode = 0;
375 guint32 ibus_state = 0;
376 IBusKeyEventFromViewsKeyEvent(key, &ibus_keyval, &ibus_keycode, &ibus_state);
377
378 // If |context_| is not usable, then we can only dispatch the key event as is.
379 // We also dispatch the key event directly if the current text input type is
380 // ui::TEXT_INPUT_TYPE_PASSWORD, to bypass the input method.
381 // Note: We need to send the key event to ibus even if the |context_| is not
382 // enabled, so that ibus can have a chance to enable the |context_|.
383 if (!context_focused_ ||
384 GetTextInputType() == ui::TEXT_INPUT_TYPE_PASSWORD) {
385 if (key.type() == ui::ET_KEY_PRESSED)
386 ProcessUnfilteredKeyPressEvent(key, ibus_keyval);
387 else
388 DispatchKeyEventPostIME(key);
389 return;
390 }
391
392 PendingKeyEvent* pending_key = new PendingKeyEvent(this, key, ibus_keyval);
393 pending_key_events_.insert(pending_key);
394
395 // Note:
396 // 1. We currently set timeout to -1, because ibus doesn't have a mechanism to
397 // associate input method results to corresponding key event, thus there is
398 // actually no way to abandon results generated by a specific key event. So we
399 // actually cannot abandon a specific key event and its result but accept
400 // following key events and their results. So a timeout to abandon a key event
401 // will not work.
402 // 2. We set GCancellable to NULL, because the operation of cancelling a async
403 // request also happens asynchronously, thus it's actually useless to us.
404 //
405 // The fundemental problem of ibus' async API is: it uses Glib's GIO API to
406 // realize async communication, but in fact, GIO API is specially designed for
407 // concurrent tasks, though it supports async communication as well, the API
408 // is much more complicated than an ordinary message based async communication
409 // API (such as Chrome's IPC).
410 // Thus it's very complicated, if not impossible, to implement a client that
411 // fully utilize asynchronous communication without potential problem.
412 ibus_input_context_process_key_event_async(
413 context_,
414 ibus_keyval, ibus_keycode, ibus_state, -1, NULL,
415 reinterpret_cast<GAsyncReadyCallback>(ProcessKeyEventDone),
416 pending_key);
417
418 // We don't want to suppress the result generated by this key event, but it
419 // may cause problem. See comment in ResetContext() method.
420 suppress_next_result_ = false;
421 }
422
423 void InputMethodIBus::OnTextInputTypeChanged(View* view) {
424 if (context_ && IsViewFocused(view)) {
425 ResetContext();
426 UpdateContextFocusState();
427 }
428 InputMethodBase::OnTextInputTypeChanged(view);
429 }
430
431 void InputMethodIBus::OnCaretBoundsChanged(View* view) {
432 if (!context_focused_ || !IsViewFocused(view))
433 return;
434
435 // The current text input type should not be NONE if |context_| is focused.
436 DCHECK(!IsTextInputTypeNone());
437
438 gfx::Rect rect = GetTextInputClient()->GetCaretBounds();
439 gfx::Point origin = rect.origin();
440 gfx::Point end = gfx::Point(rect.right(), rect.bottom());
441
442 // We need to convert the origin and end points separately, in case the View
443 // is scaled.
444 View::ConvertPointToScreen(view, &origin);
445 View::ConvertPointToScreen(view, &end);
446
447 // This function runs asynchronously.
448 ibus_input_context_set_cursor_location(
449 context_, origin.x(), origin.y(),
450 end.x() - origin.x(), end.y() - origin.y());
451 }
452
453 void InputMethodIBus::CancelComposition(View* view) {
454 if (context_focused_ && IsViewFocused(view))
455 ResetContext();
456 }
457
458 std::string InputMethodIBus::GetInputLocale() {
459 // Not supported.
460 return std::string("");
461 }
462
463 base::i18n::TextDirection InputMethodIBus::GetInputTextDirection() {
464 // Not supported.
465 return base::i18n::UNKNOWN_DIRECTION;
466 }
467
468 bool InputMethodIBus::IsActive() {
469 return true;
470 }
471
472 // static
473 bool InputMethodIBus::IsInputMethodIBusEnabled() {
474 #if defined(TOUCH_UI)
475 return true;
476 #else
477 return inputmethod_ibus_enabled ||
478 CommandLine::ForCurrentProcess()->HasSwitch(
479 kEnableInputMethodIBusSwitch);
480 #endif
481 }
482
483 // static
484 void InputMethodIBus::SetEnableInputMethodIBus(bool enabled) {
485 inputmethod_ibus_enabled = enabled;
486 }
487
488 void InputMethodIBus::OnWillChangeFocus(View* focused_before, View* focused) {
489 ConfirmCompositionText();
490 }
491
492 void InputMethodIBus::OnDidChangeFocus(View* focused_before, View* focused) {
493 UpdateContextFocusState();
494
495 // Force to update caret bounds, in case the View thinks that the caret
496 // bounds has not changed.
497 if (context_focused_)
498 OnCaretBoundsChanged(GetFocusedView());
499 }
500
501 void InputMethodIBus::CreateContext() {
502 DCHECK(!context_);
503 DCHECK(GetIBus());
504 DCHECK(ibus_bus_is_connected(GetIBus()));
505 DCHECK(!pending_create_ic_request_);
506
507 // Creates the input context asynchronously.
508 pending_create_ic_request_ = new PendingCreateICRequest(
509 this, &pending_create_ic_request_);
510 ibus_bus_create_input_context_async(
511 GetIBus(), "chrome", -1, NULL,
512 reinterpret_cast<GAsyncReadyCallback>(CreateInputContextDone),
513 pending_create_ic_request_);
514 }
515
516 void InputMethodIBus::SetContext(IBusInputContext* ic) {
517 DCHECK(ic);
518 DCHECK(!context_);
519 context_ = ic;
520
521 // connect input context signals
522 g_signal_connect(ic, "commit-text",
523 G_CALLBACK(OnCommitTextThunk), this);
524 g_signal_connect(ic, "forward-key-event",
525 G_CALLBACK(OnForwardKeyEventThunk), this);
526 g_signal_connect(ic, "update-preedit-text",
527 G_CALLBACK(OnUpdatePreeditTextThunk), this);
528 g_signal_connect(ic, "show-preedit-text",
529 G_CALLBACK(OnShowPreeditTextThunk), this);
530 g_signal_connect(ic, "hide-preedit-text",
531 G_CALLBACK(OnHidePreeditTextThunk), this);
532 g_signal_connect(ic, "destroy",
533 G_CALLBACK(OnDestroyThunk), this);
534
535 // TODO(suzhe): support surrounding text.
536 guint32 caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS;
537 ibus_input_context_set_capabilities(ic, caps);
538
539 UpdateContextFocusState();
540 OnInputMethodChanged();
541 }
542
543 void InputMethodIBus::DestroyContext() {
544 if (pending_create_ic_request_) {
545 DCHECK(!context_);
546 // |pending_create_ic_request_| will be deleted in CreateInputContextDone().
547 pending_create_ic_request_->abandon();
548 pending_create_ic_request_ = NULL;
549 } else if (context_) {
550 // ibus_proxy_destroy() will not really release the resource of |context_|
551 // object. We still need to handle "destroy" signal and call
552 // g_object_unref() there.
553 ibus_proxy_destroy(reinterpret_cast<IBusProxy *>(context_));
554 DCHECK(!context_);
555 }
556 }
557
558 void InputMethodIBus::ConfirmCompositionText() {
559 ui::TextInputClient* client = GetTextInputClient();
560 if (client && client->HasCompositionText())
561 client->ConfirmCompositionText();
562
563 ResetContext();
564 }
565
566 void InputMethodIBus::ResetContext() {
567 if (!context_focused_ || !GetTextInputClient())
568 return;
569
570 DCHECK(widget_focused());
571 DCHECK(GetFocusedView());
572
573 // Because ibus runs in asynchronous mode, the input method may still send us
574 // results after sending out the reset request, so we use a flag to discard
575 // all results generated by previous key events. But because ibus does not
576 // have a mechanism to identify each key event and corresponding results, this
577 // approach will not work for some corner cases. For example if the user types
578 // very fast, then the next key event may come in before the |context_| is
579 // really reset. Then we actually cannot know whether or not the next
580 // result should be discard.
581 suppress_next_result_ = true;
582
583 composition_.Clear();
584 result_text_.clear();
585 composing_text_ = false;
586 composition_changed_ = false;
587
588 // We need to abandon all pending key events, but as above comment says, there
589 // is no reliable way to abandon all results generated by these abandoned key
590 // events.
591 AbandonAllPendingKeyEvents();
592
593 // This function runs asynchronously.
594 // Note: some input method engines may not support reset method, such as
595 // ibus-anthy. But as we control all input method engines by ourselves, we can
596 // make sure that all of the engines we are using support it correctly.
597 ibus_input_context_reset(context_);
598
599 character_composer_.Reset();
600 }
601
602 void InputMethodIBus::UpdateContextFocusState() {
603 if (!context_) {
604 context_focused_ = false;
605 return;
606 }
607
608 const bool old_context_focused = context_focused_;
609 // Use switch here in case we are going to add more text input types.
610 switch (GetTextInputType()) {
611 case ui::TEXT_INPUT_TYPE_NONE:
612 case ui::TEXT_INPUT_TYPE_PASSWORD:
613 context_focused_ = false;
614 break;
615 default:
616 context_focused_ = true;
617 break;
618 }
619
620 // We only focus in |context_| when the focus is in a normal textfield.
621 // ibus_input_context_focus_{in|out}() run asynchronously.
622 if (old_context_focused && !context_focused_)
623 ibus_input_context_focus_out(context_);
624 else if (!old_context_focused && context_focused_)
625 ibus_input_context_focus_in(context_);
626 }
627
628 void InputMethodIBus::ProcessKeyEventPostIME(const KeyEvent& key,
629 guint32 ibus_keyval,
630 bool handled) {
631 // If we get here without a focused text input client, then it means the key
632 // event is sent to the global ibus input context.
633 if (!GetTextInputClient()) {
634 DispatchKeyEventPostIME(key);
635 return;
636 }
637
638 const View* old_focused_view = GetFocusedView();
639
640 // Same reason as above DCHECK.
641 DCHECK(old_focused_view);
642
643 if (key.type() == ui::ET_KEY_PRESSED && handled)
644 ProcessFilteredKeyPressEvent(key);
645
646 // In case the focus was changed by the key event. The |context_| should have
647 // been reset when the focused view changed.
648 if (old_focused_view != GetFocusedView())
649 return;
650
651 if (HasInputMethodResult())
652 ProcessInputMethodResult(key, handled);
653
654 // In case the focus was changed when sending input method results to the
655 // focused View.
656 if (old_focused_view != GetFocusedView())
657 return;
658
659 if (key.type() == ui::ET_KEY_PRESSED && !handled)
660 ProcessUnfilteredKeyPressEvent(key, ibus_keyval);
661 else if (key.type() == ui::ET_KEY_RELEASED)
662 DispatchKeyEventPostIME(key);
663 }
664
665 void InputMethodIBus::ProcessFilteredKeyPressEvent(const KeyEvent& key) {
666 if (NeedInsertChar()) {
667 DispatchKeyEventPostIME(key);
668 } else {
669 KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, key.flags());
670 DispatchKeyEventPostIME(key);
671 }
672 }
673
674 void InputMethodIBus::ProcessUnfilteredKeyPressEvent(const KeyEvent& key,
675 guint32 ibus_keyval) {
676 const View* old_focused_view = GetFocusedView();
677 DispatchKeyEventPostIME(key);
678
679 // We shouldn't dispatch the character anymore if the key event caused focus
680 // change.
681 if (old_focused_view != GetFocusedView())
682 return;
683
684 // Process compose and dead keys
685 if (character_composer_.FilterKeyPress(ibus_keyval)) {
686 string16 composed = character_composer_.composed_character();
687 if (!composed.empty()) {
688 ui::TextInputClient* client = GetTextInputClient();
689 if (client)
690 client->InsertText(composed);
691 }
692 return;
693 }
694 // If a key event was not filtered by |context_| and |character_composer_|,
695 // then it means the key event didn't generate any result text. So we need
696 // to send corresponding character to the focused text input client.
697
698 ui::TextInputClient* client = GetTextInputClient();
699 char16 ch = key.GetCharacter();
700 if (ch && client)
701 client->InsertChar(ch, key.flags());
702 }
703
704 void InputMethodIBus::ProcessInputMethodResult(const KeyEvent& key,
705 bool handled) {
706 ui::TextInputClient* client = GetTextInputClient();
707 DCHECK(client);
708
709 if (result_text_.length()) {
710 if (handled && NeedInsertChar()) {
711 for (string16::const_iterator i = result_text_.begin();
712 i != result_text_.end(); ++i) {
713 client->InsertChar(*i, key.flags());
714 }
715 } else {
716 client->InsertText(result_text_);
717 composing_text_ = false;
718 }
719 }
720
721 if (composition_changed_ && !IsTextInputTypeNone()) {
722 if (composition_.text.length()) {
723 composing_text_ = true;
724 client->SetCompositionText(composition_);
725 } else if (result_text_.empty()) {
726 client->ClearCompositionText();
727 }
728 }
729
730 // We should not clear composition text here, as it may belong to the next
731 // composition session.
732 result_text_.clear();
733 composition_changed_ = false;
734 }
735
736 bool InputMethodIBus::NeedInsertChar() const {
737 return GetTextInputClient() &&
738 (IsTextInputTypeNone() ||
739 (!composing_text_ && result_text_.length() == 1));
740 }
741
742 bool InputMethodIBus::HasInputMethodResult() const {
743 return result_text_.length() || composition_changed_;
744 }
745
746 void InputMethodIBus::SendFakeProcessKeyEvent(bool pressed) const {
747 KeyEvent key(pressed ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED,
748 ui::VKEY_PROCESSKEY, 0);
749 DispatchKeyEventPostIME(key);
750 }
751
752 void InputMethodIBus::FinishPendingKeyEvent(PendingKeyEvent* pending_key) {
753 DCHECK(pending_key_events_.count(pending_key));
754
755 // |pending_key| will be deleted in ProcessKeyEventDone().
756 pending_key_events_.erase(pending_key);
757 }
758
759 void InputMethodIBus::AbandonAllPendingKeyEvents() {
760 for (std::set<PendingKeyEvent*>::iterator i = pending_key_events_.begin();
761 i != pending_key_events_.end(); ++i) {
762 // The object will be deleted in ProcessKeyEventDone().
763 (*i)->abandon();
764 }
765 pending_key_events_.clear();
766 }
767
768 void InputMethodIBus::OnCommitText(IBusInputContext* context, IBusText* text) {
769 DCHECK_EQ(context_, context);
770 if (suppress_next_result_ || !text || !text->text)
771 return;
772
773 // We need to receive input method result even if the text input type is
774 // ui::TEXT_INPUT_TYPE_NONE, to make sure we can always send correct
775 // character for each key event to the focused text input client.
776 if (!GetTextInputClient())
777 return;
778
779 string16 utf16_text(UTF8ToUTF16(text->text));
780
781 // Append the text to the buffer, because commit signal might be fired
782 // multiple times when processing a key event.
783 result_text_.append(utf16_text);
784
785 // If we are not handling key event, do not bother sending text result if the
786 // focused text input client does not support text input.
787 if (pending_key_events_.empty() && !IsTextInputTypeNone()) {
788 SendFakeProcessKeyEvent(true);
789 GetTextInputClient()->InsertText(utf16_text);
790 SendFakeProcessKeyEvent(false);
791 result_text_.clear();
792 }
793 }
794
795 void InputMethodIBus::OnForwardKeyEvent(IBusInputContext* context,
796 guint keyval,
797 guint keycode,
798 guint state) {
799 DCHECK_EQ(context_, context);
800
801 ui::KeyboardCode key_code = ui::VKEY_UNKNOWN;
802 #if defined(USE_AURA) || defined(TOUCH_UI)
803 key_code = ui::KeyboardCodeFromXKeysym(keyval);
804 #elif defined(TOOLKIT_USES_GTK)
805 key_code = ui::WindowsKeyCodeForGdkKeyCode(keyval);
806 #endif
807
808 if (!key_code)
809 return;
810
811 KeyEvent key(state & IBUS_RELEASE_MASK ?
812 ui::ET_KEY_RELEASED : ui::ET_KEY_PRESSED,
813 key_code, EventFlagsFromIBusState(state));
814
815 // It is not clear when the input method will forward us a fake key event.
816 // If there is a pending key event, then we may already received some input
817 // method results, so we dispatch this fake key event directly rather than
818 // calling ProcessKeyEventPostIME(), which will clear pending input method
819 // results.
820 if (key.type() == ui::ET_KEY_PRESSED)
821 ProcessUnfilteredKeyPressEvent(key, keyval);
822 else
823 DispatchKeyEventPostIME(key);
824 }
825
826 void InputMethodIBus::OnShowPreeditText(IBusInputContext* context) {
827 DCHECK_EQ(context_, context);
828 if (suppress_next_result_ || IsTextInputTypeNone())
829 return;
830
831 composing_text_ = true;
832 }
833
834 void InputMethodIBus::OnUpdatePreeditText(IBusInputContext* context,
835 IBusText* text,
836 guint cursor_pos,
837 gboolean visible) {
838 DCHECK_EQ(context_, context);
839 if (suppress_next_result_ || IsTextInputTypeNone())
840 return;
841
842 // |visible| argument is very confusing. For example, what's the correct
843 // behavior when:
844 // 1. OnUpdatePreeditText() is called with a text and visible == false, then
845 // 2. OnShowPreeditText() is called afterwards.
846 //
847 // If it's only for clearing the current preedit text, then why not just use
848 // OnHidePreeditText()?
849 if (!visible) {
850 OnHidePreeditText(context);
851 return;
852 }
853
854 ExtractCompositionTextFromIBusPreedit(text, cursor_pos, &composition_);
855
856 composition_changed_ = true;
857
858 // In case OnShowPreeditText() is not called.
859 if (composition_.text.length())
860 composing_text_ = true;
861
862 // If we receive a composition text without pending key event, then we need to
863 // send it to the focused text input client directly.
864 if (pending_key_events_.empty()) {
865 SendFakeProcessKeyEvent(true);
866 GetTextInputClient()->SetCompositionText(composition_);
867 SendFakeProcessKeyEvent(false);
868 composition_changed_ = false;
869 composition_.Clear();
870 }
871 }
872
873 void InputMethodIBus::OnHidePreeditText(IBusInputContext* context) {
874 DCHECK_EQ(context_, context);
875 if (composition_.text.empty() || IsTextInputTypeNone())
876 return;
877
878 // Intentionally leaves |composing_text_| unchanged.
879 composition_changed_ = true;
880 composition_.Clear();
881
882 if (pending_key_events_.empty()) {
883 ui::TextInputClient* client = GetTextInputClient();
884 if (client && client->HasCompositionText())
885 client->ClearCompositionText();
886 composition_changed_ = false;
887 }
888 }
889
890 void InputMethodIBus::OnDestroy(IBusInputContext* context) {
891 DCHECK_EQ(context_, context);
892 g_object_unref(context_);
893 context_ = NULL;
894 context_focused_ = false;
895
896 ConfirmCompositionText();
897
898 // We are dead, so we need to ask the client to stop relying on us.
899 // We cannot do it in DestroyContext(), because OnDestroy() may be called
900 // automatically.
901 OnInputMethodChanged();
902 }
903
904 void InputMethodIBus::OnIBusConnected(IBusBus* bus) {
905 DCHECK_EQ(GetIBus(), bus);
906 DCHECK(ibus_bus_is_connected(bus));
907
908 DestroyContext();
909 CreateContext();
910 }
911
912 void InputMethodIBus::OnIBusDisconnected(IBusBus* bus) {
913 DCHECK_EQ(GetIBus(), bus);
914
915 // TODO(suzhe): Make sure if we really do not need to handle this signal.
916 // And I'm actually wondering if ibus-daemon will release the resource of the
917 // |context_| correctly when the connection is lost.
918 }
919
920 // static
921 IBusBus* InputMethodIBus::GetIBus() {
922 // Everything happens in UI thread, so we do not need to care about
923 // synchronization issue.
924 static IBusBus* ibus = NULL;
925
926 if (!ibus) {
927 ibus_init();
928 ibus = ibus_bus_new();
929 DCHECK(ibus);
930 }
931 return ibus;
932 }
933
934 // static
935 void InputMethodIBus::ProcessKeyEventDone(IBusInputContext* context,
936 GAsyncResult* res,
937 PendingKeyEvent* data) {
938 DCHECK(data);
939 DCHECK(!data->input_method() ||
940 data->input_method()->context_ == context);
941
942 gboolean handled = ibus_input_context_process_key_event_async_finish(
943 context, res, NULL);
944 data->ProcessPostIME(handled);
945 delete data;
946 }
947
948 // static
949 void InputMethodIBus::CreateInputContextDone(IBusBus* bus,
950 GAsyncResult* res,
951 PendingCreateICRequest* data) {
952 DCHECK_EQ(GetIBus(), bus);
953 DCHECK(data);
954 IBusInputContext* ic =
955 ibus_bus_create_input_context_async_finish(bus, res, NULL);
956 if (ic)
957 data->StoreOrAbandonInputContext(ic);
958 delete data;
959 }
960
961 } // namespace views
OLDNEW
« no previous file with comments | « views/ime/input_method_ibus.h ('k') | views/ime/input_method_wayland.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698