| Index: views/ime/ibus_ime_context.cc
|
| diff --git a/views/ime/ibus_ime_context.cc b/views/ime/ibus_ime_context.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..130d863d3491249598b718502ecdbd5ad6c984ee
|
| --- /dev/null
|
| +++ b/views/ime/ibus_ime_context.cc
|
| @@ -0,0 +1,398 @@
|
| +// 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 "gfx/rect.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 "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(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_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
|
|
|