summaryrefslogtreecommitdiffstats
path: root/views
diff options
context:
space:
mode:
authorsuzhe@google.com <suzhe@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-01 22:59:32 +0000
committersuzhe@google.com <suzhe@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-01 22:59:32 +0000
commit4b99402b089ba598ae26c82d99c3c3d2fe0cd574 (patch)
treef4fe9b7a92bc90b364c40b901919fee163b7061b /views
parent4b8b6d844d019a612a02274d298e3dcffc42276b (diff)
downloadchromium_src-4b99402b089ba598ae26c82d99c3c3d2fe0cd574.zip
chromium_src-4b99402b089ba598ae26c82d99c3c3d2fe0cd574.tar.gz
chromium_src-4b99402b089ba598ae26c82d99c3c3d2fe0cd574.tar.bz2
Refactor RenderWidgetHostViewViews to use the new input method API for Views.
This CL also fixes some issues related to NULL host_ pointer in the original code. Hope the fixes are correct. Another side effect of this CL is: it converts views::KeyEvent to NativeWebKeyboardEvent by using the conversion table of Views instead of WebInputEventFactory provided by WebKit. See bug 54315. BUG=54315 BUG=75003 TEST=none Review URL: http://codereview.chromium.org/6713083 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@80238 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views')
-rw-r--r--views/ime/ibus_ime_context.cc398
-rw-r--r--views/ime/ime_context.cc88
-rw-r--r--views/ime/ime_context.h240
-rw-r--r--views/ime/input_method_gtk.cc5
-rw-r--r--views/ime/input_method_ibus.cc998
-rw-r--r--views/ime/input_method_ibus.h204
-rw-r--r--views/views.gyp24
-rw-r--r--views/widget/widget_gtk.cc15
-rw-r--r--views/widget/widget_gtk.h2
9 files changed, 1234 insertions, 740 deletions
diff --git a/views/ime/ibus_ime_context.cc b/views/ime/ibus_ime_context.cc
deleted file mode 100644
index 9c49560..0000000
--- a/views/ime/ibus_ime_context.cc
+++ /dev/null
@@ -1,398 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <ibus.h>
-
-#include "base/logging.h"
-#include "base/utf_string_conversions.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "ui/base/gtk/gtk_signal.h"
-#include "ui/base/keycodes/keyboard_code_conversion_gtk.h"
-#include "ui/gfx/rect.h"
-#include "views/events/event.h"
-#include "views/ime/ime_context.h"
-
-namespace {
-
-class IBusIMEContext;
-
-struct ProcessKeyEventData {
- ProcessKeyEventData(IBusIMEContext* context,
- const views::KeyEvent& event)
- : context(context),
- event(event.type(), event.key_code(), event.flags()) {
- }
-
- IBusIMEContext* context;
- views::KeyEvent event;
-};
-
-// Implements IMEContext to integrate ibus input method framework
-class IBusIMEContext : public views::IMEContext {
- public:
- explicit IBusIMEContext(views::View* view);
- virtual ~IBusIMEContext();
-
- // views::IMEContext implementations:
- virtual void Focus();
- virtual void Blur();
- virtual void Reset();
- virtual bool FilterKeyEvent(const views::KeyEvent& event);
- virtual void SetCursorLocation(const gfx::Rect& caret_rect);
- virtual void SetSurrounding(const string16& text, int cursor_pos);
-
- private:
- void CreateContext();
- void DestroyContext();
-
- // Event handlers for IBusBus:
- CHROMEG_CALLBACK_0(IBusIMEContext, void, OnConnected, IBusBus*);
- CHROMEG_CALLBACK_0(IBusIMEContext, void, OnDisconnected, IBusBus*);
-
- // Event handlers for IBusIMEContext:
- CHROMEG_CALLBACK_1(IBusIMEContext, void, OnCommitText,
- IBusInputContext*, IBusText*);
- CHROMEG_CALLBACK_3(IBusIMEContext, void, OnForwardKeyEvent,
- IBusInputContext*, guint, guint, guint);
- CHROMEG_CALLBACK_3(IBusIMEContext, void, OnUpdatePreeditText,
- IBusInputContext*, IBusText*, guint, gboolean);
- CHROMEG_CALLBACK_0(IBusIMEContext, void, OnShowPreeditText,
- IBusInputContext*);
- CHROMEG_CALLBACK_0(IBusIMEContext, void, OnHidePreeditText,
- IBusInputContext*);
- CHROMEG_CALLBACK_0(IBusIMEContext, void, OnEnable, IBusInputContext*);
- CHROMEG_CALLBACK_0(IBusIMEContext, void, OnDisable, IBusInputContext*);
- CHROMEG_CALLBACK_0(IBusIMEContext, void, OnDestroy, IBusInputContext*);
-
- static void ProcessKeyEventDone(IBusInputContext* context,
- GAsyncResult* res,
- ProcessKeyEventData *data);
-
- IBusInputContext* context_;
- bool is_focused_;
- gfx::Rect caret_rect_;
-
- static IBusBus* bus_;
-
- DISALLOW_COPY_AND_ASSIGN(IBusIMEContext);
-};
-
-IBusBus* IBusIMEContext::bus_ = NULL;
-
-IBusIMEContext::IBusIMEContext(views::View* view)
- : views::IMEContext(view),
- context_(NULL),
- is_focused_(false) {
- // init ibus
- if (!bus_) {
- ibus_init();
- bus_ = ibus_bus_new();
- }
-
- // connect bus signals
- g_signal_connect(bus_,
- "connected",
- G_CALLBACK(OnConnectedThunk),
- this);
- g_signal_connect(bus_,
- "disconnected",
- G_CALLBACK(OnDisconnectedThunk),
- this);
-
- if (ibus_bus_is_connected(bus_))
- CreateContext();
-}
-
-IBusIMEContext::~IBusIMEContext() {
- // disconnect bus signals
- g_signal_handlers_disconnect_by_func(bus_,
- reinterpret_cast<gpointer>(OnConnectedThunk), this);
- g_signal_handlers_disconnect_by_func(bus_,
- reinterpret_cast<gpointer>(OnDisconnectedThunk), this);
-
- DestroyContext();
-}
-
-void IBusIMEContext::Focus() {
- if (is_focused_)
- return;
- is_focused_ = true;
-
- if (context_)
- ibus_input_context_focus_in(context_);
-}
-
-void IBusIMEContext::Blur() {
- if (!is_focused_)
- return;
-
- is_focused_ = false;
-
- if (context_)
- ibus_input_context_focus_out(context_);
-}
-
-void IBusIMEContext::Reset() {
- if (context_)
- ibus_input_context_reset(context_);
-}
-
-bool IBusIMEContext::FilterKeyEvent(const views::KeyEvent& event) {
- guint keyval = ui::GdkKeyCodeForWindowsKeyCode(event.key_code(),
- event.IsShiftDown() ^ event.IsCapsLockDown());
-
- if (context_) {
- guint modifiers = 0;
-
- if (event.type() == ui::ET_KEY_RELEASED)
- modifiers |= IBUS_RELEASE_MASK;
-
- if (event.IsShiftDown())
- modifiers |= IBUS_SHIFT_MASK;
- if (event.IsControlDown())
- modifiers |= IBUS_CONTROL_MASK;
- if (event.IsAltDown())
- modifiers |= IBUS_MOD1_MASK;
- if (event.IsCapsLockDown())
- modifiers |= IBUS_LOCK_MASK;
-
- ibus_input_context_process_key_event_async(context_,
- keyval, 0, modifiers,
- -1,
- NULL,
- reinterpret_cast<GAsyncReadyCallback>(ProcessKeyEventDone),
- new ProcessKeyEventData(this, event));
- return true;
- }
-
- return false;
-}
-
-void IBusIMEContext::SetCursorLocation(const gfx::Rect& caret_rect) {
- if (context_ && caret_rect_ != caret_rect) {
- caret_rect_ = caret_rect;
- ibus_input_context_set_cursor_location(context_,
- caret_rect_.x(),
- caret_rect_.y(),
- caret_rect_.width(),
- caret_rect_.height());
- }
-}
-
-void IBusIMEContext::SetSurrounding(const string16& text,
- int cursor_pos) {
- // TODO(penghuang) support surrounding
-}
-
-void IBusIMEContext::CreateContext() {
- DCHECK(bus_ != NULL);
- DCHECK(ibus_bus_is_connected(bus_));
-
- context_ = ibus_bus_create_input_context(bus_, "chrome");
-
- // connect input context signals
- g_signal_connect(context_,
- "commit-text",
- G_CALLBACK(OnCommitTextThunk),
- this);
- g_signal_connect(context_,
- "forward-key-event",
- G_CALLBACK(OnForwardKeyEventThunk),
- this);
- g_signal_connect(context_,
- "update-preedit-text",
- G_CALLBACK(OnUpdatePreeditTextThunk),
- this);
- g_signal_connect(context_,
- "show-preedit-text",
- G_CALLBACK(OnShowPreeditTextThunk),
- this);
- g_signal_connect(context_,
- "hide-preedit-text",
- G_CALLBACK(OnHidePreeditTextThunk),
- this);
- g_signal_connect(context_,
- "enabled",
- G_CALLBACK(OnEnableThunk),
- this);
- g_signal_connect(context_,
- "disabled",
- G_CALLBACK(OnDisableThunk),
- this);
- g_signal_connect(context_,
- "destroy",
- G_CALLBACK(OnDestroyThunk),
- this);
-
- guint32 caps = IBUS_CAP_PREEDIT_TEXT |
- IBUS_CAP_FOCUS |
- IBUS_CAP_SURROUNDING_TEXT;
- ibus_input_context_set_capabilities(context_, caps);
-
- if (is_focused_)
- ibus_input_context_focus_in(context_);
-
- ibus_input_context_set_cursor_location(context_,
- caret_rect_.x(),
- caret_rect_.y(),
- caret_rect_.width(),
- caret_rect_.height());
-}
-
-void IBusIMEContext::DestroyContext() {
- if (!context_)
- return;
-
- ibus_proxy_destroy(reinterpret_cast<IBusProxy *>(context_));
-
- DCHECK(!context_);
-}
-
-void IBusIMEContext::OnConnected(IBusBus *bus) {
- DCHECK(bus_ == bus);
- DCHECK(ibus_bus_is_connected(bus_));
-
- if (context_)
- DestroyContext();
- CreateContext();
-}
-
-void IBusIMEContext::OnDisconnected(IBusBus *bus) {
-}
-
-void IBusIMEContext::OnCommitText(IBusInputContext *context,
- IBusText* text) {
- DCHECK(context_ == context);
- views::IMEContext::CommitText(UTF8ToUTF16(text->text));
-}
-
-void IBusIMEContext::OnForwardKeyEvent(IBusInputContext *context,
- guint keyval,
- guint keycode,
- guint state) {
- DCHECK(context_ == context);
-
- int flags = 0;
-
- if (state & IBUS_LOCK_MASK)
- flags |= ui::EF_CAPS_LOCK_DOWN;
- if (state & IBUS_CONTROL_MASK)
- flags |= ui::EF_CONTROL_DOWN;
- if (state & IBUS_SHIFT_MASK)
- flags |= ui::EF_SHIFT_DOWN;
- if (state & IBUS_MOD1_MASK)
- flags |= ui::EF_ALT_DOWN;
- if (state & IBUS_BUTTON1_MASK)
- flags |= ui::EF_LEFT_BUTTON_DOWN;
- if (state & IBUS_BUTTON2_MASK)
- flags |= ui::EF_MIDDLE_BUTTON_DOWN;
- if (state & IBUS_BUTTON3_MASK)
- flags |= ui::EF_RIGHT_BUTTON_DOWN;
-
- ForwardKeyEvent(views::KeyEvent(
- state & IBUS_RELEASE_MASK ?
- ui::ET_KEY_RELEASED : ui::ET_KEY_PRESSED,
- ui::WindowsKeyCodeForGdkKeyCode(keyval),
- flags));
-}
-
-void IBusIMEContext::OnUpdatePreeditText(IBusInputContext *context,
- IBusText* text,
- guint cursor_pos,
- gboolean visible) {
- DCHECK(context_ == context);
- DCHECK(IBUS_IS_TEXT(text));
-
- if (visible) {
- views::CompositionAttributeList attributes;
- IBusAttrList *attrs = text->attrs;
- if (attrs) {
- int i = 0;
- while (1) {
- IBusAttribute *attr = ibus_attr_list_get(attrs, i++);
- if (!attr)
- break;
- if (attr->type == IBUS_ATTR_TYPE_UNDERLINE ||
- attr->type == IBUS_ATTR_TYPE_BACKGROUND) {
- views::CompositionAttribute attribute(attr->start_index,
- attr->end_index,
- SK_ColorBLACK,
- false);
- if (attr->type == IBUS_ATTR_TYPE_BACKGROUND) {
- attribute.thick = true;
- } else if (attr->type == IBUS_ATTR_TYPE_UNDERLINE) {
- if (attr->value == IBUS_ATTR_UNDERLINE_DOUBLE) {
- attribute.thick = true;
- } else if (attr->value == IBUS_ATTR_UNDERLINE_ERROR) {
- attribute.color = SK_ColorRED;
- }
- }
- attributes.push_back(attribute);
- }
- }
- }
-
- SetComposition(UTF8ToUTF16(text->text),
- attributes,
- cursor_pos);
- } else {
- EndComposition();
- }
-}
-
-void IBusIMEContext::OnShowPreeditText(IBusInputContext *context) {
- DCHECK(context_ == context);
-}
-
-void IBusIMEContext::OnHidePreeditText(IBusInputContext *context) {
- DCHECK(context_ == context);
-
- EndComposition();
-}
-
-void IBusIMEContext::OnEnable(IBusInputContext *context) {
- DCHECK(context_ == context);
-}
-
-void IBusIMEContext::OnDisable(IBusInputContext *context) {
- DCHECK(context_ == context);
-}
-
-void IBusIMEContext::OnDestroy(IBusInputContext *context) {
- DCHECK(context_ == context);
-
- g_object_unref(context_);
- context_ = NULL;
-
- EndComposition();
-}
-
-void IBusIMEContext::ProcessKeyEventDone(IBusInputContext *context,
- GAsyncResult* res,
- ProcessKeyEventData *data) {
- DCHECK(data->context->context_ == context);
-
- gboolean processed = FALSE;
-
- if (!ibus_input_context_process_key_event_async_finish(context,
- res,
- &processed,
- NULL))
- processed = FALSE;
-
- if (processed == FALSE)
- data->context->ForwardKeyEvent(data->event);
-
- delete data;
-}
-
-} // namespace
-
-namespace views {
-
-IMEContext* IMEContext::Create(View* view) {
- return new IBusIMEContext(view);
-}
-
-} // namespace views
diff --git a/views/ime/ime_context.cc b/views/ime/ime_context.cc
deleted file mode 100644
index 033e3be..0000000
--- a/views/ime/ime_context.cc
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "views/ime/ime_context.h"
-
-namespace {
-
-#ifdef USE_DUMMY_IME_CONTEXT
-class DummyIMEContext : public views::IMEContext {
- public:
- explicit DummyIMEContext(views::View* view)
- : views::IMEContext(view) {}
- virtual ~DummyIMEContext() {}
-
- // views::IMEContext implementations:
- virtual void Focus() {}
- virtual void Blur() {}
- virtual void Reset() {}
- virtual bool FilterKeyEvent(const views::KeyEvent& event) {
- return false;
- }
- virtual void SetCursorLocation(const gfx::Rect& caret_rect) {}
- virtual void SetSurrounding(const string16& text, int cursor_pos) {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DummyIMEContext);
-};
-#endif // USE_DUMMY_IME_CONTEXT
-
-} // namespace
-
-namespace views {
-
-IMEContext::IMEContext(View* view)
- : view_(view) ,
- commit_text_listener_(NULL),
- composition_listener_(NULL),
- forward_key_event_listener_(NULL),
- surrounding_listener_(NULL) {
-}
-
-void IMEContext::CommitText(const string16& text) {
- if (commit_text_listener_)
- commit_text_listener_->OnCommitText(this, text);
-}
-
-void IMEContext::StartComposition() {
- if (composition_listener_)
- composition_listener_->OnStartComposition(this);
-}
-
-void IMEContext::EndComposition() {
- if (composition_listener_)
- composition_listener_->OnEndComposition(this);
-}
-
-void IMEContext::SetComposition(const string16& text,
- const CompositionAttributeList& attributes,
- uint32 cursor_pos) {
- if (composition_listener_)
- composition_listener_->OnSetComposition(this, text, attributes, cursor_pos);
-}
-
-void IMEContext::ForwardKeyEvent(const KeyEvent& event) {
- if (forward_key_event_listener_)
- forward_key_event_listener_->OnForwardKeyEvent(this, event);
-}
-
-bool IMEContext::SetSurroundingActive(bool active) {
- if (surrounding_listener_)
- return surrounding_listener_->OnSetSurroundingActive(this, active);
- return false;
-}
-
-bool IMEContext::DeleteSurrounding(int offset, int nchars) {
- if (surrounding_listener_)
- return surrounding_listener_->OnDeleteSurrounding(this, offset, nchars);
- return false;
-}
-
-#ifdef USE_DUMMY_IME_CONTEXT
-IMEContext* IMEContext::Create(View* view) {
- return new DummyIMEContext(view);
-}
-#endif // USE_DUMMY_IME_CONTEXT
-
-} // namespace views
diff --git a/views/ime/ime_context.h b/views/ime/ime_context.h
deleted file mode 100644
index 7261b4e..0000000
--- a/views/ime/ime_context.h
+++ /dev/null
@@ -1,240 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef VIEWS_IME_IME_CONTEXT_H_
-#define VIEWS_IME_IME_CONTEXT_H_
-#pragma once
-
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/string16.h"
-#include "third_party/skia/include/core/SkColor.h"
-
-namespace gfx {
-class Rect;
-}
-
-namespace views {
-
-class IMEContext;
-class KeyEvent;
-class View;
-
-// Duplicate WebKit::WebCompositionUnderline
-struct CompositionUnderline {
- CompositionUnderline()
- : start_offset(0),
- end_offset(0),
- color(0),
- thick(false) {}
-
- CompositionUnderline(uint32 s, uint32 e, SkColor c, bool t)
- : start_offset(s),
- end_offset(e),
- color(c),
- thick(t) {}
-
- uint32 start_offset;
- uint32 end_offset;
- SkColor color;
- bool thick;
-};
-
-// WebKit only supports underline attribue for composition, so we use
-// CompositionUnderline as CompositionAttribute right now.
-typedef struct CompositionUnderline CompositionAttribute;
-typedef std::vector<CompositionAttribute> CompositionAttributeList;
-
-// TODO(penghuang) more attributes (background, foreground color and etc)
-// class CompositionAttribute {
-// public:
-// enum CompositionAttributeType{
-// CAT_UNDERLINE = 1,
-// CAT_FORGROUND = 2,
-// CAT_BACKGRUND = 3,
-// };
-//
-// CompositionAttributeType GetType() const { return type_; }
-// unsigned GetStartOffset() const { return start_offset_; }
-// unsigned GetEndOffset() const { return end_offset_; }
-// unsigned GetValue() const { return value_; }
-//
-// private:
-// CompositionAttributeType type_;
-// unsigned start_offset_;
-// unsigned end_offset_;
-// unsigned value_;
-// };
-
-// CommitTextListener is notified when a text is commited from the context.
-class CommitTextListener {
- public:
- virtual void OnCommitText(IMEContext* sender,
- const string16& text) = 0;
-
- protected:
- virtual ~CommitTextListener() {}
-};
-
-// CompositionListener is notified when composition of the context is changed.
-class CompositionListener {
- public:
- virtual void OnStartComposition(IMEContext* sender) = 0;
- virtual void OnEndComposition(IMEContext* sender) = 0;
- virtual void OnSetComposition(IMEContext* sender,
- const string16& text,
- const CompositionAttributeList& attributes,
- uint32 cursor_pos) = 0;
-
- protected:
- virtual ~CompositionListener() {}
-};
-
-// CompositionListener is notified when a key event is forwarded from
-// the context.
-class ForwardKeyEventListener {
- public:
- virtual void OnForwardKeyEvent(IMEContext* sender,
- const KeyEvent& event) = 0;
-
- protected:
- virtual ~ForwardKeyEventListener() {}
-};
-
-// SurroundingListener is notified when an input method context needs to
-// manipulate the text surround the input cursor. If associated view supports
-// surrounding, it should set the listener to input method context. Some input
-// methods generate different result depends on the chars before or after the
-// input cursor.
-//
-// For example:
-// Some Korean input method:
-// Key events Unicode char
-// 1 'C' +U314a
-// 2 'K' +U314f
-// 3 'C' 'K' +Ucc28
-//
-// In case 2 and 3, when users press 'K', the input method will check the char
-// before input cursor. If the char is +U314a, it will remove the char and
-// insert a new char +Ucc28. If no char before input cursor, it will insert char
-// +U314f.
-//
-// See also OnSetSurroundingActive() and OnDeleteSurrounding().
-class SurroundingListener {
- public:
- // Activate or inactivate surrounding support. When surrounding is activated,
- // The assoicated view should call SetSurrounding() to notify any changes of
- // text surround the input cursor.
- // Return true if associated view can support surrounding.
- virtual bool OnSetSurroundingActive(IMEContext* sender,
- bool activate) = 0;
-
- // Delete a picse of text surround the input cursor.
- // Return true if associated view delete the surrounding text successfully.
- virtual bool OnDeleteSurrounding(IMEContext* sender,
- int offset,
- int chars) = 0;
- protected:
- virtual ~SurroundingListener() {}
-};
-
-class IMEContext {
- public:
- virtual ~IMEContext() {}
-
- // Set associated view.
- void set_view(View* view) { view_ = view; }
-
- // Get associated view.
- const View* view() const { return view_; }
-
- // Set a listener to receive a callback when im context commits a text.
- void set_commit_text_listener(CommitTextListener* listener) {
- commit_text_listener_ = listener;
- }
-
- // Set a listener to receive a callback when im context changes composition.
- void set_composition_listener(CompositionListener* listener) {
- composition_listener_ = listener;
- }
-
- // Set a listener to receive a callback when im context forwards a key event.
- void set_forward_key_event_listener(ForwardKeyEventListener* listener) {
- forward_key_event_listener_ = listener;
- }
-
- // Set a listener to receive a callback when im context need operater
- // surrounding.
- void set_surrounding_listener(SurroundingListener* listener) {
- surrounding_listener_ = listener;
- }
-
- // Tell the context it got/lost focus.
- virtual void Focus() = 0;
- virtual void Blur() = 0;
-
- // Reset context, context will be reset to initial state.
- virtual void Reset() = 0;
-
- // Filter key event, returns true, if the key event is handled,
- // associated widget should ignore it.
- virtual bool FilterKeyEvent(const views::KeyEvent& event) = 0;
-
- // Set text input cursor location on screen.
- virtual void SetCursorLocation(const gfx::Rect& caret_rect) = 0;
-
- // Set surrounding context.
- virtual void SetSurrounding(const string16& text,
- int cursor_pos) = 0;
-
- // Create an im context.
- static IMEContext* Create(View* view);
-
- protected:
- explicit IMEContext(View* view);
-
- // Commit text.
- void CommitText(const string16& text);
-
- // Start compostion.
- void StartComposition();
-
- // End composition.
- void EndComposition();
-
- // Set composition.
- void SetComposition(const string16& text,
- const CompositionAttributeList& attributes,
- uint32 cursor_pos);
-
- // Forward key event.
- void ForwardKeyEvent(const KeyEvent& event);
-
- // Notify the associated view whether or not the input context needs
- // surrounding. When surrounding is activated, associated view should
- // call SetSurrounding() to update any changes of text arround the input
- // cursor.
- // Return true if associated view can support surrounding.
- bool SetSurroundingActive(bool activate);
-
- // Delete surrouding arround cursor. Return true, if it is handled.
- bool DeleteSurrounding(int offset, int nchars);
-
- private:
- // Client view.
- View* view_;
-
- // Listeners:
- CommitTextListener* commit_text_listener_;
- CompositionListener* composition_listener_;
- ForwardKeyEventListener* forward_key_event_listener_;
- SurroundingListener* surrounding_listener_;
-
- DISALLOW_COPY_AND_ASSIGN(IMEContext);
-};
-
-} // namespace views
-
-#endif // VIEWS_IME_IM_CONTEXT_H_
diff --git a/views/ime/input_method_gtk.cc b/views/ime/input_method_gtk.cc
index 93445c9..023bcc6 100644
--- a/views/ime/input_method_gtk.cc
+++ b/views/ime/input_method_gtk.cc
@@ -213,6 +213,11 @@ void InputMethodGtk::FocusedViewWillChange() {
void InputMethodGtk::FocusedViewDidChange() {
UpdateContextFocusState();
+
+ // Force to update caret bounds, in case the View thinks that the caret
+ // bounds has not changed.
+ if (context_focused_)
+ OnCaretBoundsChanged(focused_view());
}
void InputMethodGtk::ConfirmCompositionText() {
diff --git a/views/ime/input_method_ibus.cc b/views/ime/input_method_ibus.cc
new file mode 100644
index 0000000..406554e
--- /dev/null
+++ b/views/ime/input_method_ibus.cc
@@ -0,0 +1,998 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "views/ime/input_method_ibus.h"
+
+#include <ibus.h>
+
+#include <cstring>
+#include <set>
+
+#if defined(TOUCH_UI)
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#endif
+
+#include "base/basictypes.h"
+#include "base/i18n/char_iterator.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/third_party/icu/icu_utf.h"
+#include "base/utf_string_conversions.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+#include "ui/base/keycodes/keyboard_code_conversion_gtk.h"
+#include "views/events/event.h"
+#include "views/widget/widget.h"
+
+#if defined(TOUCH_UI)
+#include "ui/base/keycodes/keyboard_code_conversion_x.h"
+#endif
+
+namespace {
+
+// Converts ibus key state flags to Views event flags.
+int ViewsFlagsFromIBusState(guint32 state) {
+ return (state & IBUS_LOCK_MASK ? ui::EF_CAPS_LOCK_DOWN : 0) |
+ (state & IBUS_CONTROL_MASK ? ui::EF_CONTROL_DOWN : 0) |
+ (state & IBUS_SHIFT_MASK ? ui::EF_SHIFT_DOWN : 0) |
+ (state & IBUS_MOD1_MASK ? ui::EF_ALT_DOWN : 0) |
+ (state & IBUS_BUTTON1_MASK ? ui::EF_LEFT_BUTTON_DOWN : 0) |
+ (state & IBUS_BUTTON2_MASK ? ui::EF_MIDDLE_BUTTON_DOWN : 0) |
+ (state & IBUS_BUTTON3_MASK ? ui::EF_RIGHT_BUTTON_DOWN : 0);
+}
+
+// Converts Views event flags to ibus key state flags.
+guint32 IBusStateFromViewsFlags(int flags) {
+ return (flags & ui::EF_CAPS_LOCK_DOWN ? IBUS_LOCK_MASK : 0) |
+ (flags & ui::EF_CONTROL_DOWN ? IBUS_CONTROL_MASK : 0) |
+ (flags & ui::EF_SHIFT_DOWN ? IBUS_SHIFT_MASK : 0) |
+ (flags & ui::EF_ALT_DOWN ? IBUS_MOD1_MASK : 0) |
+ (flags & ui::EF_LEFT_BUTTON_DOWN ? IBUS_BUTTON1_MASK : 0) |
+ (flags & ui::EF_MIDDLE_BUTTON_DOWN ? IBUS_BUTTON2_MASK : 0) |
+ (flags & ui::EF_RIGHT_BUTTON_DOWN ? IBUS_BUTTON3_MASK : 0);
+}
+
+void ExtractCompositionTextFromIBusPreedit(IBusText* text,
+ guint cursor_position,
+ ui::CompositionText* composition) {
+ composition->Clear();
+ composition->text = UTF8ToUTF16(text->text);
+
+ if (composition->text.empty())
+ return;
+
+ // ibus uses character index for cursor position and attribute range, but we
+ // use char16 offset for them. So we need to do conversion here.
+ std::vector<size_t> char16_offsets;
+ size_t length = composition->text.length();
+ base::i18n::UTF16CharIterator char_iterator(&composition->text);
+ do {
+ char16_offsets.push_back(char_iterator.array_pos());
+ } while (char_iterator.Advance());
+
+ // The text length in Unicode characters.
+ guint char_length = static_cast<guint>(char16_offsets.size());
+ // Make sure we can convert the value of |char_length| as well.
+ char16_offsets.push_back(length);
+
+ size_t cursor_offset =
+ char16_offsets[std::min(char_length, cursor_position)];
+
+ composition->selection = ui::Range(cursor_offset);
+ if (text->attrs) {
+ guint i = 0;
+ while(true) {
+ IBusAttribute* attr = ibus_attr_list_get(text->attrs, i++);
+ if (!attr)
+ break;
+ if (attr->type != IBUS_ATTR_TYPE_UNDERLINE &&
+ attr->type != IBUS_ATTR_TYPE_BACKGROUND) {
+ continue;
+ }
+ guint start = std::min(char_length, attr->start_index);
+ guint end = std::min(char_length, attr->end_index);
+ if (start >= end)
+ continue;
+ ui::CompositionUnderline underline(
+ char16_offsets[start], char16_offsets[end],
+ SK_ColorBLACK, false /* thick */);
+ if (attr->type == IBUS_ATTR_TYPE_BACKGROUND) {
+ underline.thick = true;
+ // If the cursor is at start or end of this underline, then we treat
+ // it as the selection range as well, but make sure to set the cursor
+ // position to the selection end.
+ if (underline.start_offset == cursor_offset) {
+ composition->selection.set_start(underline.end_offset);
+ composition->selection.set_end(cursor_offset);
+ } else if (underline.end_offset == cursor_offset) {
+ composition->selection.set_start(underline.start_offset);
+ composition->selection.set_end(cursor_offset);
+ }
+ } else if (attr->type == IBUS_ATTR_TYPE_UNDERLINE) {
+ if (attr->value == IBUS_ATTR_UNDERLINE_DOUBLE)
+ underline.thick = true;
+ else if (attr->value == IBUS_ATTR_UNDERLINE_ERROR)
+ underline.color = SK_ColorRED;
+ }
+ composition->underlines.push_back(underline);
+ }
+ }
+
+ // Use a black thin underline by default.
+ if (composition->underlines.empty()) {
+ composition->underlines.push_back(ui::CompositionUnderline(
+ 0, length, SK_ColorBLACK, false /* thick */));
+ }
+}
+
+} // namespace
+
+namespace views {
+
+// InputMethodIBus::PendingKeyEvent implementation ----------------------------
+class InputMethodIBus::PendingKeyEvent {
+ public:
+ PendingKeyEvent(InputMethodIBus* input_method, const KeyEvent& key);
+ ~PendingKeyEvent();
+
+ // Abandon this pending key event. Its result will just be discarded.
+ void abandon() { input_method_ = NULL; }
+
+ InputMethodIBus* input_method() const { return input_method_; }
+
+ // Process this pending key event after we receive its result from the input
+ // method. It just call through InputMethodIBus::ProcessKeyEventPostIME().
+ void ProcessPostIME(bool handled);
+
+ private:
+ InputMethodIBus* input_method_;
+
+ // Complete information of a views::KeyEvent. Sadly, views::KeyEvent doesn't
+ // support copy.
+ ui::EventType type_;
+ int flags_;
+ ui::KeyboardCode key_code_;
+
+#if defined(TOUCH_UI)
+ // corresponding XEvent data of a views::KeyEvent. It's a plain struct so we
+ // can do bitwise copy.
+ XKeyEvent x_event_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(PendingKeyEvent);
+};
+
+InputMethodIBus::PendingKeyEvent::PendingKeyEvent(InputMethodIBus* input_method,
+ const KeyEvent& key)
+ : input_method_(input_method),
+ type_(key.type()),
+ flags_(key.flags()),
+ key_code_(key.key_code()) {
+ DCHECK(input_method_);
+#if defined(TOUCH_UI)
+ if (key.native_event_2())
+ x_event_ = *reinterpret_cast<XKeyEvent*>(key.native_event_2());
+ else
+ memset(&x_event_, 0, sizeof(x_event_));
+#endif
+}
+
+InputMethodIBus::PendingKeyEvent::~PendingKeyEvent() {
+ if (input_method_)
+ input_method_->FinishPendingKeyEvent(this);
+}
+
+void InputMethodIBus::PendingKeyEvent::ProcessPostIME(bool handled) {
+ if (!input_method_)
+ return;
+
+#if defined(TOUCH_UI)
+ if (x_event_.type == KeyPress || x_event_.type == KeyRelease) {
+ Event::FromNativeEvent2 from_native;
+ KeyEvent key(reinterpret_cast<XEvent*>(&x_event_), from_native);
+ input_method_->ProcessKeyEventPostIME(key, handled);
+ return;
+ }
+#endif
+ KeyEvent key(type_, key_code_, flags_);
+ input_method_->ProcessKeyEventPostIME(key, handled);
+}
+
+// InputMethodIBus::PendingCreateICRequest implementation ---------------------
+class InputMethodIBus::PendingCreateICRequest {
+ public:
+ PendingCreateICRequest(InputMethodIBus* input_method,
+ PendingCreateICRequest** request_ptr,
+ bool fake);
+ ~PendingCreateICRequest();
+
+ // Abandon this pending key event. Its result will just be discarded.
+ void abandon() {
+ input_method_ = NULL;
+ request_ptr_ = NULL;
+ }
+
+ // Stores the result input context to |input_method_|, or abandon it if
+ // |input_method_| is NULL.
+ void StoreOrAbandonInputContext(IBusInputContext* ic);
+
+ private:
+ InputMethodIBus* input_method_;
+ PendingCreateICRequest** request_ptr_;
+ bool fake_;
+};
+
+InputMethodIBus::PendingCreateICRequest::PendingCreateICRequest(
+ InputMethodIBus* input_method,
+ PendingCreateICRequest** request_ptr,
+ bool fake)
+ : input_method_(input_method),
+ request_ptr_(request_ptr),
+ fake_(fake) {
+}
+
+InputMethodIBus::PendingCreateICRequest::~PendingCreateICRequest() {
+ if (request_ptr_) {
+ DCHECK_EQ(*request_ptr_, this);
+ *request_ptr_ = NULL;
+ }
+}
+
+void InputMethodIBus::PendingCreateICRequest::StoreOrAbandonInputContext(
+ IBusInputContext* ic) {
+ if (input_method_) {
+ input_method_->SetContext(ic, fake_);
+ } else {
+ // ibus_proxy_destroy() will not really release the object, we still need
+ // to call g_object_unref() explicitly.
+ ibus_proxy_destroy(reinterpret_cast<IBusProxy *>(ic));
+ g_object_unref(ic);
+ }
+}
+
+// InputMethodIBus implementation ---------------------------------------------
+InputMethodIBus::InputMethodIBus(internal::InputMethodDelegate* delegate)
+ : context_(NULL),
+ fake_context_(NULL),
+ pending_create_ic_request_(NULL),
+ pending_create_fake_ic_request_(NULL),
+ context_focused_(false),
+ composing_text_(false),
+ composition_changed_(false),
+ suppress_next_result_(false),
+ enabled_(false) {
+ set_delegate(delegate);
+}
+
+InputMethodIBus::~InputMethodIBus() {
+ AbandonAllPendingKeyEvents();
+ DestroyContext();
+
+ // Disconnect bus signals
+ g_signal_handlers_disconnect_by_func(
+ GetIBus(), reinterpret_cast<gpointer>(OnIBusConnectedThunk), this);
+ g_signal_handlers_disconnect_by_func(
+ GetIBus(), reinterpret_cast<gpointer>(OnIBusDisconnectedThunk), this);
+}
+
+void InputMethodIBus::OnFocus() {
+ DCHECK(!widget_focused());
+ InputMethodBase::OnFocus();
+ UpdateContextFocusState();
+}
+
+void InputMethodIBus::OnBlur() {
+ DCHECK(widget_focused());
+ ConfirmCompositionText();
+ InputMethodBase::OnBlur();
+ UpdateContextFocusState();
+}
+
+void InputMethodIBus::Init(Widget* widget) {
+ InputMethodBase::Init(widget);
+
+ // Initializes the connection to ibus daemon. It may happen asynchronously,
+ // and as soon as the connection is established, the |context_| will be
+ // created automatically.
+ IBusBus* bus = GetIBus();
+
+ // connect bus signals
+ g_signal_connect(bus, "connected",
+ G_CALLBACK(OnIBusConnectedThunk), this);
+ g_signal_connect(bus, "disconnected",
+ G_CALLBACK(OnIBusDisconnectedThunk), this);
+
+ // Creates the |context_| if the connection is already established. In such
+ // case, we will not get "connected" signal.
+ if (ibus_bus_is_connected(bus))
+ CreateContext();
+}
+
+void InputMethodIBus::DispatchKeyEvent(const KeyEvent& key) {
+ DCHECK(key.type() == ui::ET_KEY_PRESSED || key.type() == ui::ET_KEY_RELEASED);
+ DCHECK(widget_focused());
+
+ // If |context_| is not usable and |fake_context_| is not created yet, then we
+ // can only dispatch the key event as is. We also dispatch the key event
+ // directly if the current text input type is ui::TEXT_INPUT_TYPE_PASSWORD,
+ // to bypass the input method.
+ // Note: We need to send the key event to ibus even if the |context_| is not
+ // enabled, so that ibus can have a chance to enable the |context_|.
+ if (!(context_focused_ || fake_context_) ||
+ GetTextInputType() == ui::TEXT_INPUT_TYPE_PASSWORD) {
+ if (key.type() == ui::ET_KEY_PRESSED)
+ ProcessUnfilteredKeyPressEvent(key);
+ else
+ DispatchKeyEventPostIME(key);
+ return;
+ }
+
+ guint32 ibus_keyval = 0;
+ guint32 ibus_keycode = 0;
+ guint32 ibus_state = 0;
+ PendingKeyEvent* pending_key =
+ NewPendingKeyEvent(key, &ibus_keyval, &ibus_keycode, &ibus_state);
+
+ // Note:
+ // 1. We currently set timeout to -1, because ibus doesn't have a mechanism to
+ // associate input method results to corresponding key event, thus there is
+ // actually no way to abandon results generated by a specific key event. So we
+ // actually cannot abandon a specific key event and its result but accept
+ // following key events and their results. So a timeout to abandon a key event
+ // will not work.
+ // 2. We set GCancellable to NULL, because the operation of cancelling a async
+ // request also happens asynchronously, thus it's actually useless to us.
+ //
+ // The fundemental problem of ibus' async API is: it uses Glib's GIO API to
+ // realize async communication, but in fact, GIO API is specially designed for
+ // concurrent tasks, though it supports async communication as well, the API
+ // is much more complicated than an ordinary message based async communication
+ // API (such as Chrome's IPC).
+ // Thus it's very complicated, if not impossible, to implement a client that
+ // fully utilize asynchronous communication without potential problem.
+ ibus_input_context_process_key_event_async(
+ context_focused_ ? context_ : fake_context_,
+ ibus_keyval, ibus_keycode, ibus_state, -1, NULL,
+ reinterpret_cast<GAsyncReadyCallback>(ProcessKeyEventDone),
+ pending_key);
+
+ // We don't want to suppress the result generated by this key event, but it
+ // may cause problem. See comment in ResetContext() method.
+ suppress_next_result_ = false;
+}
+
+void InputMethodIBus::OnTextInputTypeChanged(View* view) {
+ if (context_ && IsViewFocused(view)) {
+ ResetContext();
+ UpdateContextFocusState();
+ }
+}
+
+void InputMethodIBus::OnCaretBoundsChanged(View* view) {
+ if (!context_focused_ || !IsViewFocused(view))
+ return;
+
+ // The current text input type should not be NONE if |context_| is focused.
+ DCHECK(!IsTextInputTypeNone());
+
+ gfx::Rect rect = GetTextInputClient()->GetCaretBounds();
+ gfx::Point origin = rect.origin();
+ gfx::Point end = gfx::Point(rect.right(), rect.bottom());
+
+ // We need to convert the origin and end points separately, in case the View
+ // is scaled.
+ View::ConvertPointToScreen(view, &origin);
+ View::ConvertPointToScreen(view, &end);
+
+ // This function runs asynchronously.
+ ibus_input_context_set_cursor_location(
+ context_, origin.x(), origin.y(),
+ end.x() - origin.x(), end.y() - origin.y());
+}
+
+void InputMethodIBus::CancelComposition(View* view) {
+ if (context_focused_ && IsViewFocused(view))
+ ResetContext();
+}
+
+std::string InputMethodIBus::GetInputLocale() {
+ // Not supported.
+ return std::string("");
+}
+
+base::i18n::TextDirection InputMethodIBus::GetInputTextDirection() {
+ // Not supported.
+ return base::i18n::UNKNOWN_DIRECTION;
+}
+
+bool InputMethodIBus::IsActive() {
+ return enabled_;
+}
+
+void InputMethodIBus::FocusedViewWillChange() {
+ ConfirmCompositionText();
+}
+
+void InputMethodIBus::FocusedViewDidChange() {
+ UpdateContextFocusState();
+
+ // Force to update caret bounds, in case the View thinks that the caret
+ // bounds has not changed.
+ if (context_focused_)
+ OnCaretBoundsChanged(focused_view());
+}
+
+void InputMethodIBus::CreateContext() {
+ DCHECK(!context_);
+ DCHECK(!fake_context_);
+ DCHECK(GetIBus());
+ DCHECK(ibus_bus_is_connected(GetIBus()));
+ DCHECK(!pending_create_ic_request_);
+ DCHECK(!pending_create_fake_ic_request_);
+
+ // Creates the input context asynchronously.
+ pending_create_ic_request_ = new PendingCreateICRequest(
+ this, &pending_create_ic_request_, false /* fake */);
+ ibus_bus_create_input_context_async(
+ GetIBus(), "chrome", -1, NULL,
+ reinterpret_cast<GAsyncReadyCallback>(CreateInputContextDone),
+ pending_create_ic_request_);
+
+ // Creates the fake input context asynchronously. ibus will match the "fake"
+ // prefix in the application name to do magic thing.
+ pending_create_fake_ic_request_ = new PendingCreateICRequest(
+ this, &pending_create_fake_ic_request_, true /* fake */);
+ ibus_bus_create_input_context_async(
+ GetIBus(), "fake-chrome", -1, NULL,
+ reinterpret_cast<GAsyncReadyCallback>(CreateInputContextDone),
+ pending_create_fake_ic_request_);
+}
+
+void InputMethodIBus::SetContext(IBusInputContext* ic, bool fake) {
+ DCHECK(ic);
+ if (fake) {
+ DCHECK(!fake_context_);
+ fake_context_ = ic;
+
+ // We only need to care about "destroy" signal of the fake context,
+ // as it will not generate any input method result.
+ g_signal_connect(ic, "destroy", G_CALLBACK(OnFakeDestroyThunk), this);
+
+ ibus_input_context_set_capabilities(ic, IBUS_CAP_FOCUS);
+ UpdateFakeContextFocusState();
+ return;
+ }
+
+ DCHECK(!context_);
+ context_ = ic;
+
+ // connect input context signals
+ g_signal_connect(ic, "commit-text",
+ G_CALLBACK(OnCommitTextThunk), this);
+ g_signal_connect(ic, "forward-key-event",
+ G_CALLBACK(OnForwardKeyEventThunk), this);
+ g_signal_connect(ic, "update-preedit-text",
+ G_CALLBACK(OnUpdatePreeditTextThunk), this);
+ g_signal_connect(ic, "show-preedit-text",
+ G_CALLBACK(OnShowPreeditTextThunk), this);
+ g_signal_connect(ic, "hide-preedit-text",
+ G_CALLBACK(OnHidePreeditTextThunk), this);
+ g_signal_connect(ic, "enabled",
+ G_CALLBACK(OnEnableThunk), this);
+ g_signal_connect(ic, "disabled",
+ G_CALLBACK(OnDisableThunk), this);
+ g_signal_connect(ic, "destroy",
+ G_CALLBACK(OnDestroyThunk), this);
+
+ // TODO(suzhe): support surrounding text.
+ guint32 caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS;
+ ibus_input_context_set_capabilities(ic, caps);
+
+ // Sadly, we will not receive "enabled" signal at all. So just assume the
+ // input context is enabled by default.
+ enabled_ = true;
+ UpdateContextFocusState();
+ OnInputMethodChanged();
+}
+
+void InputMethodIBus::DestroyContext() {
+ if (pending_create_ic_request_) {
+ DCHECK(!context_);
+ // |pending_create_ic_request_| will be deleted in CreateInputContextDone().
+ pending_create_ic_request_->abandon();
+ pending_create_ic_request_ = NULL;
+ } else if (context_) {
+ // ibus_proxy_destroy() will not really release the resource of |context_|
+ // object. We still need to handle "destroy" signal and call
+ // g_object_unref() there.
+ ibus_proxy_destroy(reinterpret_cast<IBusProxy *>(context_));
+ DCHECK(!context_);
+ }
+
+ if (pending_create_fake_ic_request_) {
+ DCHECK(!fake_context_);
+ // |pending_create_fake_ic_request_| will be deleted in
+ // CreateInputContextDone().
+ pending_create_fake_ic_request_->abandon();
+ pending_create_fake_ic_request_ = NULL;
+ } else if (fake_context_) {
+ ibus_proxy_destroy(reinterpret_cast<IBusProxy *>(fake_context_));
+ DCHECK(!fake_context_);
+ }
+}
+
+void InputMethodIBus::ConfirmCompositionText() {
+ TextInputClient* client = GetTextInputClient();
+ if (client && client->HasCompositionText())
+ client->ConfirmCompositionText();
+
+ ResetContext();
+}
+
+void InputMethodIBus::ResetContext() {
+ if (!context_focused_ || !GetTextInputClient())
+ return;
+
+ DCHECK(widget_focused());
+ DCHECK(focused_view());
+
+ // Because ibus runs in asynchronous mode, the input method may still send us
+ // results after sending out the reset request, so we use a flag to discard
+ // all results generated by previous key events. But because ibus does not
+ // have a mechanism to identify each key event and corresponding results, this
+ // approach will not work for some corner cases. For example if the user types
+ // very fast, then the next key event may come in before the |context_| is
+ // really reset. Then we actually cannot know whether or not the next
+ // result should be discard.
+ suppress_next_result_ = true;
+
+ composition_.Clear();
+ result_text_.clear();
+ composing_text_ = false;
+ composition_changed_ = false;
+
+ // We need to abandon all pending key events, but as above comment says, there
+ // is no reliable way to abandon all results generated by these abandoned key
+ // events.
+ AbandonAllPendingKeyEvents();
+
+ // This function runs asynchronously.
+ // Note: some input method engines may not support reset method, such as
+ // ibus-anthy. But as we control all input method engines by ourselves, we can
+ // make sure that all of the engines we are using support it correctly.
+ ibus_input_context_reset(context_);
+
+ // We don't need to reset |fake_context_|.
+}
+
+void InputMethodIBus::UpdateContextFocusState() {
+ if (!context_) {
+ context_focused_ = false;
+ UpdateFakeContextFocusState();
+ return;
+ }
+
+ const bool old_context_focused = context_focused_;
+ // Use switch here in case we are going to add more text input types.
+ switch (GetTextInputType()) {
+ case ui::TEXT_INPUT_TYPE_NONE:
+ case ui::TEXT_INPUT_TYPE_PASSWORD:
+ context_focused_ = false;
+ break;
+ default:
+ context_focused_ = true;
+ break;
+ }
+
+ // We only focus in |context_| when the focus is in a normal textfield.
+ // ibus_input_context_focus_{in|out}() run asynchronously.
+ if (old_context_focused && !context_focused_)
+ ibus_input_context_focus_out(context_);
+ else if (!old_context_focused && context_focused_)
+ ibus_input_context_focus_in(context_);
+
+ UpdateFakeContextFocusState();
+}
+
+void InputMethodIBus::UpdateFakeContextFocusState() {
+ if (!fake_context_)
+ return;
+
+ if (widget_focused() && !context_focused_ &&
+ GetTextInputType() != ui::TEXT_INPUT_TYPE_PASSWORD) {
+ // We disable input method in password fields, so it makes no sense to allow
+ // switching input method.
+ ibus_input_context_focus_in(fake_context_);
+ } else {
+ ibus_input_context_focus_out(fake_context_);
+ }
+}
+
+void InputMethodIBus::ProcessKeyEventPostIME(const KeyEvent& key,
+ bool handled) {
+ // If we get here without a focused text input client, then it means the key
+ // event is sent to the global ibus input context.
+ if (!GetTextInputClient()) {
+ DispatchKeyEventPostIME(key);
+ return;
+ }
+
+ const View* old_focused_view = focused_view();
+
+ // Same reason as above DCHECK.
+ DCHECK(old_focused_view);
+
+ if (key.type() == ui::ET_KEY_PRESSED && handled)
+ ProcessFilteredKeyPressEvent(key);
+
+ // In case the focus was changed by the key event. The |context_| should have
+ // been reset when the focused view changed.
+ if (old_focused_view != focused_view())
+ return;
+
+ if (HasInputMethodResult())
+ ProcessInputMethodResult(key, handled);
+
+ // In case the focus was changed when sending input method results to the
+ // focused View.
+ if (old_focused_view != focused_view())
+ return;
+
+ if (key.type() == ui::ET_KEY_PRESSED && !handled)
+ ProcessUnfilteredKeyPressEvent(key);
+ else if (key.type() == ui::ET_KEY_RELEASED)
+ DispatchKeyEventPostIME(key);
+}
+
+void InputMethodIBus::ProcessFilteredKeyPressEvent(const KeyEvent& key) {
+ if (NeedInsertChar()) {
+ DispatchKeyEventPostIME(key);
+ } else {
+ KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, key.flags());
+ DispatchKeyEventPostIME(key);
+ }
+}
+
+void InputMethodIBus::ProcessUnfilteredKeyPressEvent(const KeyEvent& key) {
+ const View* old_focused_view = focused_view();
+ DispatchKeyEventPostIME(key);
+
+ // We shouldn't dispatch the character anymore if the key event caused focus
+ // change.
+ if (old_focused_view != focused_view())
+ return;
+
+ // If a key event was not filtered by |context_|, then it means the key
+ // event didn't generate any result text. So we need to send corresponding
+ // character to the focused text input client.
+ // TODO(suzhe): support compose and dead keys. Gtk supports it in
+ // GtkIMContextSimple class. We need similar thing after getting rid of Gtk.
+ char16 ch = key.GetCharacter();
+ TextInputClient* client = GetTextInputClient();
+ if (ch && client)
+ client->InsertChar(ch, key.flags());
+}
+
+void InputMethodIBus::ProcessInputMethodResult(const KeyEvent& key,
+ bool filtered) {
+ TextInputClient* client = GetTextInputClient();
+ DCHECK(client);
+
+ if (result_text_.length()) {
+ if (filtered && NeedInsertChar()) {
+ for (string16::const_iterator i = result_text_.begin();
+ i != result_text_.end(); ++i) {
+ client->InsertChar(*i, key.flags());
+ }
+ } else {
+ client->InsertText(result_text_);
+ composing_text_ = false;
+ }
+ }
+
+ if (composition_changed_ && !IsTextInputTypeNone()) {
+ if (composition_.text.length()) {
+ composing_text_ = true;
+ client->SetCompositionText(composition_);
+ } else if (result_text_.empty()) {
+ client->ClearCompositionText();
+ }
+ }
+
+ // We should not clear composition text here, as it may belong to the next
+ // composition session.
+ result_text_.clear();
+ composition_changed_ = false;
+}
+
+bool InputMethodIBus::NeedInsertChar() const {
+ return GetTextInputClient() &&
+ (IsTextInputTypeNone() ||
+ (!composing_text_ && result_text_.length() == 1));
+}
+
+bool InputMethodIBus::HasInputMethodResult() const {
+ return result_text_.length() || composition_changed_;
+}
+
+void InputMethodIBus::SendFakeProcessKeyEvent(bool pressed) const {
+ KeyEvent key(pressed ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED,
+ ui::VKEY_PROCESSKEY, 0);
+ DispatchKeyEventPostIME(key);
+}
+
+InputMethodIBus::PendingKeyEvent* InputMethodIBus::NewPendingKeyEvent(
+ const KeyEvent& key,
+ guint32* ibus_keyval,
+ guint32* ibus_keycode,
+ guint32* ibus_state) {
+#if defined(TOUCH_UI)
+ if (key.native_event_2()) {
+ XKeyEvent* x_key = reinterpret_cast<XKeyEvent*>(key.native_event_2());
+ // Yes, ibus uses X11 keysym. We cannot use XLookupKeysym(), which doesn't
+ // translate Shift and CapsLock states.
+ KeySym keysym = NoSymbol;
+ ::XLookupString(x_key, NULL, 0, &keysym, NULL);
+ *ibus_keyval = keysym;
+ *ibus_keycode = x_key->keycode;
+ }
+#elif defined(TOOLKIT_USES_GTK)
+ if (key.native_event()) {
+ GdkEventKey* gdk_key = reinterpret_cast<GdkEventKey*>(key.native_event());
+ *ibus_keyval = gdk_key->keyval;
+ *ibus_keycode = gdk_key->hardware_keycode;
+ }
+#endif
+ else {
+ // GdkKeyCodeForWindowsKeyCode() is actually nothing to do with Gtk, we
+ // probably want to rename it to something like XKeySymForWindowsKeyCode(),
+ // because Gtk keyval is actually same as X KeySym.
+ *ibus_keyval = ui::GdkKeyCodeForWindowsKeyCode(
+ key.key_code(), key.IsShiftDown() ^ key.IsCapsLockDown());
+ *ibus_keycode = 0;
+ }
+
+ *ibus_state = IBusStateFromViewsFlags(key.flags());
+ if (key.type() == ui::ET_KEY_RELEASED)
+ *ibus_state |= IBUS_RELEASE_MASK;
+
+ PendingKeyEvent* pending_key = new PendingKeyEvent(this, key);
+ pending_key_events_.insert(pending_key);
+ return pending_key;
+}
+
+void InputMethodIBus::FinishPendingKeyEvent(PendingKeyEvent* pending_key) {
+ DCHECK(pending_key_events_.count(pending_key));
+
+ // |pending_key| will be deleted in ProcessKeyEventDone().
+ pending_key_events_.erase(pending_key);
+}
+
+void InputMethodIBus::AbandonAllPendingKeyEvents() {
+ for (std::set<PendingKeyEvent*>::iterator i = pending_key_events_.begin();
+ i != pending_key_events_.end(); ++i) {
+ // The object will be deleted in ProcessKeyEventDone().
+ (*i)->abandon();
+ }
+ pending_key_events_.clear();
+}
+
+void InputMethodIBus::OnCommitText(IBusInputContext* context, IBusText* text) {
+ DCHECK_EQ(context_, context);
+ if (suppress_next_result_ || !text || !text->text)
+ return;
+
+ // We need to receive input method result even if the text input type is
+ // ui::TEXT_INPUT_TYPE_NONE, to make sure we can always send correct
+ // character for each key event to the focused text input client.
+ if (!GetTextInputClient())
+ return;
+
+ string16 utf16_text(UTF8ToUTF16(text->text));
+
+ // Append the text to the buffer, because commit signal might be fired
+ // multiple times when processing a key event.
+ result_text_.append(utf16_text);
+
+ // If we are not handling key event, do not bother sending text result if the
+ // focused text input client does not support text input.
+ if (pending_key_events_.empty() && !IsTextInputTypeNone()) {
+ SendFakeProcessKeyEvent(true);
+ GetTextInputClient()->InsertText(utf16_text);
+ SendFakeProcessKeyEvent(false);
+ result_text_.clear();
+ }
+}
+
+void InputMethodIBus::OnForwardKeyEvent(IBusInputContext* context,
+ guint keyval,
+ guint keycode,
+ guint state) {
+ DCHECK_EQ(context_, context);
+
+ ui::KeyboardCode key_code = ui::VKEY_UNKNOWN;
+#if defined(TOUCH_UI)
+ key_code = ui::KeyboardCodeFromXKeysym(keyval);
+#elif defined(TOOLKIT_USES_GTK)
+ key_code = ui::WindowsKeyCodeForGdkKeyCode(keyval);
+#endif
+
+ if (!key_code)
+ return;
+
+ KeyEvent key(state & IBUS_RELEASE_MASK ?
+ ui::ET_KEY_RELEASED : ui::ET_KEY_PRESSED,
+ key_code, ViewsFlagsFromIBusState(state));
+
+ // It is not clear when the input method will forward us a fake key event.
+ // If there is a pending key event, then we may already received some input
+ // method results, so we dispatch this fake key event directly rather than
+ // calling ProcessKeyEventPostIME(), which will clear pending input method
+ // results.
+ if (key.type() == ui::ET_KEY_PRESSED)
+ ProcessUnfilteredKeyPressEvent(key);
+ else
+ DispatchKeyEventPostIME(key);
+}
+
+void InputMethodIBus::OnShowPreeditText(IBusInputContext* context) {
+ DCHECK_EQ(context_, context);
+ if (suppress_next_result_ || IsTextInputTypeNone())
+ return;
+
+ composing_text_ = true;
+}
+
+void InputMethodIBus::OnUpdatePreeditText(IBusInputContext* context,
+ IBusText* text,
+ guint cursor_pos,
+ gboolean visible) {
+ DCHECK_EQ(context_, context);
+ if (suppress_next_result_ || IsTextInputTypeNone())
+ return;
+
+ // |visible| argument is very confusing. For example, what's the correct
+ // behavior when:
+ // 1. OnUpdatePreeditText() is called with a text and visible == false, then
+ // 2. OnShowPreeditText() is called afterwards.
+ //
+ // If it's only for clearing the current preedit text, then why not just use
+ // OnHidePreeditText()?
+ if (!visible) {
+ OnHidePreeditText(context);
+ return;
+ }
+
+ ExtractCompositionTextFromIBusPreedit(text, cursor_pos, &composition_);
+
+ composition_changed_ = true;
+
+ // In case OnShowPreeditText() is not called.
+ if (composition_.text.length())
+ composing_text_ = true;
+
+ // If we receive a composition text without pending key event, then we need to
+ // send it to the focused text input client directly.
+ if (pending_key_events_.empty()) {
+ SendFakeProcessKeyEvent(true);
+ GetTextInputClient()->SetCompositionText(composition_);
+ SendFakeProcessKeyEvent(false);
+ composition_changed_ = false;
+ composition_.Clear();
+ }
+}
+
+void InputMethodIBus::OnHidePreeditText(IBusInputContext* context) {
+ DCHECK_EQ(context_, context);
+ if (composition_.text.empty() || IsTextInputTypeNone())
+ return;
+
+ // Intentionally leaves |composing_text_| unchanged.
+ composition_changed_ = true;
+ composition_.Clear();
+
+ if (pending_key_events_.empty()) {
+ TextInputClient* client = GetTextInputClient();
+ if (client && client->HasCompositionText())
+ client->ClearCompositionText();
+ composition_changed_ = false;
+ }
+}
+
+void InputMethodIBus::OnEnable(IBusInputContext* context) {
+ DCHECK_EQ(context_, context);
+ enabled_ = true;
+ OnInputMethodChanged();
+}
+
+void InputMethodIBus::OnDisable(IBusInputContext* context) {
+ DCHECK_EQ(context_, context);
+ enabled_ = false;
+ OnInputMethodChanged();
+}
+
+void InputMethodIBus::OnDestroy(IBusInputContext* context) {
+ DCHECK_EQ(context_, context);
+ g_object_unref(context_);
+ context_ = NULL;
+ context_focused_ = false;
+
+ ConfirmCompositionText();
+
+ // We are dead, so we need to ask the client to stop relying on us.
+ // We cannot do it in DestroyContext(), because OnDestroy() may be called
+ // automatically.
+ enabled_ = false;
+ OnInputMethodChanged();
+}
+
+void InputMethodIBus::OnFakeDestroy(IBusInputContext* context) {
+ DCHECK_EQ(fake_context_, context);
+ g_object_unref(fake_context_);
+ fake_context_ = NULL;
+}
+
+void InputMethodIBus::OnIBusConnected(IBusBus* bus) {
+ DCHECK_EQ(GetIBus(), bus);
+ DCHECK(ibus_bus_is_connected(bus));
+
+ DestroyContext();
+ CreateContext();
+}
+
+void InputMethodIBus::OnIBusDisconnected(IBusBus* bus) {
+ DCHECK_EQ(GetIBus(), bus);
+
+ // TODO(suzhe): Make sure if we really do not need to handle this signal.
+ // And I'm actually wondering if ibus-daemon will release the resource of the
+ // |context_| correctly when the connection is lost.
+}
+
+// static
+IBusBus* InputMethodIBus::GetIBus() {
+ // Everything happens in UI thread, so we do not need to care about
+ // synchronization issue.
+ static IBusBus* ibus = NULL;
+
+ if (!ibus) {
+ ibus_init();
+ ibus = ibus_bus_new();
+ DCHECK(ibus);
+ }
+ return ibus;
+}
+
+// static
+void InputMethodIBus::ProcessKeyEventDone(IBusInputContext* context,
+ GAsyncResult* res,
+ PendingKeyEvent* data) {
+ DCHECK(data);
+ DCHECK(!data->input_method() ||
+ data->input_method()->context_ == context ||
+ data->input_method()->fake_context_ == context);
+
+ gboolean handled = FALSE;
+ if (!ibus_input_context_process_key_event_async_finish(context, res,
+ &handled, NULL)) {
+ handled = FALSE;
+ }
+
+ data->ProcessPostIME(handled);
+ delete data;
+}
+
+// static
+void InputMethodIBus::CreateInputContextDone(IBusBus* bus,
+ GAsyncResult* res,
+ PendingCreateICRequest* data) {
+ DCHECK_EQ(GetIBus(), bus);
+ DCHECK(data);
+ IBusInputContext* ic =
+ ibus_bus_create_input_context_async_finish(bus, res, NULL);
+ if (ic)
+ data->StoreOrAbandonInputContext(ic);
+ delete data;
+}
+
+} // namespace views
diff --git a/views/ime/input_method_ibus.h b/views/ime/input_method_ibus.h
new file mode 100644
index 0000000..ac1dcbb
--- /dev/null
+++ b/views/ime/input_method_ibus.h
@@ -0,0 +1,204 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef VIEWS_IME_INPUT_METHOD_IBUS_H_
+#define VIEWS_IME_INPUT_METHOD_IBUS_H_
+#pragma once
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/scoped_ptr.h"
+#include "ui/base/gtk/gtk_signal.h"
+#include "views/events/event.h"
+#include "views/ime/input_method_base.h"
+#include "views/view.h"
+
+// Forward declarations, so that we don't need to include ibus.h in this file.
+typedef struct _IBusBus IBusBus;
+typedef struct _IBusInputContext IBusInputContext;
+typedef struct _IBusText IBusText;
+
+namespace views {
+
+// An InputMethod implementation based on IBus.
+class InputMethodIBus : public InputMethodBase {
+ public:
+ explicit InputMethodIBus(internal::InputMethodDelegate* delegate);
+ virtual ~InputMethodIBus();
+
+ // Overridden from InputMethod:
+ virtual void Init(Widget* widget) OVERRIDE;
+ virtual void OnFocus() OVERRIDE;
+ virtual void OnBlur() OVERRIDE;
+ virtual void DispatchKeyEvent(const KeyEvent& key) OVERRIDE;
+ virtual void OnTextInputTypeChanged(View* view) OVERRIDE;
+ virtual void OnCaretBoundsChanged(View* view) OVERRIDE;
+ virtual void CancelComposition(View* view) OVERRIDE;
+ virtual std::string GetInputLocale() OVERRIDE;
+ virtual base::i18n::TextDirection GetInputTextDirection() OVERRIDE;
+ virtual bool IsActive() OVERRIDE;
+
+ private:
+ // A class to hold all data related to a key event being processed by the
+ // input method but still has no result back yet.
+ class PendingKeyEvent;
+
+ // A class to hold information of a pending request for creating an ibus input
+ // context.
+ class PendingCreateICRequest;
+
+ // Overridden from InputMethodBase:
+ virtual void FocusedViewWillChange() OVERRIDE;
+ virtual void FocusedViewDidChange() OVERRIDE;
+
+ // Creates |context_| instance asynchronously.
+ void CreateContext();
+
+ // Sets |context_| or |fake_context_| and hooks necessary signals.
+ void SetContext(IBusInputContext* ic, bool fake);
+
+ // Destroys |context_| instance.
+ void DestroyContext();
+
+ // Asks the client to confirm current composition text.
+ void ConfirmCompositionText();
+
+ // Resets |context_| and abandon all pending results and key events.
+ void ResetContext();
+
+ // Checks the availability of focused text input client and update focus state
+ // of |context_| and |context_simple_| accordingly.
+ void UpdateContextFocusState();
+
+ // Updates focus state of |fake_context_| accordingly.
+ void UpdateFakeContextFocusState();
+
+ // Process a key returned from the input method.
+ void ProcessKeyEventPostIME(const KeyEvent& key, bool handled);
+
+ // Processes a key event that was already filtered by the input method.
+ // A VKEY_PROCESSKEY may be dispatched to the focused View.
+ void ProcessFilteredKeyPressEvent(const KeyEvent& key);
+
+ // Processes a key event that was not filtered by the input method.
+ void ProcessUnfilteredKeyPressEvent(const KeyEvent& key);
+
+ // Sends input method result caused by the given key event to the focused text
+ // input client.
+ void ProcessInputMethodResult(const KeyEvent& key, bool filtered);
+
+ // Checks if the pending input method result needs inserting into the focused
+ // text input client as a single character.
+ bool NeedInsertChar() const;
+
+ // Checks if there is pending input method result.
+ bool HasInputMethodResult() const;
+
+ // Fabricates a key event with VKEY_PROCESSKEY key code and dispatches it to
+ // the focused View.
+ void SendFakeProcessKeyEvent(bool pressed) const;
+
+ // Creates a new PendingKeyEvent object and add it to |pending_key_events_|.
+ // Corresponding ibus key event information will be stored in |*ibus_keyval|,
+ // |*ibus_keycode| and |*ibus_state|.
+ PendingKeyEvent* NewPendingKeyEvent(const KeyEvent& key,
+ guint32* ibus_keyval,
+ guint32* ibus_keycode,
+ guint32* ibus_state);
+
+ // Called when a pending key event has finished. The event will be removed
+ // from |pending_key_events_|.
+ void FinishPendingKeyEvent(PendingKeyEvent* pending_key);
+
+ // Abandons all pending key events. It usually happends when we lose keyboard
+ // focus, the text input type is changed or we are destroyed.
+ void AbandonAllPendingKeyEvents();
+
+ // Event handlers for IBusInputContext:
+ CHROMEG_CALLBACK_1(InputMethodIBus, void, OnCommitText,
+ IBusInputContext*, IBusText*);
+ CHROMEG_CALLBACK_3(InputMethodIBus, void, OnForwardKeyEvent,
+ IBusInputContext*, guint, guint, guint);
+ CHROMEG_CALLBACK_0(InputMethodIBus, void, OnShowPreeditText,
+ IBusInputContext*);
+ CHROMEG_CALLBACK_3(InputMethodIBus, void, OnUpdatePreeditText,
+ IBusInputContext*, IBusText*, guint, gboolean);
+ CHROMEG_CALLBACK_0(InputMethodIBus, void, OnHidePreeditText,
+ IBusInputContext*);
+ CHROMEG_CALLBACK_0(InputMethodIBus, void, OnEnable, IBusInputContext*);
+ CHROMEG_CALLBACK_0(InputMethodIBus, void, OnDisable, IBusInputContext*);
+ CHROMEG_CALLBACK_0(InputMethodIBus, void, OnDestroy, IBusInputContext*);
+ CHROMEG_CALLBACK_0(InputMethodIBus, void, OnFakeDestroy, IBusInputContext*);
+
+ // Event handlers for IBusBus:
+ CHROMEG_CALLBACK_0(InputMethodIBus, void, OnIBusConnected, IBusBus*);
+ CHROMEG_CALLBACK_0(InputMethodIBus, void, OnIBusDisconnected, IBusBus*);
+
+ // Returns the global IBusBus instance.
+ static IBusBus* GetIBus();
+
+ // Callback function for ibus_input_context_process_key_event_async().
+ static void ProcessKeyEventDone(IBusInputContext* context,
+ GAsyncResult* res,
+ PendingKeyEvent* data);
+
+ // Callback function for ibus_bus_create_input_context_async().
+ static void CreateInputContextDone(IBusBus* bus,
+ GAsyncResult* res,
+ PendingCreateICRequest* data);
+
+ // The input context for actual text input.
+ IBusInputContext* context_;
+
+ // The "fake" input context for hotkey handling, it is only used when there
+ // is no focused view, or it doesn't support text input.
+ IBusInputContext* fake_context_;
+
+ // All pending key events. Note: we do not own these object, we just save
+ // pointers to these object so that we can abandon them when necessary.
+ // They will be deleted in ProcessKeyEventDone().
+ std::set<PendingKeyEvent*> pending_key_events_;
+
+ // The pending request for creating the |context_| instance. We need to keep
+ // this pointer so that we can receive or abandon the result.
+ PendingCreateICRequest* pending_create_ic_request_;
+
+ // The pending request for creating the |context_| instance. We need to keep
+ // this pointer so that we can receive or abandon the result.
+ PendingCreateICRequest* pending_create_fake_ic_request_;
+
+ // Pending composition text generated by the current pending key event.
+ // It'll be sent to the focused text input client as soon as we receive the
+ // processing result of the pending key event.
+ ui::CompositionText composition_;
+
+ // Pending result text generated by the current pending key event.
+ // It'll be sent to the focused text input client as soon as we receive the
+ // processing result of the pending key event.
+ string16 result_text_;
+
+ // Indicates if |context_| and |context_simple_| are focused or not.
+ bool context_focused_;
+
+ // Indicates if there is an ongoing composition text.
+ bool composing_text_;
+
+ // Indicates if the composition text is changed or deleted.
+ bool composition_changed_;
+
+ // If it's true then all input method result received before the next key
+ // event will be discarded.
+ bool suppress_next_result_;
+
+ // Indicates if |context_| is enabled.
+ bool enabled_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputMethodIBus);
+};
+
+} // namespace views
+
+#endif // VIEWS_IME_INPUT_METHOD_IBUS_H_
diff --git a/views/views.gyp b/views/views.gyp
index 26e4d6d..c7fac12 100644
--- a/views/views.gyp
+++ b/views/views.gyp
@@ -285,15 +285,14 @@
'focus/focus_util_win.h',
'focus/view_storage.cc',
'focus/view_storage.h',
- 'ime/ibus_ime_context.cc',
- 'ime/ime_context.cc',
- 'ime/ime_context.h',
'ime/input_method.h',
'ime/input_method_delegate.h',
'ime/input_method_base.cc',
'ime/input_method_base.h',
'ime/input_method_gtk.cc',
'ime/input_method_gtk.h',
+ 'ime/input_method_ibus.cc',
+ 'ime/input_method_ibus.h',
'ime/input_method_win.cc',
'ime/input_method_win.h',
'ime/text_input_client.h',
@@ -446,18 +445,25 @@
['exclude', 'touchui/touch_factory.h'],
],
}],
- ['"<!@(<(pkg-config) --atleast-version=1.3.99 ibus-1.0 || echo $?)"!=""', {
+ # TODO(suzhe): We should not check ibus version here. Instead, we
+ # should use a variable to control whether or not to use ibus.
+ ['"<!@(<(pkg-config) --atleast-version=1.3.99 ibus-1.0 || echo $?)"==""', {
+ 'defines': ['HAVE_IBUS=1'],
'sources/': [
- ['exclude', 'ime/ibus_ime_context.cc'],
+ ['exclude', 'ime/input_method_gtk.cc'],
+ ['exclude', 'ime/input_method_gtk.h'],
+ ],
+ }, { # else: no ibus
+ 'sources/': [
+ ['exclude', 'ime/input_method_ibus.cc'],
+ ['exclude', 'ime/input_method_ibus.h'],
],
- 'defines': ['USE_DUMMY_IME_CONTEXT'],
}],
],
}, { # else: touchui != 1
'sources!': [
- 'ime/ibus_ime_context.cc',
- 'ime/ime_context.cc',
- 'ime/ime_context.h',
+ 'ime/input_method_ibus.cc',
+ 'ime/input_method_ibus.h',
],
}],
['OS=="win"', {
diff --git a/views/widget/widget_gtk.cc b/views/widget/widget_gtk.cc
index da998b4..9de4fa1 100644
--- a/views/widget/widget_gtk.cc
+++ b/views/widget/widget_gtk.cc
@@ -44,6 +44,12 @@
#endif
#endif
+#if defined(TOUCH_UI) && defined(HAVE_IBUS)
+#include "views/ime/input_method_ibus.h"
+#else
+#include "views/ime/input_method_gtk.h"
+#endif
+
using ui::OSExchangeData;
using ui::OSExchangeDataProviderGtk;
using ui::ActiveWindowWatcherX;
@@ -505,12 +511,13 @@ void WidgetGtk::Init(GtkWidget* parent,
// already created at this point.
// TODO(suzhe): Always enable input method when we start to use
// RenderWidgetHostViewViews in normal ChromeOS.
-#if !defined(TOUCH_UI)
- if (type_ != TYPE_CHILD && NativeTextfieldViews::IsTextfieldViewsEnabled()) {
-#else
+#if defined(TOUCH_UI) && defined(HAVE_IBUS)
if (type_ != TYPE_CHILD) {
-#endif
+ input_method_.reset(new InputMethodIBus(this));
+#else
+ if (type_ != TYPE_CHILD && NativeTextfieldViews::IsTextfieldViewsEnabled()) {
input_method_.reset(new InputMethodGtk(this));
+#endif
input_method_->Init(GetWidget());
}
diff --git a/views/widget/widget_gtk.h b/views/widget/widget_gtk.h
index 4028997..3217900 100644
--- a/views/widget/widget_gtk.h
+++ b/views/widget/widget_gtk.h
@@ -14,7 +14,6 @@
#include "ui/gfx/size.h"
#include "views/focus/focus_manager.h"
#include "views/ime/input_method_delegate.h"
-#include "views/ime/input_method_gtk.h"
#include "views/widget/native_widget.h"
#include "views/widget/widget.h"
@@ -32,6 +31,7 @@ using ui::OSExchangeDataProviderGtk;
namespace views {
class DropTargetGtk;
+class InputMethod;
class TooltipManagerGtk;
class View;
class WindowGtk;