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

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

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

Powered by Google App Engine
This is Rietveld 408576698