Move input focus on Emojier to engines' preedit

This commit is contained in:
fujiwarat 2018-06-18 12:46:11 +09:00
Родитель 37aa95f1ad
Коммит 5ee3f48049
25 изменённых файлов: 3710 добавлений и 715 удалений

Просмотреть файл

@ -6,8 +6,15 @@ namespace IBus {
[CCode (cname = "ibus_text_new_from_static_string", has_construct_function = false)]
public Text.from_static_string (string str);
}
public class ExtensionEvent : IBus.Serializable {
[CCode (cname = "ibus_extension_event_new", has_construct_function = true)]
public ExtensionEvent (string first_property_name, ...);
}
public class XEvent : IBus.Serializable {
[CCode (cname = "ibus_x_event_new", has_construct_function = true)]
public XEvent (string first_property_name, ...);
}
public class PanelService : IBus.Service {
public void panel_extension_register_keys(string first_property_name, ...);
}
}

Просмотреть файл

@ -3,7 +3,7 @@
# ibus - The Input Bus
#
# Copyright (c) 2007-2016 Peng Huang <shawn.p.huang@gmail.com>
# Copyright (c) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
# Copyright (c) 2017-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
# Copyright (c) 2007-2017 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
@ -86,6 +86,7 @@ EXTRA_DIST = \
ibus-1.0.deps \
ibus-emoji-dialog-1.0.deps \
config.vapi \
gdk-wayland.vapi \
xi.vapi \
$(NULL)

Просмотреть файл

@ -0,0 +1,7 @@
[CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "gdk/gdkwayland.h")]
namespace GdkWayland
{
[CCode (type_id = "gdk_wayland_display_get_type ()")]
public class Display : Gdk.Display {
}
}

Просмотреть файл

@ -377,10 +377,10 @@ bus_engine_proxy_class_init (BusEngineProxyClass *class)
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
bus_marshal_VOID__VARIANT,
bus_marshal_VOID__OBJECT,
G_TYPE_NONE,
1,
G_TYPE_VARIANT);
IBUS_TYPE_EXTENSION_EVENT);
text_empty = ibus_text_new_from_static_string ("");
g_object_ref_sink (text_empty);
@ -644,7 +644,16 @@ bus_engine_proxy_g_signal (GDBusProxy *proxy,
}
if (g_strcmp0 (signal_name, "PanelExtension") == 0) {
g_signal_emit (engine, engine_signals[PANEL_EXTENSION], 0, parameters);
GVariant *arg0 = NULL;
g_variant_get (parameters, "(v)", &arg0);
g_return_if_fail (arg0 != NULL);
IBusExtensionEvent *event = IBUS_EXTENSION_EVENT (
ibus_serializable_deserialize (arg0));
g_variant_unref (arg0);
g_return_if_fail (event != NULL);
g_signal_emit (engine, engine_signals[PANEL_EXTENSION], 0, event);
_g_object_unref_if_floating (event);
return;
}
@ -1323,6 +1332,44 @@ bus_engine_proxy_is_enabled (BusEngineProxy *engine)
return engine->enabled;
}
void
bus_engine_proxy_panel_extension_received (BusEngineProxy *engine,
IBusExtensionEvent *event)
{
GVariant *variant;
g_assert (BUS_IS_ENGINE_PROXY (engine));
g_assert (IBUS_IS_EXTENSION_EVENT (event));
variant = ibus_serializable_serialize_object (
IBUS_SERIALIZABLE (event));
g_return_if_fail (variant != NULL);
g_dbus_proxy_call ((GDBusProxy *)engine,
"PanelExtensionReceived",
g_variant_new ("(v)", variant),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
NULL,
NULL);
}
void
bus_engine_proxy_panel_extension_register_keys (BusEngineProxy *engine,
GVariant *parameters)
{
g_assert (BUS_IS_ENGINE_PROXY (engine));
g_assert (parameters);
g_dbus_proxy_call ((GDBusProxy *)engine,
"PanelExtensionRegisterKeys",
g_variant_new ("(v)", g_variant_ref (parameters)),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
NULL,
NULL);
}
static gboolean
initable_init (GInitable *initable,
GCancellable *cancellable,

Просмотреть файл

@ -2,7 +2,8 @@
/* vim:set et sts=4: */
/* ibus - The Input Bus
* Copyright (C) 2008-2013 Peng Huang <shawn.p.huang@gmail.com>
* Copyright (C) 2008-2013 Red Hat, Inc.
* Copyright (C) 2018 Takao Fujiwara <takao.fujiwara@gmail.com>
* Copyright (C) 2008-2018 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -325,5 +326,27 @@ void bus_engine_proxy_set_content_type
IBusPropList *bus_engine_proxy_get_properties
(BusEngineProxy *engine);
/**
* bus_engine_proxy_panel_extension_received:
* @engine: A #BusEngineProxy.
* @event: An #IBusExtensionEvent.
*
* Send an #IBusExtensionEvent to the engine.
*/
void bus_engine_proxy_panel_extension_received
(BusEngineProxy *engine,
IBusExtensionEvent *event);
/**
* bus_engine_proxy_panel_extension_register_keys:
* @engine: A #BusEngineProxy.
* @parameters: A #GVariant array which includes the name and shortcut keys.
*
* Send shortcut keys to the engine to enable the extension.
*/
void bus_engine_proxy_panel_extension_register_keys
(BusEngineProxy *engine,
GVariant *parameters);
G_END_DECLS
#endif

Просмотреть файл

@ -74,7 +74,8 @@ struct _BusIBusImpl {
BusInputContext *focused_context;
BusPanelProxy *panel;
BusPanelProxy *extension;
BusPanelProxy *emoji_extension;
gboolean enable_emoji_extension;
/* a default keymap of ibus-daemon (usually "us") which is used only
* when use_sys_layout is FALSE. */
@ -83,6 +84,7 @@ struct _BusIBusImpl {
gboolean use_global_engine;
gchar *global_engine_name;
gchar *global_previous_engine_name;
GVariant *extension_register_keys;
};
struct _BusIBusImplClass {
@ -294,40 +296,158 @@ _panel_destroy_cb (BusPanelProxy *panel,
if (ibus->panel == panel)
ibus->panel = NULL;
else if (ibus->extension == panel)
ibus->extension = NULL;
else if (ibus->emoji_extension == panel)
ibus->emoji_extension = NULL;
else
g_return_if_reached ();
g_object_unref (panel);
}
static void
bus_ibus_impl_panel_extension_received (BusIBusImpl *ibus,
GVariant *parameters)
bus_ibus_impl_set_panel_extension_mode (BusIBusImpl *ibus,
IBusExtensionEvent *event)
{
if (!ibus->extension) {
gboolean is_extension = FALSE;
g_return_if_fail (BUS_IS_IBUS_IMPL (ibus));
g_return_if_fail (IBUS_IS_EXTENSION_EVENT (event));
if (!ibus->emoji_extension) {
g_warning ("Panel extension is not running.");
return;
}
g_return_if_fail (BUS_IS_IBUS_IMPL (ibus));
g_return_if_fail (BUS_IS_PANEL_PROXY (ibus->extension));
g_return_if_fail (BUS_IS_PANEL_PROXY (ibus->emoji_extension));
ibus->enable_emoji_extension = ibus_extension_event_is_enabled (event);
is_extension = ibus_extension_event_is_extension (event);
if (ibus->focused_context != NULL) {
if (ibus->enable_emoji_extension) {
bus_input_context_set_emoji_extension (ibus->focused_context,
ibus->emoji_extension);
} else {
bus_input_context_set_emoji_extension (ibus->focused_context, NULL);
}
if (is_extension)
bus_input_context_panel_extension_received (ibus->focused_context,
event);
}
if (is_extension)
return;
/* Use the DBus method because it seems any DBus signal,
* g_dbus_message_new_signal(), cannot be reached to the server. */
g_dbus_proxy_call (G_DBUS_PROXY (ibus->extension),
"PanelExtensionReceived",
parameters,
G_DBUS_CALL_FLAGS_NONE,
-1, NULL, NULL, NULL);
bus_panel_proxy_panel_extension_received (ibus->emoji_extension,
event);
}
static void
_panel_panel_extension_cb (BusPanelProxy *panel,
GVariant *parameters,
BusIBusImpl *ibus)
bus_ibus_impl_set_panel_extension_keys (BusIBusImpl *ibus,
GVariant *parameters)
{
bus_ibus_impl_panel_extension_received (ibus, parameters);
BusEngineProxy *engine = NULL;
g_return_if_fail (BUS_IS_IBUS_IMPL (ibus));
g_return_if_fail (parameters);
if (!ibus->emoji_extension) {
g_warning ("Panel extension is not running.");
return;
}
if (ibus->extension_register_keys)
g_variant_unref (ibus->extension_register_keys);
ibus->extension_register_keys = g_variant_ref_sink (parameters);
if (ibus->focused_context != NULL) {
engine = bus_input_context_get_engine (ibus->focused_context);
}
if (!engine)
return;
bus_engine_proxy_panel_extension_register_keys (engine, parameters);
}
static void
_panel_panel_extension_cb (BusPanelProxy *panel,
IBusExtensionEvent *event,
BusIBusImpl *ibus)
{
bus_ibus_impl_set_panel_extension_mode (ibus, event);
}
static void
_panel_panel_extension_register_keys_cb (BusInputContext *context,
GVariant *parameters,
BusIBusImpl *ibus)
{
bus_ibus_impl_set_panel_extension_keys (ibus, parameters);
}
static void
_panel_update_preedit_text_received_cb (BusPanelProxy *panel,
IBusText *text,
guint cursor_pos,
gboolean visible,
BusIBusImpl *ibus)
{
g_return_if_fail (BUS_IS_IBUS_IMPL (ibus));
if (!ibus->focused_context)
return;
bus_input_context_update_preedit_text (ibus->focused_context,
text, cursor_pos, visible, IBUS_ENGINE_PREEDIT_CLEAR, FALSE);
}
static void
_panel_update_lookup_table_received_cb (BusPanelProxy *panel,
IBusLookupTable *table,
gboolean visible,
BusIBusImpl *ibus)
{
g_return_if_fail (BUS_IS_IBUS_IMPL (ibus));
g_return_if_fail (IBUS_IS_LOOKUP_TABLE (table));
if (!ibus->focused_context)
return;
/* Call bus_input_context_update_lookup_table() instead of
* bus_panel_proxy_update_lookup_table() for panel extensions because
* bus_input_context_page_up() can call bus_panel_proxy_page_up_received().
*/
bus_input_context_update_lookup_table (
ibus->focused_context, table, visible, TRUE);
}
static void
_panel_update_auxiliary_text_received_cb (BusPanelProxy *panel,
IBusText *text,
gboolean visible,
BusIBusImpl *ibus)
{
g_return_if_fail (BUS_IS_IBUS_IMPL (ibus));
g_return_if_fail (IBUS_IS_TEXT (text));
if (!ibus->panel)
return;
bus_panel_proxy_update_auxiliary_text (
ibus->panel, text, visible);
}
static void
_panel_show_lookup_table_received_cb (BusPanelProxy *panel,
BusIBusImpl *ibus)
{
g_return_if_fail (BUS_IS_IBUS_IMPL (ibus));
if (ibus->panel)
bus_panel_proxy_show_lookup_table (ibus->panel);
}
static void
_panel_hide_lookup_table_received_cb (BusPanelProxy *panel,
BusIBusImpl *ibus)
{
g_return_if_fail (BUS_IS_IBUS_IMPL (ibus));
if (ibus->panel)
bus_panel_proxy_hide_lookup_table (ibus->panel);
}
static void
@ -361,8 +481,8 @@ _dbus_name_owner_changed_cb (BusDBusImpl *dbus,
if (!g_strcmp0 (name, IBUS_SERVICE_PANEL))
panel_type = PANEL_TYPE_PANEL;
else if (!g_strcmp0 (name, IBUS_SERVICE_PANEL_EXTENSION))
panel_type = PANEL_TYPE_EXTENSION;
else if (!g_strcmp0 (name, IBUS_SERVICE_PANEL_EXTENSION_EMOJI))
panel_type = PANEL_TYPE_EXTENSION_EMOJI;
if (panel_type != PANEL_TYPE_NONE) {
if (g_strcmp0 (new_name, "") != 0) {
@ -370,7 +490,7 @@ _dbus_name_owner_changed_cb (BusDBusImpl *dbus,
BusConnection *connection;
BusInputContext *context = NULL;
BusPanelProxy **panel = (panel_type == PANEL_TYPE_PANEL) ?
&ibus->panel : &ibus->extension;
&ibus->panel : &ibus->emoji_extension;
if (*panel != NULL) {
ibus_proxy_destroy ((IBusProxy *)(*panel));
@ -383,6 +503,8 @@ _dbus_name_owner_changed_cb (BusDBusImpl *dbus,
g_return_if_fail (connection != NULL);
*panel = bus_panel_proxy_new (connection, panel_type);
if (panel_type == PANEL_TYPE_EXTENSION_EMOJI)
ibus->enable_emoji_extension = FALSE;
g_signal_connect (*panel,
"destroy",
@ -392,6 +514,26 @@ _dbus_name_owner_changed_cb (BusDBusImpl *dbus,
"panel-extension",
G_CALLBACK (_panel_panel_extension_cb),
ibus);
g_signal_connect (*panel,
"panel-extension-register-keys",
G_CALLBACK (
_panel_panel_extension_register_keys_cb),
ibus);
g_signal_connect (
*panel,
"update-preedit-text-received",
G_CALLBACK (_panel_update_preedit_text_received_cb),
ibus);
g_signal_connect (
*panel,
"update-lookup-table-received",
G_CALLBACK (_panel_update_lookup_table_received_cb),
ibus);
g_signal_connect (
*panel,
"update-auxiliary-text-received",
G_CALLBACK (_panel_update_auxiliary_text_received_cb),
ibus);
if (ibus->focused_context != NULL) {
context = ibus->focused_context;
@ -450,7 +592,7 @@ bus_ibus_impl_init (BusIBusImpl *ibus)
ibus->contexts = NULL;
ibus->focused_context = NULL;
ibus->panel = NULL;
ibus->extension = NULL;
ibus->emoji_extension = NULL;
ibus->keymap = ibus_keymap_get ("us");
@ -650,11 +792,11 @@ bus_ibus_impl_set_context_engine_from_desc (BusIBusImpl *ibus,
}
static void
_context_panel_extension_cb (BusInputContext *context,
GVariant *parameters,
BusIBusImpl *ibus)
_context_panel_extension_cb (BusInputContext *context,
IBusExtensionEvent *event,
BusIBusImpl *ibus)
{
bus_ibus_impl_panel_extension_received (ibus, parameters);
bus_ibus_impl_set_panel_extension_mode (ibus, event);
}
const static struct {
@ -694,13 +836,18 @@ bus_ibus_impl_set_focused_context (BusIBusImpl *ibus,
if (engine) {
g_object_ref (engine);
bus_input_context_set_engine (ibus->focused_context, NULL);
bus_input_context_set_emoji_extension (ibus->focused_context,
NULL);
}
}
if (ibus->panel != NULL)
bus_panel_proxy_focus_out (ibus->panel, ibus->focused_context);
if (ibus->extension != NULL)
bus_panel_proxy_focus_out (ibus->extension, ibus->focused_context);
if (ibus->emoji_extension != NULL) {
bus_panel_proxy_focus_out (ibus->emoji_extension,
ibus->focused_context);
}
bus_input_context_set_emoji_extension (ibus->focused_context, NULL);
bus_input_context_get_content_type (ibus->focused_context,
&purpose, &hints);
@ -724,6 +871,12 @@ bus_ibus_impl_set_focused_context (BusIBusImpl *ibus,
if (engine != NULL) {
bus_input_context_set_engine (context, engine);
bus_input_context_enable (context);
if (ibus->enable_emoji_extension) {
bus_input_context_set_emoji_extension (context,
ibus->emoji_extension);
} else {
bus_input_context_set_emoji_extension (context, NULL);
}
}
for (i = 0; i < G_N_ELEMENTS(context_signals); i++) {
g_signal_connect (ibus->focused_context,
@ -734,8 +887,8 @@ bus_ibus_impl_set_focused_context (BusIBusImpl *ibus,
if (ibus->panel != NULL)
bus_panel_proxy_focus_in (ibus->panel, context);
if (ibus->extension != NULL)
bus_panel_proxy_focus_in (ibus->extension, context);
if (ibus->emoji_extension != NULL)
bus_panel_proxy_focus_in (ibus->emoji_extension, context);
}
if (engine != NULL)
@ -751,6 +904,12 @@ bus_ibus_impl_set_global_engine (BusIBusImpl *ibus,
if (ibus->focused_context) {
bus_input_context_set_engine (ibus->focused_context, engine);
if (ibus->enable_emoji_extension) {
bus_input_context_set_emoji_extension (ibus->focused_context,
ibus->emoji_extension);
} else {
bus_input_context_set_emoji_extension (ibus->focused_context, NULL);
}
} else if (ibus->fake_context) {
bus_input_context_set_engine (ibus->fake_context, engine);
}
@ -927,9 +1086,9 @@ _context_destroy_cb (BusInputContext *context,
bus_input_context_get_capabilities (context) & IBUS_CAP_FOCUS) {
bus_panel_proxy_destroy_context (ibus->panel, context);
}
if (ibus->extension &&
if (ibus->emoji_extension &&
bus_input_context_get_capabilities (context) & IBUS_CAP_FOCUS) {
bus_panel_proxy_destroy_context (ibus->extension, context);
bus_panel_proxy_destroy_context (ibus->emoji_extension, context);
}
ibus->contexts = g_list_remove (ibus->contexts, context);
@ -1489,6 +1648,7 @@ _ibus_set_global_engine_ready_cb (BusInputContext *context,
else {
g_dbus_method_invocation_return_value (data->invocation, NULL);
BusEngineProxy *engine = bus_input_context_get_engine (context);
if (ibus->use_global_engine && (context != ibus->focused_context)) {
/* context and ibus->focused_context don't match. This means that
* the focus is moved before _ibus_set_global_engine() asynchronous
@ -1496,14 +1656,28 @@ _ibus_set_global_engine_ready_cb (BusInputContext *context,
* being focused hasn't been updated. Update the engine here so that
* subsequent _ibus_get_global_engine() call could return a
* consistent engine name. */
BusEngineProxy *engine = bus_input_context_get_engine (context);
if (engine && ibus->focused_context != NULL) {
g_object_ref (engine);
bus_input_context_set_engine (context, NULL);
bus_input_context_set_emoji_extension (context, NULL);
bus_input_context_set_engine (ibus->focused_context, engine);
if (ibus->enable_emoji_extension) {
bus_input_context_set_emoji_extension (
ibus->focused_context,
ibus->emoji_extension);
} else {
bus_input_context_set_emoji_extension (
ibus->focused_context,
NULL);
}
g_object_unref (engine);
}
}
if (engine && ibus->extension_register_keys) {
bus_engine_proxy_panel_extension_register_keys (
engine,
ibus->extension_register_keys);
}
}
g_object_unref (ibus);
@ -2013,11 +2187,12 @@ bus_ibus_impl_registry_destroy (BusIBusImpl *ibus)
g_list_free_full (ibus->components, g_object_unref);
ibus->components = NULL;
g_hash_table_destroy (ibus->engine_table);
ibus->engine_table = NULL;
g_clear_pointer (&ibus->engine_table, g_hash_table_destroy);
ibus_object_destroy (IBUS_OBJECT (ibus->registry));
ibus->registry = NULL;
g_clear_pointer (&ibus->registry, ibus_object_destroy);
if (ibus->extension_register_keys)
g_clear_pointer (&ibus->extension_register_keys, g_variant_unref);
}
static gint

Просмотреть файл

@ -94,6 +94,9 @@ struct _BusInputContext {
/* content-type (primary purpose and hints) */
guint purpose;
guint hints;
BusPanelProxy *emoji_extension;
gboolean is_extension_lookup_table;
};
struct _BusInputContextClass {
@ -162,16 +165,12 @@ static gboolean bus_input_context_service_set_property
GError **error);
static void bus_input_context_unset_engine
(BusInputContext *context);
static void bus_input_context_update_preedit_text
(BusInputContext *context,
IBusText *text,
guint cursor_pos,
gboolean visible,
guint mode);
static void bus_input_context_show_preedit_text
(BusInputContext *context);
(BusInputContext *context,
gboolean is_extension);
static void bus_input_context_hide_preedit_text
(BusInputContext *context);
(BusInputContext *context,
gboolean is_extension);
static void bus_input_context_update_auxiliary_text
(BusInputContext *context,
IBusText *text,
@ -180,10 +179,6 @@ static void bus_input_context_show_auxiliary_text
(BusInputContext *context);
static void bus_input_context_hide_auxiliary_text
(BusInputContext *context);
static void bus_input_context_update_lookup_table
(BusInputContext *context,
IBusLookupTable *table,
gboolean visible);
static void bus_input_context_show_lookup_table
(BusInputContext *context);
static void bus_input_context_hide_lookup_table
@ -605,10 +600,10 @@ bus_input_context_class_init (BusInputContextClass *class)
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
bus_marshal_VOID__VARIANT,
bus_marshal_VOID__OBJECT,
G_TYPE_NONE,
1,
G_TYPE_VARIANT);
IBUS_TYPE_EXTENSION_EVENT);
text_empty = ibus_text_new_from_string ("");
g_object_ref_sink (text_empty);
@ -760,15 +755,17 @@ bus_input_context_property_changed (BusInputContext *context,
error);
}
/**
* _ic_process_key_event_reply_cb:
* _panel_process_key_event_cb:
*
* A GAsyncReadyCallback function to be called when bus_engine_proxy_process_key_event() is finished.
* A GAsyncReadyCallback function to be called when
* bus_panel_proxy_process_key_event() is finished.
*/
static void
_ic_process_key_event_reply_cb (GObject *source,
GAsyncResult *res,
GDBusMethodInvocation *invocation)
_panel_process_key_event_cb (GObject *source,
GAsyncResult *res,
GDBusMethodInvocation *invocation)
{
GError *error = NULL;
GVariant *value = g_dbus_proxy_call_finish ((GDBusProxy *)source,
@ -784,6 +781,61 @@ _ic_process_key_event_reply_cb (GObject *source,
}
}
typedef struct _ProcessKeyEventData ProcessKeyEventData;
struct _ProcessKeyEventData {
GDBusMethodInvocation *invocation;
BusInputContext *context;
guint keyval;
guint keycode;
guint modifiers;
};
/**
* _ic_process_key_event_reply_cb:
*
* A GAsyncReadyCallback function to be called when
* bus_engine_proxy_process_key_event() is finished.
*/
static void
_ic_process_key_event_reply_cb (GObject *source,
GAsyncResult *res,
ProcessKeyEventData *data)
{
GDBusMethodInvocation *invocation = data->invocation;
BusInputContext *context = data->context;
guint keyval = data->keyval;
guint keycode = data->keycode;
guint modifiers = data->modifiers;
GError *error = NULL;
GVariant *value = g_dbus_proxy_call_finish ((GDBusProxy *)source,
res,
&error);
if (value != NULL) {
gboolean retval = FALSE;
g_variant_get (value, "(b)", &retval);
if (context->emoji_extension && !retval) {
bus_panel_proxy_process_key_event (context->emoji_extension,
keyval,
keycode,
modifiers,
(GAsyncReadyCallback)
_panel_process_key_event_cb,
invocation);
} else {
g_dbus_method_invocation_return_value (invocation, value);
}
g_variant_unref (value);
}
else {
g_dbus_method_invocation_return_gerror (invocation, error);
g_error_free (error);
}
g_object_unref (context);
g_slice_free (ProcessKeyEventData, data);
}
/**
* _ic_process_key_event:
*
@ -840,12 +892,19 @@ _ic_process_key_event (BusInputContext *context,
/* ignore key events, if it is a fake input context */
if (context->has_focus && context->engine && context->fake == FALSE) {
ProcessKeyEventData *data = g_slice_new0 (ProcessKeyEventData);
data->invocation = invocation;
data->context = g_object_ref (context);
data->keyval = keyval;
data->keycode = keycode;
data->modifiers = modifiers;
bus_engine_proxy_process_key_event (context->engine,
keyval,
keycode,
modifiers,
(GAsyncReadyCallback) _ic_process_key_event_reply_cb,
invocation);
(GAsyncReadyCallback)
_ic_process_key_event_reply_cb,
data);
}
else {
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", FALSE));
@ -880,6 +939,13 @@ _ic_set_cursor_location (BusInputContext *context,
context->y,
context->w,
context->h);
if (context->emoji_extension) {
bus_panel_proxy_set_cursor_location (context->emoji_extension,
context->x,
context->y,
context->w,
context->h);
}
}
}
@ -912,6 +978,14 @@ _ic_set_cursor_location_relative (BusInputContext *context,
y,
w,
h);
if (context->emoji_extension) {
bus_panel_proxy_set_cursor_location_relative (
context->emoji_extension,
x,
y,
w,
h);
}
}
}
@ -1394,7 +1468,7 @@ bus_input_context_clear_preedit_text (BusInputContext *context)
/* always clear preedit text */
bus_input_context_update_preedit_text (context,
text_empty, 0, FALSE, IBUS_ENGINE_PREEDIT_CLEAR);
text_empty, 0, FALSE, IBUS_ENGINE_PREEDIT_CLEAR, TRUE);
}
void
@ -1407,7 +1481,10 @@ bus_input_context_focus_out (BusInputContext *context)
bus_input_context_clear_preedit_text (context);
bus_input_context_update_auxiliary_text (context, text_empty, FALSE);
bus_input_context_update_lookup_table (context, lookup_table_empty, FALSE);
bus_input_context_update_lookup_table (context,
lookup_table_empty,
FALSE,
FALSE);
bus_input_context_register_properties (context, props_empty);
if (context->engine) {
@ -1427,7 +1504,12 @@ bus_input_context_focus_out (BusInputContext *context)
{ \
g_assert (BUS_IS_INPUT_CONTEXT (context)); \
\
if (context->has_focus && context->engine) { \
if (context->is_extension_lookup_table && \
context->emoji_extension) { \
bus_panel_proxy_##name##_lookup_table (context->emoji_extension); \
return; \
} \
if (context->has_focus && context->engine) { \
bus_engine_proxy_##name (context->engine); \
} \
}
@ -1447,6 +1529,14 @@ bus_input_context_candidate_clicked (BusInputContext *context,
{
g_assert (BUS_IS_INPUT_CONTEXT (context));
if (context->is_extension_lookup_table && context->emoji_extension) {
bus_panel_proxy_candidate_clicked_lookup_table (
context->emoji_extension,
index,
button,
state);
return;
}
if (context->engine) {
bus_engine_proxy_candidate_clicked (context->engine,
index,
@ -1467,62 +1557,34 @@ bus_input_context_property_activate (BusInputContext *context,
}
}
/**
* bus_input_context_update_preedit_text:
*
* Update a preedit text. Send D-Bus signal to update status of client or send glib signal to the panel, depending on capabilities of the client.
*/
static void
bus_input_context_update_preedit_text (BusInputContext *context,
IBusText *text,
guint cursor_pos,
gboolean visible,
guint mode)
{
g_assert (BUS_IS_INPUT_CONTEXT (context));
if (context->preedit_text) {
g_object_unref (context->preedit_text);
}
context->preedit_text = (IBusText *) g_object_ref_sink (text ? text : text_empty);
context->preedit_cursor_pos = cursor_pos;
context->preedit_visible = visible;
context->preedit_mode = mode;
if (PREEDIT_CONDITION) {
GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)context->preedit_text);
bus_input_context_emit_signal (context,
"UpdatePreeditText",
g_variant_new ("(vub)", variant, context->preedit_cursor_pos, context->preedit_visible),
NULL);
}
else {
g_signal_emit (context,
context_signals[UPDATE_PREEDIT_TEXT],
0,
context->preedit_text,
context->preedit_cursor_pos,
context->preedit_visible);
}
}
/**
* bus_input_context_show_preedit_text:
*
* Show a preedit text. Send D-Bus signal to update status of client or send glib signal to the panel, depending on capabilities of the client.
*/
static void
bus_input_context_show_preedit_text (BusInputContext *context)
bus_input_context_show_preedit_text (BusInputContext *context,
gboolean is_extension)
{
g_assert (BUS_IS_INPUT_CONTEXT (context));
if (context->preedit_visible) {
if (context->preedit_visible)
return;
if (!is_extension && context->emoji_extension)
return;
if (!is_extension)
context->preedit_visible = TRUE;
if (context->emoji_extension && !is_extension) {
/* Do not use HIDE_PREEDIT_TEXT signal below but call
* bus_panel_proxy_hide_preedit_text() directly for the extension only
* but not for the normal panel.
*/
bus_panel_proxy_show_preedit_text (context->emoji_extension);
return;
}
context->preedit_visible = TRUE;
if (PREEDIT_CONDITION) {
bus_input_context_emit_signal (context,
"ShowPreeditText",
@ -1542,16 +1604,26 @@ bus_input_context_show_preedit_text (BusInputContext *context)
* Hide a preedit text. Send D-Bus signal to update status of client or send glib signal to the panel, depending on capabilities of the client.
*/
static void
bus_input_context_hide_preedit_text (BusInputContext *context)
bus_input_context_hide_preedit_text (BusInputContext *context,
gboolean is_extension)
{
g_assert (BUS_IS_INPUT_CONTEXT (context));
if (!context->preedit_visible) {
if (!is_extension && !context->preedit_visible)
return;
if (!is_extension)
context->preedit_visible = FALSE;
if (context->emoji_extension && !is_extension) {
/* Do not use HIDE_PREEDIT_TEXT signal below but call
* bus_panel_proxy_hide_preedit_text() directly for the extension only
* but not for the normal panel.
*/
bus_panel_proxy_hide_preedit_text (context->emoji_extension);
return;
}
context->preedit_visible = FALSE;
if (PREEDIT_CONDITION) {
bus_input_context_emit_signal (context,
"HidePreeditText",
@ -1658,19 +1730,15 @@ bus_input_context_hide_auxiliary_text (BusInputContext *context)
}
}
/**
* bus_input_context_update_lookup_table:
*
* Update contents in the lookup table.
* Send D-Bus signal to update status of client or send glib signal to the panel, depending on capabilities of the client.
*/
static void
void
bus_input_context_update_lookup_table (BusInputContext *context,
IBusLookupTable *table,
gboolean visible)
gboolean visible,
gboolean is_extension)
{
g_assert (BUS_IS_INPUT_CONTEXT (context));
context->is_extension_lookup_table = is_extension;
if (context->lookup_table) {
g_object_unref (context->lookup_table);
}
@ -2035,7 +2103,9 @@ _engine_update_preedit_text_cb (BusEngineProxy *engine,
g_assert (context->engine == engine);
bus_input_context_update_preedit_text (context, text, cursor_pos, visible, mode);
bus_input_context_update_preedit_text (context, text,
cursor_pos, visible, mode,
TRUE);
}
/**
@ -2075,7 +2145,7 @@ _engine_update_lookup_table_cb (BusEngineProxy *engine,
g_assert (context->engine == engine);
bus_input_context_update_lookup_table (context, table, visible);
bus_input_context_update_lookup_table (context, table, visible, FALSE);
}
/**
@ -2123,11 +2193,35 @@ _engine_update_property_cb (BusEngineProxy *engine,
* from the engine object.
*/
static void
_engine_panel_extension_cb (BusEngineProxy *engine,
GVariant *parameters,
BusInputContext *context)
_engine_panel_extension_cb (BusEngineProxy *engine,
IBusExtensionEvent *event,
BusInputContext *context)
{
g_signal_emit (context, context_signals[PANEL_EXTENSION], 0, parameters);
g_signal_emit (context, context_signals[PANEL_EXTENSION], 0, event);
}
static void
_engine_show_preedit_text_cb (BusEngineProxy *engine,
BusInputContext *context)
{
g_assert (BUS_IS_ENGINE_PROXY (engine));
g_assert (BUS_IS_INPUT_CONTEXT (context));
g_assert (context->engine == engine);
bus_input_context_show_preedit_text (context, FALSE);
}
static void
_engine_hide_preedit_text_cb (BusEngineProxy *engine,
BusInputContext *context)
{
g_assert (BUS_IS_ENGINE_PROXY (engine));
g_assert (BUS_IS_INPUT_CONTEXT (context));
g_assert (context->engine == engine);
bus_input_context_hide_preedit_text (context, FALSE);
}
#define DEFINE_FUNCTION(name) \
@ -2143,8 +2237,6 @@ _engine_panel_extension_cb (BusEngineProxy *engine,
bus_input_context_##name (context); \
}
DEFINE_FUNCTION (show_preedit_text)
DEFINE_FUNCTION (hide_preedit_text)
DEFINE_FUNCTION (show_auxiliary_text)
DEFINE_FUNCTION (hide_auxiliary_text)
DEFINE_FUNCTION (show_lookup_table)
@ -2239,7 +2331,10 @@ bus_input_context_disable (BusInputContext *context)
bus_input_context_clear_preedit_text (context);
bus_input_context_update_auxiliary_text (context, text_empty, FALSE);
bus_input_context_update_lookup_table (context, lookup_table_empty, FALSE);
bus_input_context_update_lookup_table (context,
lookup_table_empty,
FALSE,
FALSE);
bus_input_context_register_properties (context, props_empty);
if (context->engine) {
@ -2283,7 +2378,10 @@ bus_input_context_unset_engine (BusInputContext *context)
bus_input_context_clear_preedit_text (context);
bus_input_context_update_auxiliary_text (context, text_empty, FALSE);
bus_input_context_update_lookup_table (context, lookup_table_empty, FALSE);
bus_input_context_update_lookup_table (context,
lookup_table_empty,
FALSE,
FALSE);
bus_input_context_register_properties (context, props_empty);
if (context->engine) {
@ -2639,17 +2737,128 @@ bus_input_context_set_content_type (BusInputContext *context,
}
void
bus_input_context_commit_text (BusInputContext *context,
IBusText *text)
bus_input_context_commit_text_use_extension (BusInputContext *context,
IBusText *text,
gboolean use_extension)
{
g_assert (BUS_IS_INPUT_CONTEXT (context));
if (text == text_empty || text == NULL)
return;
GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)text);
bus_input_context_emit_signal (context,
"CommitText",
g_variant_new ("(v)", variant),
NULL);
if (use_extension && context->emoji_extension) {
bus_panel_proxy_commit_text_received (context->emoji_extension, text);
} else {
GVariant *variant = ibus_serializable_serialize (
(IBusSerializable *)text);
bus_input_context_emit_signal (context,
"CommitText",
g_variant_new ("(v)", variant),
NULL);
}
}
void
bus_input_context_commit_text (BusInputContext *context,
IBusText *text)
{
bus_input_context_commit_text_use_extension (context, text, TRUE);
}
void
bus_input_context_update_preedit_text (BusInputContext *context,
IBusText *text,
guint cursor_pos,
gboolean visible,
guint mode,
gboolean use_extension)
{
gboolean extension_visible = FALSE;
g_assert (BUS_IS_INPUT_CONTEXT (context));
if (context->preedit_text) {
g_object_unref (context->preedit_text);
}
context->preedit_text = (IBusText *) g_object_ref_sink (text ? text :
text_empty);
context->preedit_cursor_pos = cursor_pos;
if (use_extension)
context->preedit_visible = visible;
if (use_extension)
context->preedit_mode = mode;
extension_visible = context->preedit_visible |
(context->emoji_extension != NULL);
if (use_extension && context->emoji_extension) {
bus_panel_proxy_update_preedit_text (context->emoji_extension,
context->preedit_text,
context->preedit_cursor_pos,
context->preedit_visible);
} else if (PREEDIT_CONDITION) {
GVariant *variant = ibus_serializable_serialize (
(IBusSerializable *)context->preedit_text);
bus_input_context_emit_signal (context,
"UpdatePreeditText",
g_variant_new (
"(vub)",
variant,
context->preedit_cursor_pos,
extension_visible),
NULL);
} else {
g_signal_emit (context,
context_signals[UPDATE_PREEDIT_TEXT],
0,
context->preedit_text,
context->preedit_cursor_pos,
extension_visible);
}
}
void
bus_input_context_set_emoji_extension (BusInputContext *context,
BusPanelProxy *emoji_extension)
{
g_assert (BUS_IS_INPUT_CONTEXT (context));
if (context->emoji_extension)
g_object_unref (context->emoji_extension);
context->emoji_extension = emoji_extension;
if (emoji_extension) {
g_object_ref (context->emoji_extension);
if (!context->connection)
return;
bus_input_context_show_preedit_text (context, TRUE);
bus_panel_proxy_set_cursor_location (context->emoji_extension,
context->x,
context->y,
context->w,
context->h);
} else {
if (!context->connection)
return;
/* https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/113
* Cannot use bus_input_context_hide_preedit_text () yet.
*/
if (!context->preedit_visible) {
bus_input_context_update_preedit_text (context,
text_empty,
0,
FALSE,
IBUS_ENGINE_PREEDIT_CLEAR,
FALSE);
}
}
}
void
bus_input_context_panel_extension_received (BusInputContext *context,
IBusExtensionEvent *event)
{
g_assert (BUS_IS_INPUT_CONTEXT (context));
if (!context->engine)
return;
bus_engine_proxy_panel_extension_received (context->engine, event);
}

Просмотреть файл

@ -28,6 +28,11 @@
#include "connection.h"
#include "factoryproxy.h"
#ifndef __BUS_PANEL_PROXY_DEFINED
#define __BUS_PANEL_PROXY_DEFINED
typedef struct _BusPanelProxy BusPanelProxy;
#endif
/*
* Type macros.
*/
@ -63,6 +68,7 @@ BusInputContext *bus_input_context_new (BusConnection *connection,
/**
* bus_input_context_focus_in:
* @context: A #BusInputContext.
*
* Give a focus to the context. Call FocusIn, Enable, SetCapabilities,
* and SetCursorLocation methods of the engine for the context,
@ -73,6 +79,7 @@ void bus_input_context_focus_in (BusInputContext *context);
/**
* bus_input_context_focus_out:
* @context: A #BusInputContext.
*
* Remove a focus from the context. Call FocusOut method of the engine for
* the context.
@ -83,6 +90,7 @@ void bus_input_context_focus_out
/**
* bus_input_context_has_focus:
* @context: A #BusInputContext.
* @returns: context->has_focus.
*/
gboolean bus_input_context_has_focus
@ -90,6 +98,7 @@ gboolean bus_input_context_has_focus
/**
* bus_input_context_enable:
* @context: A #BusInputContext.
*
* Enable the current engine for the context. Request an engine (if needed),
* call FocusIn, Enable, SetCapabilities, and SetCursorLocation methods
@ -100,6 +109,7 @@ void bus_input_context_enable (BusInputContext *context);
/**
* bus_input_context_disable:
* @context: A #BusInputContext.
*
* Disable the current engine for the context. Request an engine (if needed),
* call FocusIn, Enable, SetCapabilities, and SetCursorLocation methods
@ -110,6 +120,7 @@ void bus_input_context_disable (BusInputContext *context);
/**
* bus_input_context_page_up:
* @context: A #BusInputContext.
*
* Call page_up method of the current engine proxy.
*/
@ -117,6 +128,7 @@ void bus_input_context_page_up (BusInputContext *context);
/**
* bus_input_context_page_down:
* @context: A #BusInputContext.
*
* Call page_down method of the current engine proxy.
*/
@ -125,6 +137,7 @@ void bus_input_context_page_down
/**
* bus_input_context_cursor_up:
* @context: A #BusInputContext.
*
* Call cursor_up method of the current engine proxy.
*/
@ -133,6 +146,7 @@ void bus_input_context_cursor_up
/**
* bus_input_context_cursor_down:
* @context: A #BusInputContext.
*
* Call cursor_down method of the current engine proxy.
*/
@ -141,6 +155,10 @@ void bus_input_context_cursor_down
/**
* bus_input_context_candidate_clicked:
* @context: A #BusInputContext.
* @index: An index.
* @button: A button number.
* @state: A button state.
*
* Call candidate_clicked method of the current engine proxy.
*/
@ -152,6 +170,8 @@ void bus_input_context_candidate_clicked
/**
* bus_input_context_set_engine:
* @context: A #BusInputContext.
* @engine: A #BusEngineProxy.
*
* Use the engine on the context.
*/
@ -161,12 +181,14 @@ void bus_input_context_set_engine
/**
* bus_input_context_set_engine_by_desc:
* @context: A #BusInputContext.
* @desc: the engine to use on the context.
* @timeout: timeout (in ms) for D-Bus calls.
* @callback: a function to be called when bus_input_context_set_engine_by_desc
* is finished. if NULL, the default callback
* function, which just calls
* bus_input_context_set_engine_by_desc_finish, is used.
* @user_data: an argument of @callback.
*
* Create a new BusEngineProxy object and use it on the context.
*/
@ -181,6 +203,9 @@ void bus_input_context_set_engine_by_desc
/**
* bus_input_context_set_engine_by_desc_finish:
* @context: A #BusInputContext.
* @res: A #GAsyncResult.
* @error: A #GError.
*
* A function to be called by the GAsyncReadyCallback function for
* bus_input_context_set_engine_by_desc.
@ -192,6 +217,7 @@ gboolean bus_input_context_set_engine_by_desc_finish
/**
* bus_input_context_get_engine:
* @context: A #BusInputContext.
*
* Get a BusEngineProxy object of the current engine.
*/
@ -200,6 +226,7 @@ BusEngineProxy *bus_input_context_get_engine
/**
* bus_input_context_get_engine_desc:
* @context: A #BusInputContext.
*
* Get an IBusEngineDesc object of the current engine.
*/
@ -208,6 +235,9 @@ IBusEngineDesc *bus_input_context_get_engine_desc
/**
* bus_input_context_property_activate:
* @context: A #BusInputContext.
* @prop_name: A property name.
* @prop_state: A property state.
*
* Call property_activate method of the current engine proxy.
*/
@ -219,6 +249,7 @@ void bus_input_context_property_activate
/**
* bus_input_context_get_capabilities:
* @context: A #BusInputContext.
* @returns: context->capabilities.
*/
guint bus_input_context_get_capabilities
@ -226,6 +257,8 @@ guint bus_input_context_get_capabilities
/**
* bus_input_context_set_capabilities:
* @context: A #BusInputContext.
* @capabilities: capabilities.
*
* Call set_capabilities method of the current engine proxy.
*/
@ -236,6 +269,7 @@ void bus_input_context_set_capabilities
/**
* bus_input_context_get_client:
* @context: A #BusInputContext.
* @returns: context->client.
*/
const gchar *bus_input_context_get_client
@ -243,6 +277,7 @@ const gchar *bus_input_context_get_client
/**
* bus_input_context_get_content_type:
* @context: A #BusInputContext.
* @purpose: Input purpose.
* @hints: Input hints.
*/
@ -253,6 +288,7 @@ void bus_input_context_get_content_type
/**
* bus_input_context_set_content_type:
* @context: A #BusInputContext.
* @purpose: Input purpose.
* @hints: Input hints.
*/
@ -263,11 +299,83 @@ void bus_input_context_set_content_type
/**
* bus_input_context_commit_text:
* @text: a commited text.
* @context: A #BusInputContext.
* @text: A committed text.
*/
void bus_input_context_commit_text
(BusInputContext *context,
IBusText *text);
/**
* bus_input_context_commit_text:
* @context: A #BusInputContext.
* @text: A committed text.
* @use_extension: Use an extension if it's %TRUE and the extension is
* available.
*/
void bus_input_context_commit_text_use_extension
(BusInputContext *context,
IBusText *text,
gboolean use_extension);
/**
* bus_input_context_set_emoji_extension:
* @context: A #BusInputContext.
* @extension: A #BusPanelProxy.
*/
void bus_input_context_set_emoji_extension
(BusInputContext *context,
BusPanelProxy *extension);
/**
* bus_input_context_update_preedit_text:
* @context: A #BusInputContext.
* @text: An #IBusText.
* @cursor_pos: The cursor position.
* @visible: %TRUE if the preedit is visible. Otherwise %FALSE.
* @mode: The preedit commit mode.
* @use_extension: %TRUE if preedit text is sent to the extesion at first.
*
* Update a preedit text. Send D-Bus signal to update status of client or
* send glib signal to the panel, depending on capabilities of the client.
*/
void bus_input_context_update_preedit_text
(BusInputContext *context,
IBusText *text,
guint cursor_pos,
gboolean visible,
guint mode,
gboolean
use_extension);
/**
* bus_input_context_update_lookup_table:
* @context: A #BusInputContext.
* @table: An #IBusTable.
* @visible: %TRUE if the lookup table is visible. Otherwise %FALSE.
* @is_extension: %TRUE if the lookup table is created by panel extensions.
*
* Update contents in the lookup table.
* Send D-Bus signal to update status of client or send glib signal to the
* panel, depending on capabilities of the client.
*/
void bus_input_context_update_lookup_table
(BusInputContext *context,
IBusLookupTable *table,
gboolean visible,
gboolean
is_extension);
/**
* bus_input_context_panel_extension_received:
* @context: A #BusInputContext.
* @event: An #IBusExtensionEvent.
*
* Send An #IBusExtensionEvent callback from an extension.
*/
void bus_input_context_panel_extension_received
(BusInputContext *context,
IBusExtensionEvent *event);
G_END_DECLS
#endif

Просмотреть файл

@ -52,6 +52,10 @@ enum {
PROPERTY_HIDE,
COMMIT_TEXT,
PANEL_EXTENSION,
PANEL_EXTENSION_REGISTER_KEYS,
UPDATE_PREEDIT_TEXT_RECEIVED,
UPDATE_LOOKUP_TABLE_RECEIVED,
UPDATE_AUXILIARY_TEXT_RECEIVED,
LAST_SIGNAL,
};
@ -125,8 +129,8 @@ bus_panel_proxy_new (BusConnection *connection,
case PANEL_TYPE_PANEL:
path = IBUS_PATH_PANEL;
break;
case PANEL_TYPE_EXTENSION:
path = IBUS_PATH_PANEL_EXTENSION;
case PANEL_TYPE_EXTENSION_EMOJI:
path = IBUS_PATH_PANEL_EXTENSION_EMOJI;
break;
default:
g_return_val_if_reached (NULL);
@ -253,6 +257,16 @@ bus_panel_proxy_class_init (BusPanelProxyClass *class)
panel_signals[PANEL_EXTENSION] =
g_signal_new (I_("panel-extension"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
bus_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
IBUS_TYPE_EXTENSION_EVENT);
panel_signals[PANEL_EXTENSION_REGISTER_KEYS] =
g_signal_new (I_("panel-extension-register-keys"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
@ -260,6 +274,40 @@ bus_panel_proxy_class_init (BusPanelProxyClass *class)
bus_marshal_VOID__VARIANT,
G_TYPE_NONE, 1,
G_TYPE_VARIANT);
panel_signals[UPDATE_PREEDIT_TEXT_RECEIVED] =
g_signal_new (I_("update-preedit-text-received"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
bus_marshal_VOID__OBJECT_UINT_BOOLEAN,
G_TYPE_NONE, 3,
IBUS_TYPE_TEXT,
G_TYPE_UINT,
G_TYPE_BOOLEAN);
panel_signals[UPDATE_LOOKUP_TABLE_RECEIVED] =
g_signal_new (I_("update-lookup-table-received"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
bus_marshal_VOID__OBJECT_BOOLEAN,
G_TYPE_NONE, 2,
IBUS_TYPE_LOOKUP_TABLE,
G_TYPE_BOOLEAN);
panel_signals[UPDATE_AUXILIARY_TEXT_RECEIVED] =
g_signal_new (I_("update-auxiliary-text-received"),
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
bus_marshal_VOID__OBJECT_BOOLEAN,
G_TYPE_NONE, 2,
IBUS_TYPE_TEXT,
G_TYPE_BOOLEAN);
}
static void
@ -355,23 +403,83 @@ bus_panel_proxy_g_signal (GDBusProxy *proxy,
if (g_strcmp0 ("CommitText", signal_name) == 0) {
GVariant *arg0 = NULL;
g_variant_get (parameters, "(v)", &arg0);
g_return_if_fail (arg0 != NULL);
g_variant_get (parameters, "(v)", &arg0);
g_return_if_fail (arg0);
IBusText *text = IBUS_TEXT (ibus_serializable_deserialize (arg0));
g_variant_unref (arg0);
g_return_if_fail (text != NULL);
g_return_if_fail (text);
g_signal_emit (panel, panel_signals[COMMIT_TEXT], 0, text);
_g_object_unref_if_floating (text);
return;
}
if (g_strcmp0 ("PanelExtension", signal_name) == 0) {
if (panel->panel_type != PANEL_TYPE_PANEL) {
g_warning ("Wrong signal");
return;
}
g_signal_emit (panel, panel_signals[PANEL_EXTENSION], 0, parameters);
GVariant *arg0 = NULL;
g_variant_get (parameters, "(v)", &arg0);
g_return_if_fail (arg0);
IBusExtensionEvent *event = IBUS_EXTENSION_EVENT (
ibus_serializable_deserialize (arg0));
g_variant_unref (arg0);
g_return_if_fail (event);
g_signal_emit (panel, panel_signals[PANEL_EXTENSION], 0, event);
_g_object_unref_if_floating (event);
return;
}
if (g_strcmp0 ("PanelExtensionRegisterKeys", signal_name) == 0) {
g_signal_emit (panel, panel_signals[PANEL_EXTENSION_REGISTER_KEYS], 0,
parameters);
return;
}
if (g_strcmp0 ("UpdatePreeditTextReceived", signal_name) == 0) {
GVariant *variant = NULL;
guint cursor_pos = 0;
gboolean visible = FALSE;
IBusText *text = NULL;
g_variant_get (parameters, "(vub)", &variant, &cursor_pos, &visible);
g_return_if_fail (variant);
text = (IBusText *) ibus_serializable_deserialize (variant);
g_variant_unref (variant);
g_return_if_fail (text);
g_signal_emit (panel, panel_signals[UPDATE_PREEDIT_TEXT_RECEIVED], 0,
text, cursor_pos, visible);
_g_object_unref_if_floating (text);
return;
}
if (g_strcmp0 ("UpdateLookupTableReceived", signal_name) == 0) {
GVariant *variant = NULL;
gboolean visible = FALSE;
IBusLookupTable *table = NULL;
g_variant_get (parameters, "(vb)", &variant, &visible);
g_return_if_fail (variant);
table = (IBusLookupTable *) ibus_serializable_deserialize (variant);
g_variant_unref (variant);
g_return_if_fail (table);
g_signal_emit (panel, panel_signals[UPDATE_LOOKUP_TABLE_RECEIVED], 0,
table, visible);
_g_object_unref_if_floating (table);
return;
}
if (g_strcmp0 ("UpdateAuxiliaryTextReceived", signal_name) == 0) {
GVariant *variant = NULL;
gboolean visible = FALSE;
IBusText *text = NULL;
g_variant_get (parameters, "(vb)", &variant, &visible);
g_return_if_fail (variant);
text = (IBusText *) ibus_serializable_deserialize (variant);
g_variant_unref (variant);
g_return_if_fail (text);
g_signal_emit (panel, panel_signals[UPDATE_AUXILIARY_TEXT_RECEIVED], 0,
text, visible);
_g_object_unref_if_floating (text);
return;
}
@ -552,12 +660,17 @@ static void
bus_panel_proxy_commit_text (BusPanelProxy *panel,
IBusText *text)
{
gboolean use_extension = TRUE;
g_assert (BUS_IS_PANEL_PROXY (panel));
g_assert (text != NULL);
if (panel->focused_context) {
bus_input_context_commit_text (panel->focused_context, text);
}
if (!panel->focused_context)
return;
if (panel->panel_type != PANEL_TYPE_PANEL)
use_extension = FALSE;
bus_input_context_commit_text_use_extension (panel->focused_context,
text,
use_extension);
}
#define DEFINE_FUNCTION(Name, name) \
@ -877,3 +990,74 @@ bus_panel_proxy_get_panel_type (BusPanelProxy *panel)
g_assert (BUS_IS_PANEL_PROXY (panel));
return panel->panel_type;
}
void
bus_panel_proxy_panel_extension_received (BusPanelProxy *panel,
IBusExtensionEvent *event)
{
GVariant *data;
g_assert (BUS_IS_PANEL_PROXY (panel));
g_assert (event);
data = ibus_serializable_serialize (IBUS_SERIALIZABLE (event));
g_return_if_fail (data);
g_dbus_proxy_call ((GDBusProxy *)panel,
"PanelExtensionReceived",
g_variant_new ("(v)", data),
G_DBUS_CALL_FLAGS_NONE,
-1, NULL, NULL, NULL);
}
void
bus_panel_proxy_process_key_event (BusPanelProxy *panel,
guint keyval,
guint keycode,
guint state,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_assert (BUS_IS_PANEL_PROXY (panel));
g_dbus_proxy_call ((GDBusProxy *)panel,
"ProcessKeyEvent",
g_variant_new ("(uuu)", keyval, keycode, state),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
callback,
user_data);
}
void
bus_panel_proxy_commit_text_received (BusPanelProxy *panel,
IBusText *text)
{
GVariant *variant;
g_assert (BUS_IS_PANEL_PROXY (panel));
g_assert (IBUS_IS_TEXT (text));
variant = ibus_serializable_serialize (IBUS_SERIALIZABLE (text));
g_dbus_proxy_call ((GDBusProxy *)panel,
"CommitTextReceived",
g_variant_new ("(v)", variant),
G_DBUS_CALL_FLAGS_NONE,
-1, NULL, NULL, NULL);
}
void
bus_panel_proxy_candidate_clicked_lookup_table (BusPanelProxy *panel,
guint index,
guint button,
guint state)
{
gboolean use_extension = TRUE;
g_assert (BUS_IS_PANEL_PROXY (panel));
g_dbus_proxy_call ((GDBusProxy *)panel,
"CandidateClickedLookupTable",
g_variant_new ("(uuu)", index, button, state),
G_DBUS_CALL_FLAGS_NONE,
-1, NULL, NULL, NULL);
}

Просмотреть файл

@ -55,7 +55,7 @@ typedef enum
{
PANEL_TYPE_NONE,
PANEL_TYPE_PANEL,
PANEL_TYPE_EXTENSION
PANEL_TYPE_EXTENSION_EMOJI
} PanelType;
typedef struct _BusPanelProxy BusPanelProxy;
@ -135,6 +135,27 @@ void bus_panel_proxy_set_content_type
guint hints);
PanelType bus_panel_proxy_get_panel_type
(BusPanelProxy *panel);
void bus_panel_proxy_panel_extension_received
(BusPanelProxy *panel,
IBusExtensionEvent
*event);
void bus_panel_proxy_process_key_event
(BusPanelProxy *panel,
guint keyval,
guint keycode,
guint state,
GAsyncReadyCallback
callback,
gpointer user_data);
void bus_panel_proxy_commit_text_received
(BusPanelProxy *panel,
IBusText *text);
void bus_panel_proxy_candidate_clicked_lookup_table
(BusPanelProxy *panel,
guint index,
guint button,
guint state);
G_END_DECLS
#endif

Просмотреть файл

@ -353,6 +353,18 @@
<long>Custom font name for language panel</long>
</locale>
</schema>
<schema>
<key>/schemas/desktop/ibus/panel/emoji/unicode-hotkey</key>
<applyto>/desktop/ibus/panel/emoji/unicode-hotkey</applyto>
<owner>ibus</owner>
<type>list</type>
<list_type>string</list_type>
<default>[&lt;Control&gt;&lt;Shift&gt;u]</default>
<locale name="C">
<short>Unicode shortcut keys for gtk_accelerator_parse</short>
<long>The shortcut keys for turning Unicode typing on or off</long>
</locale>
</schema>
<schema>
<key>/schemas/desktop/ibus/panel/emoji/hotkey</key>
<applyto>/desktop/ibus/panel/emoji/hotkey</applyto>

Просмотреть файл

@ -4,7 +4,7 @@
# ibus - The Input Bus
#
# Copyright (c) 2007-2016 Peng Huang <shawn.p.huang@gmail.com>
# Copyright (c) 2010-2017 Takao Fujiwara <takao.fujiwara1@gmail.com>
# Copyright (c) 2010-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
# Copyright (c) 2007-2016 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
@ -123,10 +123,15 @@ class Setup(object):
name = 'emoji'
label = 'emoji_dialog'
self.__init_hotkey(name, label)
name = 'unicode'
label = 'unicode_dialog'
self.__init_hotkey(name, label)
def __init_hotkey(self, name, label, comment=None):
if name == 'emoji':
shortcuts = self.__settings_emoji.get_strv('hotkey')
elif name == 'unicode':
shortcuts = self.__settings_emoji.get_strv('unicode-hotkey')
else:
shortcuts = self.__settings_hotkey.get_strv(name)
button = self.__builder.get_object("button_%s" % label)
@ -139,6 +144,9 @@ class Setup(object):
if name == 'emoji':
button.connect("clicked", self.__shortcut_button_clicked_cb,
'hotkey', 'panel/' + name, label, entry)
elif name == 'unicode':
button.connect("clicked", self.__shortcut_button_clicked_cb,
'unicode-hotkey', 'panel/emoji', label, entry)
else:
button.connect("clicked", self.__shortcut_button_clicked_cb,
name, "general/hotkey", label, entry)

Просмотреть файл

@ -870,9 +870,9 @@
<object class="GtkLabel" id="label_emoji1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">The shortcut keys for showing emoji dialog</property>
<property name="tooltip_text" translatable="yes">The shortcut keys to enable conversions of emoji annotations or Unicode names</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Emoji choice:</property>
<property name="label" translatable="yes">Emoji annotation:</property>
</object>
<packing>
<property name="left_attach">0</property>
@ -920,6 +920,60 @@
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_unicode1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">The shortcut keys to enable Unicode code point conversions</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Unicode code point:</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="hbox_unicode1">
<property name="orientation">horizontal</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<property name="hexpand">true</property>
<child>
<object class="GtkEntry" id="entry_unicode_dialog">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button_unicode_dialog">
<property name="label" translatable="yes">...</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
</child>
<child type="label">

Просмотреть файл

@ -64,8 +64,6 @@ enum {
};
typedef struct _IBusEngineKeybinding IBusEngineKeybinding;
/* IBusEnginePriv */
struct _IBusEnginePrivate {
gchar *engine_name;
@ -81,14 +79,11 @@ struct _IBusEnginePrivate {
guint content_purpose;
guint content_hints;
GSettings *settings_emoji;
IBusEngineKeybinding **emoji_keybindings;
GHashTable *extension_keybindings;
gboolean enable_extension;
gchar *current_extension_name;
};
struct _IBusEngineKeybinding {
guint keyval;
IBusModifierType modifiers;
};
static guint engine_signals[LAST_SIGNAL] = { 0 };
@ -191,10 +186,6 @@ static void ibus_engine_dbus_property_changed
const gchar *property_name,
GVariant *value);
static void ibus_engine_keybinding_free (IBusEngine *engine);
static void settings_emoji_hotkey_changed_cb
(GSettings *settings,
const gchar *key,
gpointer data);
G_DEFINE_TYPE (IBusEngine, ibus_engine, IBUS_TYPE_SERVICE)
@ -253,6 +244,12 @@ static const gchar introspection_xml[] =
" <arg direction='in' type='u' name='cursor_pos' />"
" <arg direction='in' type='u' name='anchor_pos' />"
" </method>"
" <method name='PanelExtensionReceived'>"
" <arg direction='in' type='v' name='event' />"
" </method>"
" <method name='PanelExtensionRegisterKeys'>"
" <arg direction='in' type='v' name='data' />"
" </method>"
/* FIXME signals */
" <signal name='CommitText'>"
" <arg type='v' name='text' />"
@ -309,16 +306,22 @@ ibus_engine_class_init (IBusEngineClass *class)
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (class);
gobject_class->set_property = (GObjectSetPropertyFunc) ibus_engine_set_property;
gobject_class->get_property = (GObjectGetPropertyFunc) ibus_engine_get_property;
gobject_class->set_property =
(GObjectSetPropertyFunc) ibus_engine_set_property;
gobject_class->get_property =
(GObjectGetPropertyFunc) ibus_engine_get_property;
ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_engine_destroy;
IBUS_SERVICE_CLASS (class)->service_method_call = ibus_engine_service_method_call;
IBUS_SERVICE_CLASS (class)->service_get_property = ibus_engine_service_get_property;
IBUS_SERVICE_CLASS (class)->service_set_property = ibus_engine_service_set_property;
IBUS_SERVICE_CLASS (class)->service_method_call =
ibus_engine_service_method_call;
IBUS_SERVICE_CLASS (class)->service_get_property =
ibus_engine_service_get_property;
IBUS_SERVICE_CLASS (class)->service_set_property =
ibus_engine_service_set_property;
ibus_service_class_add_interfaces (IBUS_SERVICE_CLASS (class), introspection_xml);
ibus_service_class_add_interfaces (IBUS_SERVICE_CLASS (class),
introspection_xml);
class->process_key_event = ibus_engine_process_key_event;
class->focus_in = ibus_engine_focus_in;
@ -839,26 +842,25 @@ ibus_engine_init (IBusEngine *engine)
{
IBusEnginePrivate *priv;
engine->priv = priv = IBUS_ENGINE_GET_PRIVATE (engine);
priv->surrounding_text = g_object_ref_sink (text_empty);
priv->settings_emoji =
g_settings_new ("org.freedesktop.ibus.panel.emoji");
settings_emoji_hotkey_changed_cb (priv->settings_emoji, "hotkey", engine);
g_signal_connect (priv->settings_emoji, "changed::hotkey",
G_CALLBACK (settings_emoji_hotkey_changed_cb), engine);
priv->extension_keybindings = g_hash_table_new_full (
g_str_hash,
g_str_equal,
g_free,
g_free);
}
static void
ibus_engine_destroy (IBusEngine *engine)
{
g_free (engine->priv->engine_name);
engine->priv->engine_name = NULL;
IBusEnginePrivate *priv = engine->priv;
if (engine->priv->surrounding_text) {
g_object_unref (engine->priv->surrounding_text);
engine->priv->surrounding_text = NULL;
}
ibus_engine_keybinding_free (engine);
g_clear_pointer (&priv->engine_name, g_free);
g_clear_pointer (&priv->current_extension_name, g_free);
if (priv->surrounding_text)
g_clear_object (&priv->surrounding_text);
if (priv->extension_keybindings)
g_clear_pointer (&priv->extension_keybindings, g_hash_table_destroy);
IBUS_OBJECT_CLASS(ibus_engine_parent_class)->destroy (IBUS_OBJECT (engine));
}
@ -895,19 +897,38 @@ ibus_engine_get_property (IBusEngine *engine,
}
static void
ibus_engine_panel_extension (IBusEngine *engine)
ibus_engine_panel_extension (IBusEngine *engine,
const gchar *name)
{
IBusXEvent *xevent = ibus_x_event_new (
"event-type", IBUS_X_EVENT_KEY_PRESS,
"purpose", "emoji",
IBusEnginePrivate *priv;
IBusExtensionEvent *event;
GVariant *data;
g_assert (IBUS_IS_ENGINE (engine));
g_assert (name);
priv = engine->priv;
if (!g_strcmp0 (name, priv->current_extension_name))
priv->enable_extension = !priv->enable_extension;
else
priv->enable_extension = TRUE;
if (priv->enable_extension) {
g_free (priv->current_extension_name);
priv->current_extension_name = g_strdup (name);
}
event = ibus_extension_event_new (
"name", name,
"is-enabled", priv->enable_extension,
NULL);
GVariant *data = ibus_serializable_serialize_object (
IBUS_SERIALIZABLE (xevent));
g_assert (IBUS_IS_EXTENSION_EVENT (event));
data = ibus_serializable_serialize_object (
IBUS_SERIALIZABLE (event));
g_assert (data != NULL);
ibus_engine_emit_signal (engine,
"PanelExtension",
g_variant_new ("(v)", data));
g_object_unref (event);
}
static gboolean
@ -917,7 +938,8 @@ ibus_engine_filter_key_event (IBusEngine *engine,
guint state)
{
IBusEnginePrivate *priv;
int i;
GList *names, *n;
IBusProcessKeyEventData *keys;
guint modifiers;
if ((state & IBUS_RELEASE_MASK) != 0)
@ -925,22 +947,29 @@ ibus_engine_filter_key_event (IBusEngine *engine,
g_return_val_if_fail (IBUS_IS_ENGINE (engine), FALSE);
priv = engine->priv;
if (!priv->emoji_keybindings)
return FALSE;
modifiers = state & IBUS_MODIFIER_FILTER;
if (keyval >= IBUS_KEY_A && keyval <= IBUS_KEY_Z &&
(modifiers & IBUS_SHIFT_MASK) != 0) {
keyval = keyval - IBUS_KEY_A + IBUS_KEY_a;
}
for (i = 0; priv->emoji_keybindings[i]; i++) {
IBusEngineKeybinding *binding = priv->emoji_keybindings[i];
if (binding->keyval == keyval &&
binding->modifiers == modifiers) {
ibus_engine_panel_extension (engine);
return TRUE;
names = g_hash_table_get_keys (priv->extension_keybindings);
if (!names)
return FALSE;
for (n = names; n; n = n->next) {
const gchar *name = (const gchar *)n->data;
keys = g_hash_table_lookup (priv->extension_keybindings, name);
for (; keys; keys++) {
if (keys->keyval == 0 && keys->keycode == 0 && keys->state == 0)
break;
if (keys->keyval == keyval &&
keys->state == modifiers &&
(keys->keycode == 0 || keys->keycode == keycode)) {
ibus_engine_panel_extension (engine, name);
return TRUE;
}
}
}
g_list_free (names);
return FALSE;
}
@ -953,6 +982,97 @@ ibus_engine_service_authorized_method (IBusService *service,
return FALSE;
}
static void
ibus_engine_service_panel_extension_register_keys (IBusEngine *engine,
GVariant *parameters,
GDBusMethodInvocation
*invocation)
{
IBusEnginePrivate *priv = engine->priv;
GVariant *v1 = NULL;
GVariant *v2 = NULL;
GVariant *v3 = NULL;
GVariant *vkeys = NULL;
GVariantIter *iter1 = NULL;
GVariantIter *iter2 = NULL;
const gchar *name = NULL;
guint failure_id = 0;
g_variant_get (parameters, "(v)", &v1);
if (v1)
g_variant_get (v1, "(v)", &v2);
else
failure_id = 1;
if (v2)
g_variant_get (v2, "a{sv}", &iter1);
else
failure_id = 2;
if (iter1) {
while (g_variant_iter_loop (iter1, "{&sv}", &name, &vkeys)) {
if (vkeys)
g_variant_get (vkeys, "av", &iter2);
if (name && iter2) {
IBusProcessKeyEventData *keys = NULL;
gint num = 0;
while (g_variant_iter_loop (iter2, "v", &v3)) {
if (v3) {
guint keyval = 0;
guint keycode = 0;
guint state = 0;
g_variant_get (v3, "(iii)",
&keyval, &keycode, &state);
if (!keys)
keys = g_new0 (IBusProcessKeyEventData, 2);
else
keys = g_renew (IBusProcessKeyEventData,
keys,
num + 2);
keys[num].keyval = keyval;
keys[num].keycode = keycode;
keys[num].state = state;
keys[num + 1].keyval = 0;
keys[num + 1].keycode = 0;
keys[num + 1].state = 0;
g_clear_pointer (&v3, g_variant_unref);
num++;
} else {
failure_id = 5;
}
}
if (num > 0) {
g_hash_table_replace (priv->extension_keybindings,
g_strdup (name),
keys);
} else {
g_hash_table_remove (priv->extension_keybindings, name);
}
g_clear_pointer (&iter2, g_variant_iter_free);
} else {
failure_id = 4;
}
g_clear_pointer (&vkeys, g_variant_unref);
name = NULL;
}
g_variant_iter_free (iter1);
} else {
failure_id = 3;
}
if (failure_id == 0) {
g_dbus_method_invocation_return_value (invocation, NULL);
} else {
g_dbus_method_invocation_return_error (
invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"PanelExtensionRegisterKeys method gives NULL: %d",
failure_id);
}
if (v2)
g_variant_unref (v2);
if (v1)
g_variant_unref (v1);
}
static void
ibus_engine_service_method_call (IBusService *service,
GDBusConnection *connection,
@ -964,6 +1084,7 @@ ibus_engine_service_method_call (IBusService *service,
GDBusMethodInvocation *invocation)
{
IBusEngine *engine = IBUS_ENGINE (service);
IBusEnginePrivate *priv = engine->priv;
if (g_strcmp0 (interface_name, IBUS_INTERFACE_ENGINE) != 0) {
IBUS_SERVICE_CLASS (ibus_engine_parent_class)->
@ -1002,6 +1123,33 @@ ibus_engine_service_method_call (IBusService *service,
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", retval));
return;
}
if (g_strcmp0 (method_name, "PanelExtensionReceived") == 0) {
GVariant *arg0 = NULL;
IBusExtensionEvent *event = NULL;
g_variant_get (parameters, "(v)", &arg0);
if (arg0) {
event = (IBusExtensionEvent *)ibus_serializable_deserialize_object (
arg0);
}
if (!event) {
g_dbus_method_invocation_return_error (
invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"PanelExtensionReceived method gives NULL");
return;
}
priv->enable_extension = ibus_extension_event_is_enabled (event);
g_dbus_method_invocation_return_value (invocation, NULL);
return;
}
if (g_strcmp0 (method_name, "PanelExtensionRegisterKeys") == 0) {
ibus_engine_service_panel_extension_register_keys (engine,
parameters,
invocation);
return;
}
static const struct {
gchar *member;
@ -1441,73 +1589,10 @@ static void
ibus_engine_keybinding_free (IBusEngine *engine)
{
IBusEnginePrivate *priv;
int i;
g_return_if_fail (IBUS_IS_ENGINE (engine));
priv = engine->priv;
if (priv->emoji_keybindings) {
for (i = 0; priv->emoji_keybindings[i]; i++)
g_slice_free (IBusEngineKeybinding, priv->emoji_keybindings[i]);
g_clear_pointer (&priv->emoji_keybindings, g_free);
}
}
static IBusEngineKeybinding *
ibus_engine_keybinding_new (IBusEngine *engine,
const gchar *accelerator)
{
guint keyval = 0U;
IBusModifierType modifiers = 0;
IBusEngineKeybinding *binding = NULL;
ibus_accelerator_parse (accelerator, &keyval, &modifiers);
if (keyval == 0U && modifiers == 0) {
g_warning ("Failed to parse shortcut key '%s'", accelerator);
return NULL;
}
if (modifiers & IBUS_SUPER_MASK) {
modifiers^=IBUS_SUPER_MASK;
modifiers|=IBUS_MOD4_MASK;
}
binding = g_slice_new0 (IBusEngineKeybinding);
binding->keyval = keyval;
binding->modifiers = modifiers;
return binding;
}
static void
settings_emoji_hotkey_changed_cb (GSettings *settings,
const gchar *key,
gpointer data)
{
IBusEngine *engine;
IBusEnginePrivate *priv;
gchar **accelerators;
int i, j, length;
g_return_if_fail (IBUS_IS_ENGINE (data));
engine = IBUS_ENGINE (data);
priv = engine->priv;
if (g_strcmp0 (key, "hotkey") != 0)
return;
accelerators = g_settings_get_strv (settings, key);
length = g_strv_length (accelerators);
ibus_engine_keybinding_free (engine);
if (length == 0) {
g_strfreev (accelerators);
return;
}
priv->emoji_keybindings = g_new0 (IBusEngineKeybinding*, length + 1);
for (i = 0, j = 0; i < length; i++) {
IBusEngineKeybinding *binding =
ibus_engine_keybinding_new (engine, accelerators[i]);
if (!binding)
continue;
priv->emoji_keybindings[j++] = binding;
}
g_strfreev (accelerators);
}
IBusEngine *

Просмотреть файл

@ -57,6 +57,9 @@ enum {
DESTROY_CONTEXT,
SET_CONTENT_TYPE,
PANEL_EXTENSION_RECEIVED,
PROCESS_KEY_EVENT,
COMMIT_TEXT_RECEIVED,
CANDIDATE_CLICKED_LOOKUP_TABLE,
LAST_SIGNAL,
};
@ -153,7 +156,7 @@ static void ibus_panel_service_set_content_type
guint hints);
static void ibus_panel_service_panel_extension_received
(IBusPanelService *panel,
GVariant *data);
IBusExtensionEvent *event);
G_DEFINE_TYPE (IBusPanelService, ibus_panel_service, IBUS_TYPE_SERVICE)
@ -184,6 +187,11 @@ static const gchar introspection_xml[] =
" <method name='CursorDownLookupTable' />"
" <method name='PageUpLookupTable' />"
" <method name='PageDownLookupTable' />"
" <method name='CandidateClickedLookupTable'>"
" <arg direction='in' type='u' name='index' />"
" <arg direction='in' type='u' name='button' />"
" <arg direction='in' type='u' name='state' />"
" </method>"
" <method name='RegisterProperties'>"
" <arg direction='in' type='v' name='props' />"
" </method>"
@ -221,7 +229,16 @@ static const gchar introspection_xml[] =
" <arg direction='in' type='u' name='hints' />"
" </method>"
" <method name='PanelExtensionReceived'>"
" <arg direction='in' type='v' name='data' />"
" <arg direction='in' type='v' name='event' />"
" </method>"
" <method name='ProcessKeyEvent'>"
" <arg direction='in' type='u' name='keyval' />"
" <arg direction='in' type='u' name='keycode' />"
" <arg direction='in' type='u' name='state' />"
" <arg direction='out' type='b' />"
" </method>"
" <method name='CommitTextReceived'>"
" <arg direction='in' type='v' name='text' />"
" </method>"
/* Signals */
" <signal name='CursorUp' />"
@ -247,7 +264,23 @@ static const gchar introspection_xml[] =
" <arg type='v' name='text' />"
" </signal>"
" <signal name='PanelExtension'>"
" <arg type='v' name='event' />"
" </signal>"
" <method name='PanelExtensionRegisterKeys'>"
" <arg type='v' name='data' />"
" </method>"
" <signal name='UpdatePreeditTextReceived'>"
" <arg type='v' name='text' />"
" <arg type='u' name='cursor_pos' />"
" <arg type='b' name='visible' />"
" </signal>"
" <signal name='UpdateAuxiliaryTextReceived'>"
" <arg type='v' name='text' />"
" <arg type='b' name='visible' />"
" </signal>"
" <signal name='UpdateLookupTableReceived'>"
" <arg type='v' name='table' />"
" <arg type='b' name='visible' />"
" </signal>"
" </interface>"
"</node>";
@ -927,10 +960,81 @@ ibus_panel_service_class_init (IBusPanelServiceClass *class)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusPanelServiceClass, panel_extension_received),
NULL, NULL,
_ibus_marshal_VOID__VARIANT,
_ibus_marshal_VOID__OBJECT,
G_TYPE_NONE,
1,
G_TYPE_VARIANT);
IBUS_TYPE_EXTENSION_EVENT);
/**
* IBusPanelService::process-key-event:
* @panel: An #IBusPanelService
* @keyval: Key symbol of the key press.
* @keycode: KeyCode of the key press.
* @state: Key modifier flags.
*
* Emitted when a key event is received.
* Implement the member function IBusPanelServiceClass::process_key_event
* in extended class to receive this signal.
* Both the key symbol and keycode are passed to the member function.
* See ibus_input_context_process_key_event() for further explanation of
* key symbol, keycode and which to use.
*
* Returns: %TRUE for successfully process the key; %FALSE otherwise.
* See also: ibus_input_context_process_key_event().
*
* <note><para>Argument @user_data is ignored in this function.</para>
* </note>
*/
panel_signals[PROCESS_KEY_EVENT] =
g_signal_new (I_("process-key-event"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusPanelServiceClass, process_key_event),
g_signal_accumulator_true_handled, NULL,
_ibus_marshal_BOOL__UINT_UINT_UINT,
G_TYPE_BOOLEAN,
3,
G_TYPE_UINT,
G_TYPE_UINT,
G_TYPE_UINT);
/**
* IBusPanelService::commit-text-received:
* @panel: An #IBusPanelService
* @text: A #IBusText
*
* Emitted when the client application get the ::commit-text-received.
* Implement the member function
* IBusPanelServiceClass::commit_text_received in extended class to
* receive this signal.
*
* <note><para>Argument @user_data is ignored in this function.</para>
* </note>
*/
panel_signals[COMMIT_TEXT_RECEIVED] =
g_signal_new (I_("commit-text-received"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusPanelServiceClass, commit_text_received),
NULL, NULL,
_ibus_marshal_VOID__OBJECT,
G_TYPE_NONE,
1,
IBUS_TYPE_TEXT);
panel_signals[CANDIDATE_CLICKED_LOOKUP_TABLE] =
g_signal_new (I_("candidate-clicked-lookup-table"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IBusPanelServiceClass,
candidate_clicked_lookup_table),
NULL, NULL,
_ibus_marshal_VOID__UINT_UINT_UINT,
G_TYPE_NONE,
3,
G_TYPE_UINT,
G_TYPE_UINT,
G_TYPE_UINT);
}
static void
@ -1129,9 +1233,14 @@ ibus_panel_service_service_method_call (IBusService *service,
}
if (g_strcmp0 (method_name, "PanelExtensionReceived") == 0) {
GVariant *variant = NULL;
g_variant_get (parameters, "(v)", &variant);
if (variant == NULL) {
GVariant *arg0 = NULL;
IBusExtensionEvent *event = NULL;
g_variant_get (parameters, "(v)", &arg0);
if (arg0) {
event = IBUS_EXTENSION_EVENT (ibus_serializable_deserialize (arg0));
g_variant_unref (arg0);
}
if (!event) {
g_dbus_method_invocation_return_error (
invocation,
G_DBUS_ERROR,
@ -1140,11 +1249,63 @@ ibus_panel_service_service_method_call (IBusService *service,
return;
}
g_signal_emit (panel, panel_signals[PANEL_EXTENSION_RECEIVED], 0,
variant);
g_variant_unref (variant);
event);
_g_object_unref_if_floating (event);
g_dbus_method_invocation_return_value (invocation, NULL);
return;
}
if (g_strcmp0 (method_name, "ProcessKeyEvent") == 0) {
guint keyval, keycode, state;
gboolean retval = FALSE;
g_variant_get (parameters, "(uuu)", &keyval, &keycode, &state);
g_signal_emit (panel,
panel_signals[PROCESS_KEY_EVENT],
0,
keyval,
keycode,
state,
&retval);
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(b)", retval));
return;
}
if (g_strcmp0 (method_name, "CommitTextReceived") == 0) {
GVariant *arg0 = NULL;
IBusText *text = NULL;
g_variant_get (parameters, "(v)", &arg0);
if (arg0) {
text = (IBusText *) ibus_serializable_deserialize (arg0);
g_variant_unref (arg0);
}
if (!text) {
g_dbus_method_invocation_return_error (
invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_FAILED,
"CommitTextReceived method gives NULL");
return;
}
g_signal_emit (panel,
panel_signals[COMMIT_TEXT_RECEIVED],
0,
text);
_g_object_unref_if_floating (text);
return;
}
if (g_strcmp0 (method_name, "CandidateClickedLookupTable") == 0) {
guint index = 0;
guint button = 0;
guint state = 0;
g_variant_get (parameters, "(uuu)", &index, &button, &state);
g_signal_emit (panel,
panel_signals[CANDIDATE_CLICKED_LOOKUP_TABLE],
0,
index, button, state);
return;
}
const static struct {
const gchar *name;
@ -1318,8 +1479,8 @@ ibus_panel_service_set_content_type (IBusPanelService *panel,
}
static void
ibus_panel_service_panel_extension_received (IBusPanelService *panel,
GVariant *data)
ibus_panel_service_panel_extension_received (IBusPanelService *panel,
IBusExtensionEvent *event)
{
ibus_panel_service_not_implemented(panel);
}
@ -1396,10 +1557,11 @@ void
ibus_panel_service_commit_text (IBusPanelService *panel,
IBusText *text)
{
GVariant *variant;
g_return_if_fail (IBUS_IS_PANEL_SERVICE (panel));
g_return_if_fail (IBUS_IS_TEXT (text));
GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)text);
variant = ibus_serializable_serialize ((IBusSerializable *)text);
ibus_service_emit_signal ((IBusService *) panel,
NULL,
IBUS_INTERFACE_PANEL,
@ -1413,18 +1575,144 @@ ibus_panel_service_commit_text (IBusPanelService *panel,
}
void
ibus_panel_service_panel_extension (IBusPanelService *panel,
GVariant *variant)
ibus_panel_service_panel_extension (IBusPanelService *panel,
IBusExtensionEvent *event)
{
GVariant *variant;
g_return_if_fail (IBUS_IS_PANEL_SERVICE (panel));
g_return_if_fail (variant);
g_return_if_fail (IBUS_IS_EXTENSION_EVENT (event));
variant = ibus_serializable_serialize ((IBusSerializable *)event);
ibus_service_emit_signal ((IBusService *) panel,
NULL,
IBUS_INTERFACE_PANEL,
"PanelExtension",
g_variant_new ("(v)", variant),
NULL);
if (g_object_is_floating (event)) {
g_object_unref (event);
}
}
void
ibus_panel_service_panel_extension_register_keys (IBusPanelService *panel,
const gchar
*first_property_name,
...)
{
GVariantBuilder builder;
GVariantBuilder child;
const gchar *name;
va_list var_args;
IBusProcessKeyEventData *keys;
g_return_if_fail (first_property_name);
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
name = first_property_name;
va_start (var_args, first_property_name);
do {
keys = va_arg (var_args, IBusProcessKeyEventData *);
g_return_if_fail (keys != NULL);
g_variant_builder_init (&child, G_VARIANT_TYPE ("av"));
for (; keys; keys++) {
if (keys->keyval == 0 && keys->keycode == 0 && keys->state == 0)
break;
g_variant_builder_add (&child, "v",
g_variant_new ("(iii)",
keys->keyval,
keys->keycode,
keys->state));
}
g_variant_builder_add (&builder, "{sv}",
g_strdup (name), g_variant_builder_end (&child));
} while ((name = va_arg (var_args, const gchar *)));
va_end (var_args);
ibus_service_emit_signal ((IBusService *) panel,
NULL,
IBUS_INTERFACE_PANEL,
"PanelExtensionRegisterKeys",
g_variant_new ("(v)",
g_variant_builder_end (&builder)),
NULL);
}
void
ibus_panel_service_update_preedit_text_received (IBusPanelService *panel,
IBusText *text,
guint cursor_pos,
gboolean visible)
{
GVariant *variant;
g_return_if_fail (IBUS_IS_PANEL_SERVICE (panel));
g_return_if_fail (IBUS_IS_TEXT (text));
variant = ibus_serializable_serialize ((IBusSerializable *)text);
g_return_if_fail (variant);
ibus_service_emit_signal ((IBusService *) panel,
NULL,
IBUS_INTERFACE_PANEL,
"UpdatePreeditTextReceived",
g_variant_new ("(vub)",
variant, cursor_pos, visible),
NULL);
if (g_object_is_floating (text)) {
g_object_unref (text);
}
}
void
ibus_panel_service_update_auxiliary_text_received (IBusPanelService *panel,
IBusText *text,
gboolean visible)
{
GVariant *variant;
g_return_if_fail (IBUS_IS_PANEL_SERVICE (panel));
g_return_if_fail (IBUS_IS_TEXT (text));
variant = ibus_serializable_serialize ((IBusSerializable *)text);
g_return_if_fail (variant);
ibus_service_emit_signal ((IBusService *) panel,
NULL,
IBUS_INTERFACE_PANEL,
"UpdateAuxiliaryTextReceived",
g_variant_new ("(vb)",
variant, visible),
NULL);
if (g_object_is_floating (text)) {
g_object_unref (text);
}
}
void
ibus_panel_service_update_lookup_table_received (IBusPanelService *panel,
IBusLookupTable *table,
gboolean visible)
{
GVariant *variant;
g_return_if_fail (IBUS_IS_PANEL_SERVICE (panel));
g_return_if_fail (IBUS_IS_LOOKUP_TABLE (table));
variant = ibus_serializable_serialize ((IBusSerializable *)table);
g_return_if_fail (variant);
ibus_service_emit_signal ((IBusService *) panel,
NULL,
IBUS_INTERFACE_PANEL,
"UpdateLookupTableReceived",
g_variant_new ("(vb)",
variant, visible),
NULL);
if (g_object_is_floating (table)) {
g_object_unref (table);
}
}
#define DEFINE_FUNC(name, Name) \

Просмотреть файл

@ -38,6 +38,7 @@
#include "ibuslookuptable.h"
#include "ibusservice.h"
#include "ibusproplist.h"
#include "ibusxevent.h"
/*
* Type macros.
@ -130,11 +131,24 @@ struct _IBusPanelServiceClass {
gint h);
void (* panel_extension_received)
(IBusPanelService *panel,
GVariant *data);
IBusExtensionEvent *event);
gboolean (* process_key_event)
(IBusPanelService *panel,
guint keyval,
guint keycode,
guint state);
void (* commit_text_received)
(IBusPanelService *panel,
IBusText *text);
void (* candidate_clicked_lookup_table)
(IBusPanelService *panel,
guint index,
guint button,
guint state);
/*< private >*/
/* padding */
gpointer pdummy[5]; // We can add 8 pointers without breaking the ABI.
gpointer pdummy[2]; // We can add 8 pointers without breaking the ABI.
};
GType ibus_panel_service_get_type (void);
@ -248,12 +262,105 @@ void ibus_panel_service_commit_text (IBusPanelService *panel,
/**
* ibus_panel_service_panel_extension:
* @panel: An #IBusPanelService
* @data: (transfer full): A #GVariant data which is sent to a panel extension.
* @event: (transfer full): A #PanelExtensionEvent which is sent to a
* panel extension.
*
* Enable or disable a panel extension with #IBusExtensionEvent.
* Notify that a data is sent
* by sending a "PanelExtension" message to IBus panel extension service.
*/
void ibus_panel_service_panel_extension (IBusPanelService *panel,
GVariant *data);
void ibus_panel_service_panel_extension (IBusPanelService *panel,
IBusExtensionEvent *event);
/**
* ibus_panel_service_panel_extension_register_keys:
* @panel: An #IBusPanelService
* @first_property_name: the first name of the shortcut keys. This is %NULL
" terminated.
*
* Register shortcut keys to enable panel extensions with #IBusExtensionEvent.
* Notify that a data is sent
* by sending a "PanelExtensionRegisterKeys" message to IBus panel extension
* service. Seems Vala does not support uint[][3] and use
* IBusProcessKeyEventData[]. E.g.
* IBusProcessKeyEventData[] keys = {{
* IBUS_KEY_e, 0, IBUS_SHIFT_MASK | IBUS_SUPER_MASK }};
* ibus_panel_service_panel_extension_register_keys(panel, "emoji", keys, NULL);
*/
void ibus_panel_service_panel_extension_register_keys
(IBusPanelService *panel,
const gchar *first_property_name,
...);
/**
* ibus_panel_service_update_preedit_text_received:
* @panel: An #IBusPanelService
* @text: Update content.
* @cursor_pos: Current position of cursor
* @visible: Whether the pre-edit buffer is visible.
*
* Notify that the preedit is updated by the panel extension
*
* (Note: The table object will be released, if it is floating.
* If caller want to keep the object, caller should make the object
* sink by g_object_ref_sink.)
*/
void ibus_panel_service_update_preedit_text_received
(IBusPanelService *panel,
IBusText *text,
guint cursor_pos,
gboolean visible);
/**
* ibus_panel_service_show_preedit_text_received:
* @panel: An IBusPanelService
*
* Notify that the preedit is shown by the panel extension
*/
void ibus_panel_service_show_preedit_text_received
(IBusPanelService *panel);
/**
* ibus_panel_service_hide_preedit_text_received:
* @panel: An IBusPanelService
*
* Notify that the preedit is hidden by the panel extension
*/
void ibus_panel_service_hide_preedit_text_received
(IBusPanelService *panel);
/**
* ibus_panel_service_update_auxiliary_text_received:
* @panel: An #IBusPanelService
* @text: An #IBusText
* @visible: Whether the auxilirary text is visible.
*
* Notify that the auxilirary is updated by the panel extension.
*
* (Note: The table object will be released, if it is floating.
* If caller want to keep the object, caller should make the object
* sink by g_object_ref_sink.)
*/
void ibus_panel_service_update_auxiliary_text_received
(IBusPanelService *panel,
IBusText *text,
gboolean visible);
/**
* ibus_panel_service_update_lookup_table_received:
* @panel: An #IBusPanelService
* @table: An #IBusLookupTable
* @visible: Whether the lookup table is visible.
*
* Notify that the lookup table is updated by the panel extension.
*
* (Note: The table object will be released, if it is floating.
* If caller want to keep the object, caller should make the object
* sink by g_object_ref_sink.)
*/
void ibus_panel_service_update_lookup_table_received
(IBusPanelService *panel,
IBusLookupTable *table,
gboolean visible);
G_END_DECLS
#endif

Просмотреть файл

@ -73,6 +73,15 @@
*/
#define IBUS_SERVICE_PANEL_EXTENSION "org.freedesktop.IBus.Panel.Extension"
/**
* IBUS_SERVICE_PANEL_EXTENSION_EMOJI:
*
* Address of IBus panel extension service for emoji.
* This service provides emoji, Unicode code point, Unicode name features.
*/
#define IBUS_SERVICE_PANEL_EXTENSION_EMOJI \
"org.freedesktop.IBus.Panel.Extension.Emoji"
/**
* IBUS_SERVICE_CONFIG:
*
@ -109,11 +118,13 @@
#define IBUS_PATH_PANEL "/org/freedesktop/IBus/Panel"
/**
* IBUS_PATH_PANEL_EXTENSION:
* IBUS_PATH_PANEL_EXTENSION_EMOJI:
*
* D-Bus path for IBus panel.
* D-Bus path for IBus extension panel for emoji.
* This service provides emoji, Unicode code point, Unicode name features.
*/
#define IBUS_PATH_PANEL_EXTENSION "/org/freedesktop/IBus/Panel/Extension"
#define IBUS_PATH_PANEL_EXTENSION_EMOJI \
"/org/freedesktop/IBus/Panel/Extension/Emoji"
/**
* IBUS_PATH_CONFIG:

Просмотреть файл

@ -22,13 +22,23 @@
#include "ibusinternal.h"
#include "ibusxevent.h"
#define IBUS_EXTENSION_EVENT_VERSION 1
#define IBUS_EXTENSION_EVENT_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), \
IBUS_TYPE_EXTENSION_EVENT, \
IBusExtensionEventPrivate))
#define IBUS_X_EVENT_VERSION 1
#define IBUS_X_EVENT_GET_PRIVATE(o) \
#define IBUS_X_EVENT_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_X_EVENT, IBusXEventPrivate))
enum {
PROP_0,
PROP_VERSION,
PROP_NAME,
PROP_IS_ENABLED,
PROP_IS_EXTENSION,
PROP_PARAMS,
PROP_EVENT_TYPE,
PROP_WINDOW,
PROP_SEND_EVENT,
@ -52,6 +62,14 @@ enum {
};
struct _IBusExtensionEventPrivate {
guint version;
gchar *name;
gboolean is_enabled;
gboolean is_extension;
gchar *params;
};
struct _IBusXEventPrivate {
guint version;
guint32 time;
@ -73,24 +91,346 @@ struct _IBusXEventPrivate {
};
/* functions prototype */
static void ibus_x_event_destroy (IBusXEvent *event);
static void ibus_x_event_set_property (IBusXEvent *event,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void ibus_x_event_get_property (IBusXEvent *event,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static gboolean ibus_x_event_serialize (IBusXEvent *event,
GVariantBuilder *builder);
static gint ibus_x_event_deserialize (IBusXEvent *event,
GVariant *variant);
static gboolean ibus_x_event_copy (IBusXEvent *dest,
const IBusXEvent *src);
static void ibus_extension_event_destroy (IBusExtensionEvent *event);
static void ibus_extension_event_set_property (IBusExtensionEvent *event,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void ibus_extension_event_get_property (IBusExtensionEvent *event,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static gboolean ibus_extension_event_serialize (IBusExtensionEvent *event,
GVariantBuilder
*builder);
static gint ibus_extension_event_deserialize (IBusExtensionEvent *event,
GVariant
*variant);
static gboolean ibus_extension_event_copy (IBusExtensionEvent
*dest,
const IBusExtensionEvent
*src);
static void ibus_x_event_destroy (IBusXEvent *event);
static void ibus_x_event_set_property (IBusXEvent *event,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void ibus_x_event_get_property (IBusXEvent *event,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static gboolean ibus_x_event_serialize (IBusXEvent *event,
GVariantBuilder
*builder);
static gint ibus_x_event_deserialize (IBusXEvent *event,
GVariant
*variant);
static gboolean ibus_x_event_copy (IBusXEvent *dest,
const IBusXEvent *src);
G_DEFINE_TYPE (IBusExtensionEvent, ibus_extension_event, IBUS_TYPE_SERIALIZABLE)
G_DEFINE_TYPE (IBusXEvent, ibus_x_event, IBUS_TYPE_SERIALIZABLE)
static void
ibus_extension_event_class_init (IBusExtensionEventClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class);
IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class);
gobject_class->set_property =
(GObjectSetPropertyFunc) ibus_extension_event_set_property;
gobject_class->get_property =
(GObjectGetPropertyFunc) ibus_extension_event_get_property;
object_class->destroy =
(IBusObjectDestroyFunc) ibus_extension_event_destroy;
serializable_class->serialize =
(IBusSerializableSerializeFunc) ibus_extension_event_serialize;
serializable_class->deserialize =
(IBusSerializableDeserializeFunc) ibus_extension_event_deserialize;
serializable_class->copy =
(IBusSerializableCopyFunc) ibus_extension_event_copy;
/* install properties */
/**
* IBusExtensionEvent:version:
*
* Version of the #IBusExtensionEvent.
*/
g_object_class_install_property (gobject_class,
PROP_VERSION,
g_param_spec_uint ("version",
"version",
"version",
0,
G_MAXUINT32,
IBUS_EXTENSION_EVENT_VERSION,
G_PARAM_READABLE));
/**
* IBusExtensionEvent:name:
*
* Name of the extension in the #IBusExtensionEvent.
*/
g_object_class_install_property (gobject_class,
PROP_NAME,
g_param_spec_string ("name",
"name",
"name of the extension",
"",
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
/**
* IBusExtensionEvent:is-enabled:
*
* %TRUE if the extension is enabled in the #IBusExtensionEvent.
*/
g_object_class_install_property (gobject_class,
PROP_IS_ENABLED,
g_param_spec_boolean ("is-enabled",
"is enabled",
"if the extension is enabled",
FALSE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
/**
* IBusExtensionEvent:is-extension:
*
* %TRUE if the #IBusExtensionEvent is called by an extension.
* %FALSE if the #IBusExtensionEvent is called by an active engine or
* panel.
* If this value is %TRUE, the event is send to ibus-daemon, an active
* engine. If it's %FALSE, the event is sned to ibus-daemon, panels.
*/
g_object_class_install_property (gobject_class,
PROP_IS_EXTENSION,
g_param_spec_boolean ("is-extension",
"is extension",
"if the event is called by an extension",
FALSE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
/**
* IBusExtensionEvent:params:
*
* Parameters to enable the extension in the #IBusExtensionEvent.
*/
g_object_class_install_property (gobject_class,
PROP_PARAMS,
g_param_spec_string ("params",
"params",
"Parameters to enable the extension",
"",
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
g_type_class_add_private (class, sizeof (IBusExtensionEventPrivate));
}
static void
ibus_extension_event_init (IBusExtensionEvent *event)
{
event->priv = IBUS_EXTENSION_EVENT_GET_PRIVATE (event);
event->priv->version = IBUS_EXTENSION_EVENT_VERSION;
}
static void
ibus_extension_event_destroy (IBusExtensionEvent *event)
{
g_clear_pointer (&event->priv->name, g_free);
IBUS_OBJECT_CLASS(ibus_extension_event_parent_class)->
destroy (IBUS_OBJECT (event));
}
static void
ibus_extension_event_set_property (IBusExtensionEvent *event,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
IBusExtensionEventPrivate *priv = event->priv;
switch (prop_id) {
case PROP_NAME:
g_free (priv->name);
priv->name = g_value_dup_string (value);
break;
case PROP_IS_ENABLED:
priv->is_enabled = g_value_get_boolean (value);
break;
case PROP_IS_EXTENSION:
priv->is_extension = g_value_get_boolean (value);
break;
case PROP_PARAMS:
priv->params = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (event, prop_id, pspec);
}
}
static void
ibus_extension_event_get_property (IBusExtensionEvent *event,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
IBusExtensionEventPrivate *priv = event->priv;
switch (prop_id) {
case PROP_VERSION:
g_value_set_uint (value, priv->version);
break;
case PROP_NAME:
g_value_set_string (value, priv->name);
break;
case PROP_IS_ENABLED:
g_value_set_boolean (value, priv->is_enabled);
break;
case PROP_IS_EXTENSION:
g_value_set_boolean (value, priv->is_extension);
break;
case PROP_PARAMS:
g_value_set_string (value, priv->params);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (event, prop_id, pspec);
}
}
static gboolean
ibus_extension_event_serialize (IBusExtensionEvent *event,
GVariantBuilder *builder)
{
gboolean retval;
IBusExtensionEventPrivate *priv;
retval = IBUS_SERIALIZABLE_CLASS (ibus_extension_event_parent_class)->
serialize ((IBusSerializable *)event, builder);
g_return_val_if_fail (retval, FALSE);
/* End dict iter */
priv = event->priv;
#define NOTNULL(s) ((s) != NULL ? (s) : "")
/* If you will add a new property, you can append it at the end and
* you should not change the serialized order of name, longname,
* description, ... because the order is also used in other applications
* likes ibus-qt. */
g_variant_builder_add (builder, "u", priv->version);
g_variant_builder_add (builder, "s", NOTNULL (priv->name));
g_variant_builder_add (builder, "b", priv->is_enabled);
g_variant_builder_add (builder, "b", priv->is_extension);
g_variant_builder_add (builder, "s", NOTNULL (priv->params));
#undef NOTNULL
return TRUE;
}
static gint
ibus_extension_event_deserialize (IBusExtensionEvent *event,
GVariant *variant)
{
gint retval;
IBusExtensionEventPrivate *priv;
retval = IBUS_SERIALIZABLE_CLASS (ibus_extension_event_parent_class)->
deserialize ((IBusSerializable *)event, variant);
g_return_val_if_fail (retval, 0);
priv = event->priv;
/* If you will add a new property, you can append it at the end and
* you should not change the serialized order of name, longname,
* description, ... because the order is also used in other applications
* likes ibus-qt. */
g_variant_get_child (variant, retval++, "u", &priv->version);
ibus_g_variant_get_child_string (variant, retval++,
&priv->name);
g_variant_get_child (variant, retval++, "b", &priv->is_enabled);
g_variant_get_child (variant, retval++, "b", &priv->is_extension);
ibus_g_variant_get_child_string (variant, retval++,
&priv->params);
return retval;
}
static gboolean
ibus_extension_event_copy (IBusExtensionEvent *dest,
const IBusExtensionEvent *src)
{
gboolean retval;
IBusExtensionEventPrivate *dest_priv = dest->priv;
IBusExtensionEventPrivate *src_priv = src->priv;
retval = IBUS_SERIALIZABLE_CLASS (ibus_extension_event_parent_class)->
copy ((IBusSerializable *)dest, (IBusSerializable *)src);
g_return_val_if_fail (retval, FALSE);
dest_priv->version = src_priv->version;
dest_priv->name = g_strdup (src_priv->name);
dest_priv->is_enabled = src_priv->is_enabled;
dest_priv->is_extension = src_priv->is_extension;
dest_priv->params = g_strdup (src_priv->params);
return TRUE;
}
IBusExtensionEvent *
ibus_extension_event_new (const gchar *first_property_name,
...)
{
va_list var_args;
IBusExtensionEvent *event;
va_start (var_args, first_property_name);
event = (IBusExtensionEvent *) g_object_new_valist (
IBUS_TYPE_EXTENSION_EVENT,
first_property_name,
var_args);
va_end (var_args);
g_assert (event->priv->version != 0);
return event;
}
guint
ibus_extension_event_get_version (IBusExtensionEvent *event)
{
g_return_val_if_fail (IBUS_IS_EXTENSION_EVENT (event), 0);
return event->priv->version;
}
const gchar *
ibus_extension_event_get_name (IBusExtensionEvent *event)
{
g_return_val_if_fail (IBUS_IS_EXTENSION_EVENT (event), "");
return event->priv->name;
}
gboolean
ibus_extension_event_is_enabled (IBusExtensionEvent *event)
{
g_return_val_if_fail (IBUS_IS_EXTENSION_EVENT (event), FALSE);
return event->priv->is_enabled;
}
gboolean
ibus_extension_event_is_extension (IBusExtensionEvent *event)
{
g_return_val_if_fail (IBUS_IS_EXTENSION_EVENT (event), FALSE);
return event->priv->is_extension;
}
const gchar *
ibus_extension_event_get_params (IBusExtensionEvent *event)
{
g_return_val_if_fail (IBUS_IS_EXTENSION_EVENT (event), "");
return event->priv->params;
}
static void
ibus_x_event_class_init (IBusXEventClass *class)
{
@ -454,6 +794,7 @@ static void
ibus_x_event_destroy (IBusXEvent *event)
{
g_clear_pointer (&event->priv->string, g_free);
g_clear_pointer (&event->priv->purpose, g_free);
IBUS_OBJECT_CLASS(ibus_x_event_parent_class)->destroy (IBUS_OBJECT (event));
}

Просмотреть файл

@ -29,8 +29,8 @@
/**
* SECTION: ibusxevent
* @short_description: XEvent wrapper object
* @title: IBusXEvent
* @short_description: Extension Event wrapper object
* @title: IBusExtensionEvent
* @stability: Unstable
*
* An IBusXEvent provides a wrapper of XEvent.
@ -45,25 +45,150 @@
*/
/* define GOBJECT macros */
#define IBUS_TYPE_X_EVENT \
#define IBUS_TYPE_EXTENSION_EVENT \
(ibus_extension_event_get_type ())
#define IBUS_EXTENSION_EVENT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
IBUS_TYPE_EXTENSION_EVENT, \
IBusExtensionEvent))
#define IBUS_EXTENSION_EVENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), \
IBUS_TYPE_EXTENSION_EVENT, \
IBusExtensionEventClass))
#define IBUS_IS_EXTENSION_EVENT(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), IBUS_TYPE_EXTENSION_EVENT))
#define IBUS_IS_EXTENSION_EVENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), IBUS_TYPE_EXTENSION_EVENT))
#define IBUS_EXTENSION_EVENT_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
IBUS_TYPE_EXTENSION_EVENT, \
IBusExtensionEventClass))
#define IBUS_TYPE_X_EVENT \
(ibus_x_event_get_type ())
#define IBUS_X_EVENT(obj) \
#define IBUS_X_EVENT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), IBUS_TYPE_X_EVENT, IBusXEvent))
#define IBUS_X_EVENT_CLASS(klass) \
#define IBUS_X_EVENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), IBUS_TYPE_X_EVENT, IBusXEventClass))
#define IBUS_IS_X_EVENT(obj) \
#define IBUS_IS_X_EVENT(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), IBUS_TYPE_X_EVENT))
#define IBUS_IS_X_EVENT_CLASS(klass) \
#define IBUS_IS_X_EVENT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), IBUS_TYPE_X_EVENT))
#define IBUS_X_EVENT_GET_CLASS(obj) \
#define IBUS_X_EVENT_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), IBUS_TYPE_X_EVENT, IBusXEventClass))
G_BEGIN_DECLS
typedef struct _IBusProcessKeyEventData IBusProcessKeyEventData;
typedef struct _IBusExtensionEvent IBusExtensionEvent;
typedef struct _IBusExtensionEventClass IBusExtensionEventClass;
typedef struct _IBusExtensionEventPrivate IBusExtensionEventPrivate;
typedef struct _IBusXEvent IBusXEvent;
typedef struct _IBusXEventClass IBusXEventClass;
typedef struct _IBusXEventPrivate IBusXEventPrivate;
/**
* IBusProcessKeyEventData:
*
* IBuProcessKeyEventData properties.
*/
struct _IBusProcessKeyEventData {
/*< public >*/
guint keyval;
guint keycode;
guint state;
};
/**
* IBusExtensionEvent:
*
* IBusExtensionEvent properties.
*/
struct _IBusExtensionEvent {
/*< private >*/
IBusSerializable parent;
IBusExtensionEventPrivate *priv;
/* instance members */
/*< public >*/
};
struct _IBusExtensionEventClass {
/*< private >*/
IBusSerializableClass parent;
/* class members */
/*< public >*/
/*< private >*/
/* padding */
gpointer pdummy[10];
};
GType ibus_extension_event_get_type (void);
/**
* ibus_extension_event_new:
* @first_property_name: Name of the first property.
* @...: the NULL-terminated arguments of the properties and values.
*
* Create a new #IBusExtensionEvent.
*
* Returns: A newly allocated #IBusExtensionEvent. E.g.
* ibus_extension_event_new ("name", "emoji", "is-enabled", TRUE, NULL);
*/
IBusExtensionEvent *ibus_extension_event_new (const gchar
*first_property_name,
...);
/**
* ibus_extension_event_get_version:
* @event: An #IBusExtensionEvent.
*
* Returns: Version of #IBusExtensionEvent
*/
guint ibus_extension_event_get_version (IBusExtensionEvent *event);
/**
* ibus_extension_event_get_purpose:
* @event: An #IBusExtensionEvent.
*
* Returns: name of the extension for #IBusXEvent
*/
const gchar * ibus_extension_event_get_name (IBusExtensionEvent *event);
/**
* ibus_extension_event_is_enabled:
* @event: An #IBusExtensionEvent.
*
* Returns: %TRUE if the extension is enabled for #IBusExtensionEvent
*/
gboolean ibus_extension_event_is_enabled (IBusExtensionEvent *event);
/**
* ibus_extension_event_is_extension:
* @event: An #IBusExtensionEvent.
*
* Returns: %TRUE if the #IBusExtensionEvent is called by an extension.
* %FALSE if the #IBusExtensionEvent is called by an active engine or
* panel.
* If this value is %TRUE, the event is send to ibus-daemon, an active
* engine. If it's %FALSE, the event is sned to ibus-daemon, panels.
*/
gboolean ibus_extension_event_is_extension
(IBusExtensionEvent *event);
/**
* ibus_extension_event_get_params:
* @event: An #IBusExtensionEvent.
*
* Returns: Parameters to enable the extension for #IBusXEvent
*/
const gchar * ibus_extension_event_get_params (IBusExtensionEvent *event);
typedef enum {
IBUS_X_EVENT_NOTHING = -1,
IBUS_X_EVENT_KEY_PRESS = 0,
@ -76,7 +201,7 @@ typedef enum {
* IBusXEvent:
* @type: event type
*
* IBusEngine properties.
* IBusXEvent properties.
*/
struct _IBusXEvent {
/*< private >*/

Просмотреть файл

@ -78,6 +78,7 @@ AM_VALAFLAGS = \
--pkg=ibus-1.0 \
--pkg=config \
--pkg=xi \
--pkg=gdk-wayland \
--target-glib="$(VALA_TARGET_GLIB_VERSION)" \
$(NULL)
@ -176,6 +177,7 @@ ibus_ui_emojier_VALASOURCES = \
emojier.vala \
iconwidget.vala \
separator.vala \
pango.vala \
$(NULL)
ibus_ui_emojier_SOURCES = \
$(ibus_ui_emojier_VALASOURCES:.vala=.c) \
@ -213,6 +215,7 @@ ibus_extension_gtk3_VALASOURCES = \
iconwidget.vala \
keybindingmanager.vala \
panelbinding.vala \
pango.vala \
$(NULL)
ibus_extension_gtk3_SOURCES = \
$(ibus_extension_gtk3_VALASOURCES:.vala=.c) \

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -28,8 +28,9 @@ int partial_match_condition = -1;
public class EmojiApplication : Gtk.Application {
private IBusEmojier? m_emojier;
GLib.Settings m_settings_emoji =
private GLib.Settings m_settings_emoji =
new GLib.Settings("org.freedesktop.ibus.panel.emoji");
private ApplicationCommandLine? m_command_line = null;
private EmojiApplication() {
@ -40,25 +41,39 @@ public class EmojiApplication : Gtk.Application {
private void show_dialog(ApplicationCommandLine command_line) {
m_emojier = new IBusEmojier();
// For title handling in gnome-shell
add_window(m_emojier);
Gdk.Event event = Gtk.get_current_event();
// Plasma and GNOME3 desktop returns null event
if (event == null) {
event = new Gdk.Event(Gdk.EventType.KEY_PRESS);
event.key.time = Gdk.CURRENT_TIME;
// event.get_seat() refers event.any.window
event.key.window = Gdk.get_default_root_window();
event.key.window.ref();
}
string emoji = m_emojier.run("", event);
remove_window(m_emojier);
if (emoji == null) {
m_emojier = null;
command_line.print("%s\n", _("Canceled to choose an emoji."));
m_command_line = command_line;
m_emojier.reset();
m_emojier.set_annotation("");
m_emojier.show_all();
}
public void candidate_clicked_lookup_table(uint index,
uint button,
uint state) {
if (m_command_line == null)
return;
if (button == IBusEmojier.BUTTON_CLOSE_BUTTON) {
m_emojier.hide();
m_command_line.print("%s\n", _("Canceled to choose an emoji."));
m_command_line = null;
return;
}
if (m_emojier == null)
return;
bool show_candidate = false;
uint ncandidates = m_emojier.get_number_of_candidates();
if (ncandidates > 0 && ncandidates >= index) {
m_emojier.set_cursor_pos(index);
show_candidate = m_emojier.has_variants(index);
} else {
return;
}
if (show_candidate) {
return;
}
string emoji = m_emojier.get_current_candidate();
m_emojier.hide();
Gtk.Clipboard clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD);
clipboard.set_text(emoji, -1);
clipboard.store();
@ -75,9 +90,8 @@ public class EmojiApplication : Gtk.Application {
emojier_favorites += emoji;
m_settings_emoji.set_strv("favorites", emojier_favorites);
}
m_emojier = null;
command_line.print("%s\n", _("Copied an emoji to your clipboard."));
m_command_line.print("%s\n", _("Copied an emoji to your clipboard."));
m_command_line = null;
}
@ -88,7 +102,7 @@ public class EmojiApplication : Gtk.Application {
}
private int _command_line (ApplicationCommandLine command_line) {
private int _command_line(ApplicationCommandLine command_line) {
// Set default font size
IBusEmojier.set_emoji_font(m_settings_emoji.get_string("font"));
@ -181,13 +195,22 @@ public class EmojiApplication : Gtk.Application {
IBusEmojier.load_unicode_dict();
if (m_emojier == null) {
m_emojier = new IBusEmojier();
// For title handling in gnome-shell
add_window(m_emojier);
m_emojier.candidate_clicked.connect((i, b, s) => {
candidate_clicked_lookup_table(i, b, s);
});
}
activate_dialog(command_line);
return Posix.EXIT_SUCCESS;
}
public override int command_line (ApplicationCommandLine command_line) {
public override int command_line(ApplicationCommandLine command_line) {
// keep the application running until we are done with this commandline
this.hold();
int result = _command_line(command_line);
@ -196,6 +219,13 @@ public class EmojiApplication : Gtk.Application {
}
public override void shutdown() {
base.shutdown();
remove_window(m_emojier);
m_emojier = null;
}
public static int main (string[] args) {
GLib.Intl.bindtextdomain(Config.GETTEXT_PACKAGE,
Config.GLIB_LOCALE_DIR);

Просмотреть файл

@ -50,20 +50,20 @@ class ExtensionGtk : Gtk.Application {
"org.freedesktop.DBus",
"NameAcquired",
"/org/freedesktop/DBus",
IBus.SERVICE_PANEL_EXTENSION,
IBus.SERVICE_PANEL_EXTENSION_EMOJI,
DBusSignalFlags.NONE,
bus_name_acquired_cb);
connection.signal_subscribe("org.freedesktop.DBus",
"org.freedesktop.DBus",
"NameLost",
"/org/freedesktop/DBus",
IBus.SERVICE_PANEL_EXTENSION,
IBus.SERVICE_PANEL_EXTENSION_EMOJI,
DBusSignalFlags.NONE,
bus_name_lost_cb);
var flags =
IBus.BusNameFlag.ALLOW_REPLACEMENT |
IBus.BusNameFlag.REPLACE_EXISTING;
m_bus.request_name(IBus.SERVICE_PANEL_EXTENSION, flags);
m_bus.request_name(IBus.SERVICE_PANEL_EXTENSION_EMOJI, flags);
}

Просмотреть файл

@ -1148,26 +1148,15 @@ class Panel : IBus.PanelService {
#if EMOJI_DICT
item = new Gtk.MenuItem.with_label(_("Emoji Choice"));
item.activate.connect((i) => {
Gdk.Event event = Gtk.get_current_event();
if (event == null) {
event = new Gdk.Event(Gdk.EventType.KEY_PRESS);
event.key.time = Gdk.CURRENT_TIME;
// event.get_seat() refers event.any.window
event.key.window = Gdk.get_default_root_window();
event.key.window.ref();
}
IBus.XEvent xevent = new IBus.XEvent(
"event-type", IBus.XEventType.KEY_PRESS,
"window",
(event.key.window as Gdk.X11.Window).get_xid(),
"time", event.key.time,
"purpose", "emoji");
/* new GLib.Variant("(sv)", "emoji", xevent.serialize_object())
IBus.ExtensionEvent event = new IBus.ExtensionEvent(
"name", "emoji", "is-enabled", true,
"params", "category-list");
/* new GLib.Variant("(sv)", "emoji", event.serialize_object())
* will call g_variant_unref() for the child variant by vala.
* I have no idea not to unref the object so integrated
* the purpose to IBus.XEvent above.
* the purpose to IBus.ExtensionEvent above.
*/
panel_extension(xevent.serialize_object());
panel_extension(event);
});
m_sys_menu.append(item);
#endif

Просмотреть файл

@ -21,7 +21,193 @@
* USA
*/
class Preedit : Gtk.Window {
private Gtk.Label m_extension_preedit_text;
private Gtk.Label m_extension_preedit_emoji;
private IBus.Text? m_engine_preedit_text;
private bool m_engine_preedit_text_show;
private uint m_engine_preedit_cursor_pos;
private string m_prefix = "@";
private bool m_is_shown = true;
public Preedit() {
GLib.Object(
name : "IBusPreedit",
type: Gtk.WindowType.POPUP
);
m_extension_preedit_text = new Gtk.Label("");
m_extension_preedit_emoji = new Gtk.Label("");
}
public new void hide() {
reset();
base.hide();
m_is_shown = false;
}
public bool is_shown() {
return m_is_shown;
}
public void reset() {
set_emoji("");
set_text("");
resize(1, 1);
m_is_shown = true;
}
public void append_text(string text) {
if (text.length == 0)
return;
string total = m_extension_preedit_text.get_text();
total += text;
m_extension_preedit_text.set_text(total);
}
public string get_text() {
return m_extension_preedit_text.get_text();
}
public void set_text(string text) {
m_extension_preedit_text.set_text(text);
}
public string get_emoji() {
return m_extension_preedit_emoji.get_text();
}
public void set_emoji(string text) {
m_extension_preedit_emoji.set_text(text);
}
public bool backspace() {
string total = m_extension_preedit_emoji.get_text();
if (total.length > 0) {
m_extension_preedit_emoji.set_text("");
resize(1, 1);
return false;
}
total = m_extension_preedit_text.get_text();
int char_count = total.char_count();
if (char_count == 0)
return true;
total = total[0:total.index_of_nth_char(char_count - 1)];
resize(1, 1);
m_extension_preedit_text.set_text(total);
if (total.length == 0)
resize(1, 1);
return true;
}
private string get_extension_text () {
string extension_text = m_extension_preedit_emoji.get_text();
if (extension_text.length == 0)
extension_text = m_extension_preedit_text.get_text();
return m_prefix + extension_text;
}
private void set_preedit_color(IBus.Text text,
uint start_index,
uint end_index) {
text.append_attribute(IBus.AttrType.UNDERLINE,
IBus.AttrUnderline.SINGLE,
start_index, (int)end_index);
}
public IBus.Text get_engine_preedit_text() {
string extension_text = get_extension_text();
uint char_count = extension_text.char_count();
IBus.Text retval;
if (m_engine_preedit_text == null || !m_engine_preedit_text_show) {
retval = new IBus.Text.from_string(extension_text);
set_preedit_color(retval, 0, char_count);
return retval;
}
retval = new IBus.Text.from_string(
extension_text + m_engine_preedit_text.get_text());
set_preedit_color(retval, 0, char_count);
unowned IBus.AttrList attrs = m_engine_preedit_text.get_attributes();
if (attrs == null)
return retval;
int i = 0;
while (true) {
IBus.Attribute attr = attrs.get(i++);
if (attr == null)
break;
long start_index = attr.start_index;
long end_index = attr.end_index;
if (start_index < 0)
start_index = 0;
if (end_index < 0)
end_index = m_engine_preedit_text.get_length();
retval.append_attribute(attr.type, attr.value,
char_count + (uint)start_index,
(int)char_count + (int)end_index);
}
return retval;
}
public void set_engine_preedit_text(IBus.Text? text) {
m_engine_preedit_text = text;
}
public void show_engine_preedit_text() {
m_engine_preedit_text_show = true;
}
public void hide_engine_preedit_text() {
m_engine_preedit_text_show = false;
}
public uint get_engine_preedit_cursor_pos() {
return get_extension_text().char_count() + m_engine_preedit_cursor_pos;
}
public void set_engine_preedit_cursor_pos(uint cursor_pos) {
m_engine_preedit_cursor_pos = cursor_pos;
}
public IBus.Text get_commit_text() {
string extension_text = m_extension_preedit_emoji.get_text();
if (extension_text.length == 0)
extension_text = m_extension_preedit_text.get_text();
return new IBus.Text.from_string(extension_text);
}
public void set_extension_name(string extension_name) {
if (extension_name.length == 0)
m_prefix = "@";
else
m_prefix = extension_name[0:1];
}
}
class PanelBinding : IBus.PanelService {
private bool m_is_wayland;
private bool m_wayland_lookup_table_is_visible;
private IBus.Bus m_bus;
private Gtk.Application m_application;
private GLib.Settings m_settings_panel = null;
@ -38,18 +224,26 @@ class PanelBinding : IBus.PanelService {
private bool m_loaded_emoji = false;
private bool m_load_unicode_at_startup;
private bool m_loaded_unicode = false;
private bool m_enable_extension;
private string m_extension_name = "";
private Preedit m_preedit;
public PanelBinding(IBus.Bus bus,
Gtk.Application application) {
GLib.assert(bus.is_connected());
// Chain up base class constructor
GLib.Object(connection : bus.get_connection(),
object_path : IBus.PATH_PANEL_EXTENSION);
object_path : IBus.PATH_PANEL_EXTENSION_EMOJI);
Type instance_type = Gdk.Display.get_default().get_type();
Type wayland_type = typeof(GdkWayland.Display);
m_is_wayland = instance_type.is_a(wayland_type);
m_bus = bus;
m_application = application;
init_settings();
m_preedit = new Preedit();
}
@ -69,12 +263,20 @@ class PanelBinding : IBus.PanelService {
ref m_css_provider);
});
m_settings_emoji.changed["unicode-hotkey"].connect((key) => {
set_emoji_hotkey();
});
m_settings_emoji.changed["font"].connect((key) => {
BindingCommon.set_custom_font(m_settings_panel,
m_settings_emoji,
ref m_css_provider);
});
m_settings_emoji.changed["hotkey"].connect((key) => {
set_emoji_hotkey();
});
m_settings_emoji.changed["favorites"].connect((key) => {
set_emoji_favorites();
});
@ -109,6 +311,54 @@ class PanelBinding : IBus.PanelService {
}
private unowned
IBus.ProcessKeyEventData? parse_accelerator(string accelerator) {
IBus.ProcessKeyEventData key = {};
uint keysym = 0;
IBus.ModifierType modifiers = 0;
IBus.accelerator_parse(accelerator,
out keysym, out modifiers);
if (keysym == 0U && modifiers == 0) {
warning("Failed to parse shortcut key '%s'".printf(accelerator));
return null;
}
if ((modifiers & IBus.ModifierType.SUPER_MASK) != 0) {
modifiers ^= IBus.ModifierType.SUPER_MASK;
modifiers |= IBus.ModifierType.MOD4_MASK;
}
key.keyval = keysym;
key.state = modifiers;
return key;
}
private void set_emoji_hotkey() {
IBus.ProcessKeyEventData[] emoji_keys = {};
IBus.ProcessKeyEventData key;
string[] accelerators = m_settings_emoji.get_strv("hotkey");
foreach (var accelerator in accelerators) {
key = parse_accelerator(accelerator);
emoji_keys += key;
}
/* Since {} is not allocated, parse_accelerator() should be unowned. */
key = {};
emoji_keys += key;
IBus.ProcessKeyEventData[] unicode_keys = {};
accelerators = m_settings_emoji.get_strv("unicode-hotkey");
foreach (var accelerator in accelerators) {
key = parse_accelerator(accelerator);
unicode_keys += key;
}
key = {};
unicode_keys += key;
panel_extension_register_keys("emoji", emoji_keys,
"unicode", unicode_keys);
}
private void set_emoji_favorites() {
m_emojier_favorites = m_settings_emoji.get_strv("favorites");
IBusEmojier.set_favorites(
@ -159,6 +409,7 @@ class PanelBinding : IBus.PanelService {
public void load_settings() {
set_emoji_hotkey();
set_load_emoji_at_startup();
set_load_unicode_at_startup();
BindingCommon.set_custom_font(m_settings_panel,
@ -181,36 +432,37 @@ class PanelBinding : IBus.PanelService {
GLib.Source.remove(m_emojier_set_emoji_lang_id);
m_emojier_set_emoji_lang_id = 0;
}
if (m_emojier != null) {
m_application.remove_window(m_emojier);
m_emojier = null;
}
m_application = null;
}
private void show_emojier(Gdk.Event event) {
if (!m_loaded_emoji)
set_emoji_lang();
if (!m_loaded_unicode && m_loaded_emoji) {
IBusEmojier.load_unicode_dict();
m_loaded_unicode = true;
}
m_emojier = new IBusEmojier();
// For title handling in gnome-shell
m_application.add_window(m_emojier);
string emoji = m_emojier.run(m_real_current_context_path, event);
m_application.remove_window(m_emojier);
if (emoji == null) {
m_emojier = null;
private void commit_text_update_favorites(IBus.Text text) {
commit_text(text);
IBus.ExtensionEvent event = new IBus.ExtensionEvent(
"name", m_extension_name,
"is-enabled", false,
"is-extension", true);
panel_extension(event);
string committed_string = text.text;
string preedit_string = m_preedit.get_text();
m_preedit.hide();
if (preedit_string == committed_string)
return;
bool has_favorite = false;
foreach (unowned string favorite in m_emojier_favorites) {
if (favorite == committed_string) {
has_favorite = true;
break;
}
}
this.emojier_focus_commit();
}
private void handle_emoji_typing(Gdk.Event event) {
if (m_emojier != null && m_emojier.is_running()) {
m_emojier.present_centralize(event);
return;
if (!has_favorite) {
m_emojier_favorites += committed_string;
m_settings_emoji.set_strv("favorites", m_emojier_favorites);
}
show_emojier(event);
}
@ -223,19 +475,8 @@ class PanelBinding : IBus.PanelService {
prev_context_path != "" &&
prev_context_path == m_current_context_path) {
IBus.Text text = new IBus.Text.from_string(selected_string);
commit_text(text);
m_emojier = null;
bool has_favorite = false;
foreach (unowned string favorite in m_emojier_favorites) {
if (favorite == selected_string) {
has_favorite = true;
break;
}
}
if (!has_favorite) {
m_emojier_favorites += selected_string;
m_settings_emoji.set_strv("favorites", m_emojier_favorites);
}
commit_text_update_favorites(text);
m_emojier.reset();
return true;
}
@ -249,8 +490,7 @@ class PanelBinding : IBus.PanelService {
string selected_string = m_emojier.get_selected_string();
string prev_context_path = m_emojier.get_input_context_path();
if (selected_string == null &&
prev_context_path != "" &&
m_emojier.is_running()) {
prev_context_path != "") {
var context = GLib.MainContext.default();
if (m_emojier_focus_commit_text_id > 0 &&
context.find_source_by_id(m_emojier_focus_commit_text_id)
@ -277,6 +517,243 @@ class PanelBinding : IBus.PanelService {
}
private bool key_press_escape() {
if (is_emoji_lookup_table()) {
bool show_candidate = m_emojier.key_press_escape();
convert_preedit_text();
return show_candidate;
}
if (m_preedit.get_emoji() != "") {
m_preedit.set_emoji("");
string annotation = m_preedit.get_text();
m_emojier.set_annotation(annotation);
return false;
}
m_enable_extension = false;
hide_emoji_lookup_table();
m_preedit.hide();
IBus.ExtensionEvent event = new IBus.ExtensionEvent(
"name", m_extension_name,
"is-enabled", false,
"is-extension", true);
panel_extension(event);
return false;
}
private bool key_press_enter() {
if (m_extension_name != "unicode" && is_emoji_lookup_table()) {
// Check if variats exist
if (m_emojier.key_press_enter())
return true;
}
IBus.Text text = m_preedit.get_commit_text();
commit_text_update_favorites(text);
return false;
}
private void convert_preedit_text() {
if (m_emojier.get_number_of_candidates() > 0)
m_preedit.set_emoji(m_emojier.get_current_candidate());
else
m_preedit.set_emoji("");
}
private bool key_press_space() {
bool show_candidate = false;
if (m_preedit.get_emoji() != "") {
m_emojier.key_press_cursor_horizontal(Gdk.Key.Right, 0);
show_candidate = true;
} else {
string annotation = m_preedit.get_text();
if (annotation.length == 0) {
show_candidate = true;
if (is_emoji_lookup_table())
m_emojier.key_press_cursor_horizontal(Gdk.Key.Right, 0);
} else {
m_emojier.set_annotation(annotation);
}
}
convert_preedit_text();
return show_candidate;
}
private bool key_press_cursor_horizontal(uint keyval,
uint modifiers) {
if (is_emoji_lookup_table()) {
m_emojier.key_press_cursor_horizontal(keyval, modifiers);
convert_preedit_text();
return true;
}
return false;
}
private bool key_press_cursor_vertical(uint keyval,
uint modifiers) {
if (is_emoji_lookup_table()) {
m_emojier.key_press_cursor_vertical(keyval, modifiers);
convert_preedit_text();
return true;
}
return false;
}
private bool key_press_cursor_home_end(uint keyval,
uint modifiers) {
if (is_emoji_lookup_table()) {
m_emojier.key_press_cursor_home_end(keyval, modifiers);
convert_preedit_text();
return true;
}
return false;
}
private bool key_press_control_keyval(uint keyval,
uint modifiers) {
bool show_candidate = false;
switch(keyval) {
case Gdk.Key.f:
show_candidate = key_press_cursor_horizontal(Gdk.Key.Right,
modifiers);
break;
case Gdk.Key.b:
show_candidate = key_press_cursor_horizontal(Gdk.Key.Left,
modifiers);
break;
case Gdk.Key.n:
case Gdk.Key.N:
show_candidate = key_press_cursor_vertical(Gdk.Key.Down, modifiers);
break;
case Gdk.Key.p:
case Gdk.Key.P:
show_candidate = key_press_cursor_vertical(Gdk.Key.Up, modifiers);
break;
case Gdk.Key.h:
show_candidate = key_press_cursor_home_end(Gdk.Key.Home, modifiers);
break;
case Gdk.Key.e:
show_candidate = key_press_cursor_home_end(Gdk.Key.End, modifiers);
break;
case Gdk.Key.u:
m_preedit.reset();
m_emojier.set_annotation("");
hide_emoji_lookup_table();
break;
case Gdk.Key.C:
case Gdk.Key.c:
if ((modifiers & Gdk.ModifierType.SHIFT_MASK) != 0) {
if (!m_is_wayland && m_emojier != null &&
m_emojier.get_number_of_candidates() > 0) {
var text = m_emojier.get_current_candidate();
Gtk.Clipboard clipboard =
Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD);
clipboard.set_text(text, -1);
clipboard.store();
}
show_candidate = is_emoji_lookup_table();
}
break;
default:
show_candidate = is_emoji_lookup_table();
break;
}
return show_candidate;
}
private void hide_wayland_lookup_table() {
m_wayland_lookup_table_is_visible = false;
var text = new IBus.Text.from_string("");
update_auxiliary_text_received(text, false);
update_lookup_table_received(
new IBus.LookupTable(1, 0, false, true),
false);
}
private void show_wayland_lookup_table(IBus.Text text) {
m_wayland_lookup_table_is_visible = true;
var table = m_emojier.get_one_dimension_lookup_table();
uint ncandidates = table.get_number_of_candidates();
update_auxiliary_text_received(
text,
ncandidates > 0 ? true : false);
update_lookup_table_received(
table,
ncandidates > 0 ? true : false);
}
private bool is_visible_wayland_lookup_table() {
return m_wayland_lookup_table_is_visible;
}
private void hide_emoji_lookup_table() {
if (m_emojier == null)
return;
if (m_is_wayland)
hide_wayland_lookup_table();
else
m_emojier.hide();
}
private void show_emoji_lookup_table() {
/* Emojier category_list is shown in both Xorg and Wayland
* because the annotation information is useful but the Wayland lookup
* window is alway one dimension. So the category_list is shown
* when the user annotation is null.
*/
if (m_is_wayland && m_preedit.get_text() != "") {
var text = m_emojier.get_title_text();
show_wayland_lookup_table(text);
} else {
// POPUP window takes the focus in Wayland.
if (m_is_wayland)
m_emojier.set_input_context_path(m_real_current_context_path);
m_emojier.show_all();
}
}
private bool is_emoji_lookup_table() {
if (m_is_wayland)
return is_visible_wayland_lookup_table();
else
return m_emojier.get_visible();
}
private void show_preedit_and_candidate(bool show_candidate) {
uint cursor_pos = 0;
if (!show_candidate)
cursor_pos = m_preedit.get_engine_preedit_cursor_pos();
update_preedit_text_received(
m_preedit.get_engine_preedit_text(),
cursor_pos,
true);
if (!show_candidate) {
hide_emoji_lookup_table();
return;
}
if (m_emojier == null)
return;
/* Wayland gives the focus on Emojir which is a GTK popup window
* and move the focus fom the current input context to Emojier.
* This forwards the lookup table to gnome-shell's lookup table
* but it enables one dimension lookup table only.
*/
show_emoji_lookup_table();
}
public override void focus_in(string input_context_path) {
m_current_context_path = input_context_path;
@ -299,48 +776,280 @@ class PanelBinding : IBus.PanelService {
}
public override void panel_extension_received(GLib.Variant data) {
IBus.XEvent? xevent = IBus.Serializable.deserialize_object(data)
as IBus.XEvent;
if (xevent == null) {
warning ("Failed to deserialize IBusXEvent");
public override void panel_extension_received(IBus.ExtensionEvent event) {
m_extension_name = event.get_name();
if (m_extension_name != "emoji" && m_extension_name != "unicode") {
string format = "The name %s is not implemented in PanelExtension";
warning (format.printf(m_extension_name));
m_extension_name = "";
return;
}
if (xevent.get_purpose() != "emoji") {
string format = "The purpose %s is not implemented in PanelExtension";
warning (format.printf(xevent.get_purpose()));
m_enable_extension = event.is_enabled;
if (!m_enable_extension) {
hide_emoji_lookup_table();
return;
}
Gdk.EventType event_type;
if (xevent.get_event_type() == IBus.XEventType.KEY_PRESS) {
event_type = Gdk.EventType.KEY_PRESS;
} else if (xevent.get_event_type() == IBus.XEventType.KEY_RELEASE) {
event_type = Gdk.EventType.KEY_RELEASE;
if (!m_loaded_emoji)
set_emoji_lang();
if (!m_loaded_unicode && m_loaded_emoji) {
IBusEmojier.load_unicode_dict();
m_loaded_unicode = true;
}
if (m_emojier == null) {
m_emojier = new IBusEmojier();
// For title handling in gnome-shell
m_application.add_window(m_emojier);
m_emojier.candidate_clicked.connect((i, b, s) => {
if (!m_is_wayland)
candidate_clicked_lookup_table(i, b, s);
});
}
m_emojier.reset();
m_emojier.set_annotation("");
m_preedit.set_extension_name(m_extension_name);
m_preedit.reset();
update_preedit_text_received(
m_preedit.get_engine_preedit_text(),
m_preedit.get_engine_preedit_cursor_pos(),
true);
string params = event.get_params();
if (params == "category-list") {
key_press_space();
show_preedit_and_candidate(true);
}
}
public override void set_cursor_location(int x,
int y,
int width,
int height) {
if (m_emojier != null)
m_emojier.set_cursor_location(x, y, width, height);
}
public override void update_preedit_text(IBus.Text text,
uint cursor_pos,
bool visible) {
m_preedit.set_engine_preedit_text(text);
if (visible)
m_preedit.show_engine_preedit_text();
else
m_preedit.hide_engine_preedit_text();
m_preedit.set_engine_preedit_cursor_pos(cursor_pos);
update_preedit_text_received(m_preedit.get_engine_preedit_text(),
m_preedit.get_engine_preedit_cursor_pos(),
visible);
}
public override void show_preedit_text() {
m_preedit.show_engine_preedit_text();
show_preedit_and_candidate(false);
}
public override void hide_preedit_text() {
m_preedit.hide_engine_preedit_text();
show_preedit_and_candidate(false);
}
public override bool process_key_event(uint keyval,
uint keycode,
uint state) {
if ((state & IBus.ModifierType.RELEASE_MASK) != 0)
return false;
uint modifiers = state;
bool show_candidate = false;
switch(keyval) {
case Gdk.Key.Escape:
show_candidate = key_press_escape();
if (!m_preedit.is_shown())
return true;
break;
case Gdk.Key.Return:
case Gdk.Key.KP_Enter:
if (m_extension_name == "unicode")
key_press_space();
show_candidate = key_press_enter();
if (!m_preedit.is_shown()) {
hide_emoji_lookup_table();
return true;
}
break;
case Gdk.Key.BackSpace:
m_preedit.backspace();
string annotation = m_preedit.get_text();
if (annotation == "" && m_extension_name == "unicode") {
key_press_escape();
return true;
}
m_emojier.set_annotation(annotation);
break;
case Gdk.Key.space:
case Gdk.Key.KP_Space:
show_candidate = key_press_space();
if (m_extension_name == "unicode") {
key_press_enter();
return true;
}
break;
case Gdk.Key.Right:
case Gdk.Key.KP_Right:
/* one dimension in Wayland, two dimensions in X11 */
if (m_is_wayland) {
show_candidate = key_press_cursor_vertical(Gdk.Key.Down,
modifiers);
} else {
show_candidate = key_press_cursor_horizontal(Gdk.Key.Right,
modifiers);
}
break;
case Gdk.Key.Left:
case Gdk.Key.KP_Left:
if (m_is_wayland) {
show_candidate = key_press_cursor_vertical(Gdk.Key.Up,
modifiers);
} else {
show_candidate = key_press_cursor_horizontal(Gdk.Key.Left,
modifiers);
}
break;
case Gdk.Key.Down:
case Gdk.Key.KP_Down:
if (m_is_wayland) {
show_candidate = key_press_cursor_horizontal(Gdk.Key.Right,
modifiers);
} else {
show_candidate = key_press_cursor_vertical(Gdk.Key.Down,
modifiers);
}
break;
case Gdk.Key.Up:
case Gdk.Key.KP_Up:
if (m_is_wayland) {
show_candidate = key_press_cursor_horizontal(Gdk.Key.Left,
modifiers);
} else {
show_candidate = key_press_cursor_vertical(Gdk.Key.Up,
modifiers);
}
break;
case Gdk.Key.Page_Down:
case Gdk.Key.KP_Page_Down:
if (m_is_wayland) {
show_candidate = key_press_cursor_vertical(Gdk.Key.Down,
modifiers);
} else {
show_candidate = key_press_cursor_vertical(Gdk.Key.Page_Down,
modifiers);
}
break;
case Gdk.Key.Page_Up:
case Gdk.Key.KP_Page_Up:
if (m_is_wayland) {
show_candidate = key_press_cursor_vertical(Gdk.Key.Up,
modifiers);
} else {
show_candidate = key_press_cursor_vertical(Gdk.Key.Page_Up,
modifiers);
}
break;
case Gdk.Key.Home:
case Gdk.Key.KP_Home:
show_candidate = key_press_cursor_home_end(Gdk.Key.Home, modifiers);
break;
case Gdk.Key.End:
case Gdk.Key.KP_End:
show_candidate = key_press_cursor_home_end(Gdk.Key.End, modifiers);
break;
default:
if ((modifiers & Gdk.ModifierType.CONTROL_MASK) != 0) {
show_candidate = key_press_control_keyval(keyval, modifiers);
break;
}
unichar ch = IBus.keyval_to_unicode(keyval);
if (ch.iscntrl())
return true;
string str = ch.to_string();
m_preedit.append_text(str);
string annotation = m_preedit.get_text();
m_emojier.set_annotation(annotation);
m_preedit.set_emoji("");
show_candidate = is_emoji_lookup_table();
break;
}
show_preedit_and_candidate(show_candidate);
return true;
}
public override void commit_text_received(IBus.Text text) {
unowned string? str = text.text;
if (str == null)
return;
/* Do not call convert_preedit_text() because it depends on
* each IME whether process_key_event() receives Shift-space or not.
*/
m_preedit.append_text(str);
m_preedit.set_emoji("");
string annotation = m_preedit.get_text();
m_emojier.set_annotation(annotation);
show_preedit_and_candidate(false);
}
public override void page_up_lookup_table() {
bool show_candidate = key_press_cursor_vertical(Gdk.Key.Up, 0);
show_preedit_and_candidate(show_candidate);
}
public override void page_down_lookup_table() {
bool show_candidate = key_press_cursor_vertical(Gdk.Key.Down, 0);
show_preedit_and_candidate(show_candidate);
}
public override void cursor_up_lookup_table() {
bool show_candidate = key_press_cursor_horizontal(Gdk.Key.Left, 0);
show_preedit_and_candidate(show_candidate);
}
public override void cursor_down_lookup_table() {
bool show_candidate = key_press_cursor_horizontal(Gdk.Key.Right, 0);
show_preedit_and_candidate(show_candidate);
}
public override void candidate_clicked_lookup_table(uint index,
uint button,
uint state) {
if (button == IBusEmojier.BUTTON_CLOSE_BUTTON) {
m_enable_extension = false;
hide_emoji_lookup_table();
m_preedit.hide();
IBus.ExtensionEvent event = new IBus.ExtensionEvent(
"name", m_extension_name,
"is-enabled", false,
"is-extension", true);
panel_extension(event);
return;
}
if (m_emojier == null)
return;
bool show_candidate = false;
uint ncandidates = m_emojier.get_number_of_candidates();
if (ncandidates > 0 && ncandidates >= index) {
m_emojier.set_cursor_pos(index);
show_candidate = m_emojier.has_variants(index);
m_preedit.set_emoji(m_emojier.get_current_candidate());
} else {
warning ("Not supported type %d".printf(xevent.get_event_type()));
return;
}
Gdk.Event event = new Gdk.Event(event_type);
uint32 time = xevent.get_time();
if (time == 0)
time = Gtk.get_current_event_time();
event.key.time = time;
X.Window xid = xevent.get_window();
Gdk.Display? display = Gdk.Display.get_default();
Gdk.Window? window = null;
if (window == null && xid != 0) {
window = Gdk.X11.Window.lookup_for_display(
display as Gdk.X11.Display, xid);
if (!show_candidate) {
IBus.Text text = m_preedit.get_commit_text();
commit_text_update_favorites(text);
hide_emoji_lookup_table();
return;
}
if (window == null && xid != 0) {
window = new Gdk.X11.Window.foreign_for_display(
display as Gdk.X11.Display, xid);
}
if (window == null) {
window = Gdk.get_default_root_window();
window.ref();
}
event.key.window = window;
handle_emoji_typing(event);
show_preedit_and_candidate(show_candidate);
}
}