summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bindings/vala/IBus-1.0-custom.vala7
-rw-r--r--bindings/vala/Makefile.am3
-rw-r--r--bindings/vala/gdk-wayland.vapi7
-rw-r--r--bus/engineproxy.c53
-rw-r--r--bus/engineproxy.h25
-rw-r--r--bus/ibusimpl.c247
-rw-r--r--bus/inputcontext.c399
-rw-r--r--bus/inputcontext.h110
-rw-r--r--bus/panelproxy.c210
-rw-r--r--bus/panelproxy.h23
-rw-r--r--data/ibus.schemas.in12
-rw-r--r--setup/main.py10
-rw-r--r--setup/setup.ui58
-rw-r--r--src/ibusengine.c305
-rw-r--r--src/ibuspanelservice.c318
-rw-r--r--src/ibuspanelservice.h117
-rw-r--r--src/ibusshare.h17
-rw-r--r--src/ibusxevent.c375
-rw-r--r--src/ibusxevent.h143
-rw-r--r--ui/gtk3/Makefile.am3
-rw-r--r--ui/gtk3/emojier.vala991
-rw-r--r--ui/gtk3/emojierapp.vala74
-rw-r--r--ui/gtk3/extension.vala6
-rw-r--r--ui/gtk3/panel.vala23
-rw-r--r--ui/gtk3/panelbinding.vala859
25 files changed, 3695 insertions, 700 deletions
diff --git a/bindings/vala/IBus-1.0-custom.vala b/bindings/vala/IBus-1.0-custom.vala
index cf1fc3fa..7d34a8bd 100644
--- a/bindings/vala/IBus-1.0-custom.vala
+++ b/bindings/vala/IBus-1.0-custom.vala
@@ -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, ...);
+ }
}
diff --git a/bindings/vala/Makefile.am b/bindings/vala/Makefile.am
index fc8e2f01..e4ecab97 100644
--- a/bindings/vala/Makefile.am
+++ b/bindings/vala/Makefile.am
@@ -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)
diff --git a/bindings/vala/gdk-wayland.vapi b/bindings/vala/gdk-wayland.vapi
new file mode 100644
index 00000000..c65f2be4
--- /dev/null
+++ b/bindings/vala/gdk-wayland.vapi
@@ -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 {
+ }
+}
diff --git a/bus/engineproxy.c b/bus/engineproxy.c
index 175aec56..2d98995c 100644
--- a/bus/engineproxy.c
+++ b/bus/engineproxy.c
@@ -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,
diff --git a/bus/engineproxy.h b/bus/engineproxy.h
index 528e61b7..a3006b47 100644
--- a/bus/engineproxy.h
+++ b/bus/engineproxy.h
@@ -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
diff --git a/bus/ibusimpl.c b/bus/ibusimpl.c
index a4ce3d9d..ec1caea8 100644
--- a/bus/ibusimpl.c
+++ b/bus/ibusimpl.c
@@ -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
+bus_ibus_impl_set_panel_extension_keys (BusIBusImpl *ibus,
+ GVariant *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,
- GVariant *parameters,
- BusIBusImpl *ibus)
+_panel_panel_extension_cb (BusPanelProxy *panel,
+ IBusExtensionEvent *event,
+ BusIBusImpl *ibus)
{
- bus_ibus_impl_panel_extension_received (ibus, parameters);
+ 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
diff --git a/bus/inputcontext.c b/bus/inputcontext.c
index dfb98c36..bf9eafcf 100644
--- a/bus/inputcontext.c
+++ b/bus/inputcontext.c
@@ -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,28 +755,85 @@ bus_input_context_property_changed (BusInputContext *context,
error);
}
+
+/**
+ * _panel_process_key_event_cb:
+ *
+ * A GAsyncReadyCallback function to be called when
+ * bus_panel_proxy_process_key_event() is finished.
+ */
+static void
+_panel_process_key_event_cb (GObject *source,
+ GAsyncResult *res,
+ GDBusMethodInvocation *invocation)
+{
+ GError *error = NULL;
+ GVariant *value = g_dbus_proxy_call_finish ((GDBusProxy *)source,
+ res,
+ &error);
+ if (value != NULL) {
+ 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);
+ }
+}
+
+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.
+ * 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,
- GDBusMethodInvocation *invocation)
+ 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) {
- g_dbus_method_invocation_return_value (invocation, value);
+ 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);
}
/**
@@ -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,
@@ -1468,60 +1558,32 @@ 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;
- 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;
+ }
if (PREEDIT_CONDITION) {
bus_input_context_emit_signal (context,
@@ -1542,15 +1604,25 @@ 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;
- }
- context->preedit_visible = FALSE;
+ 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;
+ }
if (PREEDIT_CONDITION) {
bus_input_context_emit_signal (context,
@@ -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);
}
diff --git a/bus/inputcontext.h b/bus/inputcontext.h
index 7674abd8..a46d5c06 100644
--- a/bus/inputcontext.h
+++ b/bus/inputcontext.h
@@ -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
diff --git a/bus/panelproxy.c b/bus/panelproxy.c
index c3908fcf..1c0fcca2 100644
--- a/bus/panelproxy.c
+++ b/bus/panelproxy.c
@@ -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);
@@ -257,9 +261,53 @@ bus_panel_proxy_class_init (BusPanelProxyClass *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,
+ NULL, NULL,
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);
+}
diff --git a/bus/panelproxy.h b/bus/panelproxy.h
index b5a7af17..4d8afb98 100644
--- a/bus/panelproxy.h
+++ b/bus/panelproxy.h
@@ -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
diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in
index 3c6b6f69..f4a019d0 100644
--- a/data/ibus.schemas.in
+++ b/data/ibus.schemas.in
@@ -354,6 +354,18 @@
</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>
<owner>ibus</owner>
diff --git a/setup/main.py b/setup/main.py
index f0eee996..f6adb098 100644
--- a/setup/main.py
+++ b/setup/main.py
@@ -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)
diff --git a/setup/setup.ui b/setup/setup.ui
index e64b1046..f1beb1de 100644
--- a/setup/setup.ui
+++ b/setup/setup.ui
@@ -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">
diff --git a/src/ibusengine.c b/src/ibusengine.c
index fd61102a..a3ccd7dd 100644
--- a/src/ibusengine.c
+++ b/src/ibusengine.c
@@ -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;
}
@@ -954,6 +983,97 @@ ibus_engine_service_authorized_method (IBusService *service,
}
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,
const gchar *sender,
@@ -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 *
diff --git a/src/ibuspanelservice.c b/src/ibuspanelservice.c
index f37b91c3..71028ebf 100644
--- a/src/ibuspanelservice.c
+++ b/src/ibuspanelservice.c
@@ -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,
+ 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,
- G_TYPE_VARIANT);
+ 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) \
diff --git a/src/ibuspanelservice.h b/src/ibuspanelservice.h
index 60ef842b..d91f2309 100644
--- a/src/ibuspanelservice.h
+++ b/src/ibuspanelservice.h
@@ -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
diff --git a/src/ibusshare.h b/src/ibusshare.h
index 757d915b..4f5a306b 100644
--- a/src/ibusshare.h
+++ b/src/ibusshare.h
@@ -74,6 +74,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:
*
* Address of IBus config service.
@@ -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:
diff --git a/src/ibusxevent.c b/src/ibusxevent.c
index dea80272..287bb99b 100644
--- a/src/ibusxevent.c
+++ b/src/ibusxevent.c
@@ -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,25 +91,347 @@ 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)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (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));
}
diff --git a/src/ibusxevent.h b/src/ibusxevent.h
index f35f14e4..d44cc8f4 100644
--- a/src/ibusxevent.h
+++ b/src/ibusxevent.h
@@ -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 >*/
diff --git a/ui/gtk3/Makefile.am b/ui/gtk3/Makefile.am
index bf9f98d7..aaba7a4d 100644
--- a/ui/gtk3/Makefile.am
+++ b/ui/gtk3/Makefile.am
@@ -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) \
diff --git a/ui/gtk3/emojier.vala b/ui/gtk3/emojier.vala
index 0c0865f1..cd98c9d7 100644
--- a/ui/gtk3/emojier.vala
+++ b/ui/gtk3/emojier.vala
@@ -226,43 +226,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
}
}
}
- private class ETitleLabelBox : Gtk.HeaderBar {
- private Gtk.Label m_lang_label;
- private Gtk.Label m_title_label;
-
- public ETitleLabelBox(string title) {
- GLib.Object(
- name : "IBusEmojierTitleLabelBox",
- show_close_button: true,
- decoration_layout: ":close",
- title: title
- );
- var vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
- set_custom_title(vbox);
- m_title_label = new Gtk.Label(title);
- m_title_label.get_style_context().add_class(Gtk.STYLE_CLASS_TITLE);
- vbox.pack_start(m_title_label, true, false, 0);
- m_lang_label = new Gtk.Label(null);
- m_lang_label.get_style_context().add_class(
- Gtk.STYLE_CLASS_SUBTITLE);
- vbox.pack_start(m_lang_label, true, false, 0);
-
- var menu = new GLib.Menu();
- menu.append(_("Show emoji variants"), "win.variant");
- var menu_button = new Gtk.MenuButton();
- menu_button.set_direction(Gtk.ArrowType.NONE);
- menu_button.set_valign(Gtk.Align.CENTER);
- menu_button.set_menu_model(menu);
- menu_button.set_tooltip_text(_("Menu"));
- pack_end(menu_button);
- }
- public new void set_title(string title) {
- m_title_label.set_text(title);
- }
- public void set_lang_label(string str) {
- m_lang_label.set_text(str);
- }
- }
private class LoadProgressObject : GLib.Object {
public LoadProgressObject() {
}
@@ -275,6 +238,8 @@ public class IBusEmojier : Gtk.ApplicationWindow {
BACKWARD,
}
+ public const uint BUTTON_CLOSE_BUTTON = 1000;
+
private const uint EMOJI_GRID_PAGE = 10;
private const string EMOJI_CATEGORY_FAVORITES = N_("Favorites");
private const string EMOJI_CATEGORY_OTHERS = N_("Others");
@@ -313,11 +278,19 @@ public class IBusEmojier : Gtk.ApplicationWindow {
private static bool m_show_unicode = false;
private static LoadProgressObject m_unicode_progress_object;
private static bool m_loaded_unicode = false;
+ private static string m_warning_message = "";
private ThemedRGBA m_rgba;
private Gtk.Box m_vbox;
- private ETitleLabelBox m_title;
private EEntry m_entry;
+ /* If emojier is emoji category list or Unicode category list,
+ * m_annotation is "" and preedit is also "".
+ * If emojier is candidate mode, m_annotation is an annotation and
+ * get_current_candidate() returns the current emoji.
+ * But the current preedit can be "" in candidate mode in case that
+ * Unicode candidate window has U+0000.
+ */
+ private string m_annotation = "";
private string? m_backward;
private int m_backward_index = -1;
private EScrolledWindow? m_scrolled_window = null;
@@ -326,8 +299,20 @@ public class IBusEmojier : Gtk.ApplicationWindow {
private string m_input_context_path = "";
private GLib.MainLoop? m_loop;
private string? m_result;
- private string? m_unicode_point = null;
+ /* If m_candidate_panel_is_visible is true, emojier is candidate mode and
+ * the emoji lookup window is visible.
+ * If m_candidate_panel_is_visible is false, the emoji lookup window is
+ * not visible but the mode is not clear.
+ */
private bool m_candidate_panel_is_visible;
+ /* If m_candidate_panel_mode is true, emojier is candidate mode and
+ * it does not depend on whether the window is visible or not.
+ * I.E. the first candidate does not show the lookup window and the
+ * second one shows the window.
+ * If m_candidate_panel_mode is false, emojier is emoji category list or
+ * Unicode category list.
+ */
+ private bool m_candidate_panel_mode;
private int m_category_active_index = -1;
private IBus.LookupTable m_lookup_table;
private Gtk.Label[] m_candidates;
@@ -337,23 +322,18 @@ public class IBusEmojier : Gtk.ApplicationWindow {
protected static double m_mouse_x;
protected static double m_mouse_y;
private Gtk.ProgressBar m_unicode_progress_bar;
+ private uint m_unicode_progress_id;
private Gtk.Label m_unicode_percent_label;
private double m_unicode_percent;
+ private Gdk.Rectangle m_cursor_location;
+ private bool m_is_up_side_down = false;
+ private uint m_redraw_window_id;
public signal void candidate_clicked(uint index, uint button, uint state);
public IBusEmojier() {
GLib.Object(
- type : Gtk.WindowType.TOPLEVEL,
- events : Gdk.EventMask.KEY_PRESS_MASK |
- Gdk.EventMask.KEY_RELEASE_MASK |
- Gdk.EventMask.BUTTON_PRESS_MASK |
- Gdk.EventMask.BUTTON_RELEASE_MASK,
- window_position : Gtk.WindowPosition.CENTER,
- icon_name: "ibus-setup",
- accept_focus : true,
- resizable : true,
- focus_visible : true
+ type : Gtk.WindowType.POPUP
);
// GLib.ActionEntry accepts const variables only.
@@ -363,6 +343,9 @@ public class IBusEmojier : Gtk.ApplicationWindow {
new GLib.Variant.boolean(m_show_emoji_variant));
action.activate.connect(check_action_variant_cb);
add_action(action);
+ action = new GLib.SimpleAction("close", null);
+ action.activate.connect(action_close_cb);
+ add_action(action);
if (m_current_lang_id == null)
m_current_lang_id = "en";
if (m_emoji_font_family == null)
@@ -448,14 +431,12 @@ public class IBusEmojier : Gtk.ApplicationWindow {
css_provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
- m_title = new ETitleLabelBox(_("Emoji Choice"));
- set_titlebar(m_title);
m_vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
add(m_vbox);
m_entry = new EEntry();
m_entry.set_placeholder_text(_("Type annotation or choose emoji"));
- m_vbox.add(m_entry);
+ //m_vbox.add(m_entry);
m_entry.changed.connect(() => {
update_candidate_window();
});
@@ -480,10 +461,16 @@ public class IBusEmojier : Gtk.ApplicationWindow {
m_loop.quit();
});
+ size_allocate.connect((w, a) => {
+ adjust_window_position();
+ });
+
candidate_clicked.connect((i, b, s) => {
- candidate_panel_select_index(i);
+ if (m_input_context_path != "")
+ candidate_panel_select_index(i, b);
});
+
if (m_annotation_to_emojis_dict == null) {
reload_emoji_dict();
}
@@ -814,6 +801,12 @@ public class IBusEmojier : Gtk.ApplicationWindow {
private void remove_all_children() {
+ if (m_list_box != null) {
+ foreach (Gtk.Widget w in m_list_box.get_children()) {
+ w.destroy();
+ }
+ m_list_box = null;
+ }
foreach (Gtk.Widget w in m_vbox.get_children()) {
if (w.name == "IBusEmojierEntry" ||
w.name == "IBusEmojierTitleLabelBox") {
@@ -824,15 +817,40 @@ public class IBusEmojier : Gtk.ApplicationWindow {
}
+ private void clamp_page() {
+ Gtk.ListBoxRow row;
+ if (m_category_active_index >= 0) {
+ row = m_list_box.get_row_at_index(m_category_active_index);
+ m_list_box.select_row(row);
+ } else {
+ row = m_list_box.get_row_at_index(0);
+ }
+ Gtk.Allocation alloc = { 0, 0, 0, 0 };
+ row.get_allocation(out alloc);
+ var adjustment = m_scrolled_window.get_vadjustment();
+ adjustment.clamp_page(alloc.y, alloc.y + alloc.height);
+ return_val_if_fail(m_category_active_index >= 0, false);
+ m_lookup_table.set_cursor_pos((uint)m_category_active_index);
+ }
+
+
private void show_category_list() {
+ // Do not call remove_all_children() to work adjustment.clamp_page()
+ // with PageUp/Down.
+ // After show_candidate_panel() is called, m_category_active_index
+ // is saved for Escape key but m_list_box is null by
+ // remove_all_children().
+ if (m_category_active_index >= 0 && m_list_box != null) {
+ var row = m_list_box.get_row_at_index(m_category_active_index);
+ m_list_box.select_row(row);
+ return;
+ }
+ if (m_category_active_index < 0)
+ m_category_active_index = 0;
remove_all_children();
m_scrolled_window = new EScrolledWindow();
set_fixed_size();
- m_title.set_title(_("Emoji Choice"));
- string language =
- IBus.get_language_name(m_current_lang_id);
- m_title.set_lang_label(language);
m_vbox.add(m_scrolled_window);
Gtk.Viewport viewport = new Gtk.Viewport(null, null);
m_scrolled_window.add(viewport);
@@ -842,53 +860,21 @@ public class IBusEmojier : Gtk.ApplicationWindow {
Gtk.Adjustment adjustment = m_scrolled_window.get_vadjustment();
m_list_box.set_adjustment(adjustment);
m_list_box.row_activated.connect((box, gtkrow) => {
- m_category_active_index = -1;
+ m_category_active_index = gtkrow.get_index();
EBoxRow row = gtkrow as EBoxRow;
show_emoji_for_category(row.text);
+ show_all();
});
- uint n = 0;
- if (m_favorites.length > 0) {
- EBoxRow row = new EBoxRow(EMOJI_CATEGORY_FAVORITES);
- EPaddedLabelBox widget =
- new EPaddedLabelBox(_(EMOJI_CATEGORY_FAVORITES),
- Gtk.Align.CENTER);
- row.add(widget);
- m_list_box.add(row);
- if (n++ == m_category_active_index)
- m_list_box.select_row(row);
- }
- GLib.List<unowned string> categories =
- m_category_to_emojis_dict.get_keys();
- // FIXME: How to cast GLib.CompareFunc<string> to strcmp?
- categories.sort((a, b) => {
- if (a == EMOJI_CATEGORY_OTHERS && b != EMOJI_CATEGORY_OTHERS)
- return 1;
- else if (a != EMOJI_CATEGORY_OTHERS && b == EMOJI_CATEGORY_OTHERS)
- return -1;
- return GLib.strcmp(_(a), _(b));
- });
- foreach (unowned string category in categories) {
- // "Others" category includes next unicode chars and fonts do not support
- // the base and varints yet.
- if (category == EMOJI_CATEGORY_OTHERS)
- continue;
+ uint ncandidates = m_lookup_table.get_number_of_candidates();
+ for (uint i = 0; i < ncandidates; i++) {
+ string category = m_lookup_table.get_candidate(i).text;
EBoxRow row = new EBoxRow(category);
EPaddedLabelBox widget =
new EPaddedLabelBox(_(category), Gtk.Align.CENTER);
row.add(widget);
m_list_box.add(row);
- if (n++ == m_category_active_index)
- m_list_box.select_row(row);
- }
- if (m_unicode_block_list.length() > 0) {
- EBoxRow row = new EBoxRow(EMOJI_CATEGORY_UNICODE);
- EPaddedLabelBox widget =
- new EPaddedLabelBox(_(EMOJI_CATEGORY_UNICODE),
- Gtk.Align.CENTER);
- row.add(widget);
- m_list_box.add(row);
- if (n++ == m_category_active_index)
+ if (i == m_category_active_index)
m_list_box.select_row(row);
}
@@ -903,6 +889,7 @@ public class IBusEmojier : Gtk.ApplicationWindow {
private void show_emoji_for_category(string category) {
if (category == EMOJI_CATEGORY_FAVORITES) {
m_lookup_table.clear();
+ m_candidate_panel_mode = true;
foreach (unowned string favorate in m_favorites) {
IBus.Text text = new IBus.Text.from_string(favorate);
m_lookup_table.append_candidate(text);
@@ -911,25 +898,26 @@ public class IBusEmojier : Gtk.ApplicationWindow {
} else if (category == EMOJI_CATEGORY_UNICODE) {
m_category_active_index = -1;
m_show_unicode = true;
- show_unicode_blocks();
+ update_unicode_blocks();
return;
} else {
unowned GLib.SList<unowned string> emojis =
m_category_to_emojis_dict.lookup(category);
m_lookup_table.clear();
+ m_candidate_panel_mode = true;
foreach (unowned string emoji in emojis) {
IBus.Text text = new IBus.Text.from_string(emoji);
m_lookup_table.append_candidate(text);
}
m_backward = category;
}
+ m_annotation = m_lookup_table.get_candidate(0).text;
// Restore the cursor position before the special table of
// emoji variants is shown.
if (m_backward_index >= 0) {
m_lookup_table.set_cursor_pos((uint)m_backward_index);
m_backward_index = -1;
}
- show_candidate_panel();
}
@@ -940,18 +928,28 @@ public class IBusEmojier : Gtk.ApplicationWindow {
IBus.Text text = new IBus.Text.from_string(emoji);
m_lookup_table.append_candidate(text);
}
- show_candidate_panel();
}
private void show_unicode_blocks() {
+ // Do not call remove_all_children() to work adjustment.clamp_page()
+ // with PageUp/Down.
+ // After show_candidate_panel() is called, m_category_active_index
+ // is saved for Escape key but m_list_box is null by
+ // remove_all_children().
+ if (m_category_active_index >= 0 && m_list_box != null) {
+ var row = m_list_box.get_row_at_index(m_category_active_index);
+ m_list_box.select_row(row);
+ return;
+ }
+ if (m_category_active_index < 0)
+ m_category_active_index = 0;
m_show_unicode = true;
if (m_default_window_width == 0 && m_default_window_height == 0)
get_size(out m_default_window_width, out m_default_window_height);
remove_all_children();
set_fixed_size();
- m_title.set_title(_("Unicode Choice"));
EPaddedLabelBox label =
new EPaddedLabelBox(_("Bring back emoji choice"),
Gtk.Align.CENTER,
@@ -964,10 +962,10 @@ public class IBusEmojier : Gtk.ApplicationWindow {
m_category_active_index = -1;
m_show_unicode = false;
hide_candidate_panel();
+ show_all();
return true;
});
m_scrolled_window = new EScrolledWindow();
- m_title.set_lang_label("");
m_vbox.add(m_scrolled_window);
Gtk.Viewport viewport = new Gtk.Viewport(null, null);
m_scrolled_window.add(viewport);
@@ -977,9 +975,10 @@ public class IBusEmojier : Gtk.ApplicationWindow {
Gtk.Adjustment adjustment = m_scrolled_window.get_vadjustment();
m_list_box.set_adjustment(adjustment);
m_list_box.row_activated.connect((box, gtkrow) => {
- m_category_active_index = -1;
+ m_category_active_index = gtkrow.get_index();
EBoxRow row = gtkrow as EBoxRow;
show_unicode_for_block(row.text);
+ show_candidate_panel();
});
uint n = 0;
@@ -1007,44 +1006,18 @@ public class IBusEmojier : Gtk.ApplicationWindow {
m_list_box.unselect_all();
m_list_box.invalidate_filter();
m_list_box.set_selection_mode(Gtk.SelectionMode.SINGLE);
+ Gtk.ListBoxRow row = m_list_box.get_row_at_index((int)n - 1);
+
+ // If clamp_page() would be called without the allocation signal,
+ // the jumping page could be failed when returns from
+ // show_unicode_for_block() with Escape key.
+ row.size_allocate.connect((w, a) => {
+ clamp_page();
+ });
}
+
private void show_unicode_for_block(string block_name) {
- if (!m_loaded_unicode) {
- remove_all_children();
- set_fixed_size();
- m_unicode_progress_bar = new Gtk.ProgressBar();
- m_unicode_progress_bar.set_ellipsize(Pango.EllipsizeMode.MIDDLE);
- m_unicode_progress_bar.set_halign(Gtk.Align.CENTER);
- m_unicode_progress_bar.set_valign(Gtk.Align.CENTER);
- m_vbox.add(m_unicode_progress_bar);
- m_unicode_progress_bar.show();
- var hbox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5);
- hbox.set_halign(Gtk.Align.CENTER);
- hbox.set_valign(Gtk.Align.CENTER);
- m_vbox.add(hbox);
- var label = new Gtk.Label(_("Loading a Unicode dictionary:"));
- hbox.pack_start(label, false, true, 0);
- m_unicode_percent_label = new Gtk.Label("");
- hbox.pack_start(m_unicode_percent_label, false, true, 0);
- hbox.show_all();
-
- m_unicode_progress_object.deserialize_unicode.connect((i, n) => {
- m_unicode_percent = (double)i / n;
- });
- GLib.Timeout.add(100, () => {
- m_unicode_progress_bar.set_fraction(m_unicode_percent);
- m_unicode_percent_label.set_text(
- "%.0f%%\n".printf(m_unicode_percent * 100));
- m_unicode_progress_bar.show();
- m_unicode_percent_label.show();
- if (m_loaded_unicode) {
- show_unicode_for_block(block_name);
- }
- return !m_loaded_unicode;
- });
- return;
- }
unichar start = 0;
unichar end = 0;
foreach (unowned IBus.UnicodeBlock block in m_unicode_block_list) {
@@ -1055,6 +1028,7 @@ public class IBusEmojier : Gtk.ApplicationWindow {
}
}
m_lookup_table.clear();
+ m_candidate_panel_mode = true;
for (unichar ch = start; ch < end; ch++) {
unowned IBus.UnicodeData? data =
m_unicode_to_data_dict.lookup(ch);
@@ -1064,7 +1038,7 @@ public class IBusEmojier : Gtk.ApplicationWindow {
m_lookup_table.append_candidate(text);
}
m_backward = block_name;
- show_candidate_panel();
+ m_annotation = m_lookup_table.get_candidate(0).text;
}
@@ -1091,6 +1065,41 @@ public class IBusEmojier : Gtk.ApplicationWindow {
prev_button.set_relief(Gtk.ReliefStyle.NONE);
prev_button.set_tooltip_text(_("Page Up"));
+ var menu = new GLib.Menu();
+ menu.append(_("Show emoji variants"), "win.variant");
+ menu.append(_("Close"), "win.close");
+ var menu_button = new Gtk.MenuButton();
+ menu_button.set_direction(Gtk.ArrowType.NONE);
+ menu_button.set_valign(Gtk.Align.CENTER);
+ menu_button.set_menu_model(menu);
+ menu_button.set_relief(Gtk.ReliefStyle.NONE);
+ menu_button.set_tooltip_text(_("Menu"));
+
+ IBus.Text text = this.get_title_text();
+ Pango.AttrList attrs = get_pango_attr_list_from_ibus_text(text);
+ Gtk.Label title_label = new Gtk.Label(text.get_text());
+ title_label.set_attributes(attrs);
+
+ Gtk.Button? warning_button = null;
+ if (m_warning_message != "") {
+ warning_button = new Gtk.Button();
+ warning_button.set_tooltip_text(
+ _("Click to view a warning message"));
+ warning_button.set_image(new Gtk.Image.from_icon_name(
+ "dialog-warning",
+ Gtk.IconSize.MENU));
+ warning_button.set_relief(Gtk.ReliefStyle.NONE);
+ warning_button.clicked.connect(() => {
+ Gtk.Label warning_label = new Gtk.Label(m_warning_message);
+ warning_label.set_line_wrap(true);
+ warning_label.set_max_width_chars(40);
+ Gtk.Popover popover = new Gtk.Popover(warning_button);
+ popover.add(warning_label);
+ popover.show_all();
+ popover.popup();
+ });
+ }
+
Gtk.Box buttons_hbox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
Gtk.Label state_label = new Gtk.Label(null);
state_label.set_size_request(10, -1);
@@ -1099,14 +1108,55 @@ public class IBusEmojier : Gtk.ApplicationWindow {
buttons_hbox.pack_start(state_label, false, true, 0);
buttons_hbox.pack_start(prev_button, false, false, 0);
buttons_hbox.pack_start(next_button, false, false, 0);
+ buttons_hbox.pack_start(title_label, false, false, 0);
+ if (warning_button != null)
+ buttons_hbox.pack_start(warning_button, false, false, 0);
+ buttons_hbox.pack_end(menu_button, false, false, 0);
m_vbox.pack_start(buttons_hbox, false, false, 0);
buttons_hbox.show_all();
}
- private bool check_unicode_point() {
- string annotation = m_entry.get_text();
- m_unicode_point = null;
+ private void show_unicode_progress_bar() {
+ m_unicode_progress_bar = new Gtk.ProgressBar();
+ m_unicode_progress_bar.set_ellipsize(Pango.EllipsizeMode.MIDDLE);
+ m_unicode_progress_bar.set_halign(Gtk.Align.CENTER);
+ m_unicode_progress_bar.set_valign(Gtk.Align.CENTER);
+ m_vbox.add(m_unicode_progress_bar);
+ m_unicode_progress_bar.show();
+ var hbox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5);
+ hbox.set_halign(Gtk.Align.CENTER);
+ hbox.set_valign(Gtk.Align.CENTER);
+ m_vbox.add(hbox);
+ var label = new Gtk.Label(_("Loading a Unicode dictionary:"));
+ hbox.pack_start(label, false, true, 0);
+ m_unicode_percent_label = new Gtk.Label("");
+ hbox.pack_start(m_unicode_percent_label, false, true, 0);
+ hbox.show_all();
+
+ m_unicode_progress_object.deserialize_unicode.connect((i, n) => {
+ m_unicode_percent = (double)i / n;
+ });
+ if (m_unicode_progress_id > 0) {
+ GLib.Source.remove(m_unicode_progress_id);
+ }
+ m_unicode_progress_id = GLib.Timeout.add(100, () => {
+ m_unicode_progress_id = 0;
+ m_unicode_progress_bar.set_fraction(m_unicode_percent);
+ m_unicode_percent_label.set_text(
+ "%.0f%%\n".printf(m_unicode_percent * 100));
+ m_unicode_progress_bar.show();
+ m_unicode_percent_label.show();
+ if (m_loaded_unicode) {
+ show_candidate_panel();
+ }
+ return !m_loaded_unicode;
+ });
+ }
+
+
+ private static string? check_unicode_point(string annotation) {
+ string unicode_point = null;
// Add "0x" because uint64.ascii_strtoull() is not accessible
// and need to use uint64.parse()
var buff = new GLib.StringBuilder("0x");
@@ -1114,33 +1164,31 @@ public class IBusEmojier : Gtk.ApplicationWindow {
for (int i = 0; i < annotation.char_count(); i++) {
unichar ch = annotation.get_char(i);
if (ch == 0)
- return false;
+ return null;
if (ch.isspace()) {
unichar code = (unichar)uint64.parse(buff.str);
buff.assign("0x");
if (!code.validate())
- return false;
+ return null;
retval.append(code.to_string());
continue;
}
if (!ch.isxdigit())
- return false;
+ return null;
buff.append_unichar(ch);
}
unichar code = (unichar)uint64.parse(buff.str);
if (!code.validate())
- return false;
+ return null;
retval.append(code.to_string());
- m_unicode_point = retval.str;
- if (m_unicode_point == null)
- return true;
- IBus.Text text = new IBus.Text.from_string(m_unicode_point);
- m_lookup_table.append_candidate(text);
- return true;
+ unicode_point = retval.str;
+ if (unicode_point == null)
+ return null;
+ return unicode_point;
}
- private GLib.SList<string>?
+ private static GLib.SList<string>?
lookup_emojis_from_annotation(string annotation) {
GLib.SList<string>? total_emojis = null;
unowned GLib.SList<string>? sub_emojis = null;
@@ -1221,19 +1269,19 @@ public class IBusEmojier : Gtk.ApplicationWindow {
return total_emojis;
}
+
private void update_candidate_window() {
- string annotation = m_entry.get_text();
+ string annotation = m_annotation;
if (annotation.length == 0) {
- hide_candidate_panel();
m_backward = null;
return;
}
+ m_lookup_table.clear();
+ m_category_active_index = -1;
if (annotation.length > m_emoji_max_seq_len) {
- hide_candidate_panel();
return;
}
- // Call check_unicode_point() to get m_unicode_point
- check_unicode_point();
+ string? unicode_point = check_unicode_point(annotation);
GLib.SList<string>? total_emojis =
lookup_emojis_from_annotation(annotation);
if (total_emojis == null) {
@@ -1246,18 +1294,75 @@ public class IBusEmojier : Gtk.ApplicationWindow {
annotation = annotation.down();
total_emojis = lookup_emojis_from_annotation(annotation);
}
- if (total_emojis == null && m_unicode_point == null) {
- hide_candidate_panel();
+ if (total_emojis == null && unicode_point == null) {
return;
}
- m_lookup_table.clear();
- // Call check_unicode_point() to update m_lookup_table
- check_unicode_point();
+ if (unicode_point != null) {
+ IBus.Text text = new IBus.Text.from_string(unicode_point);
+ m_lookup_table.append_candidate(text);
+ }
foreach (unowned string emoji in total_emojis) {
IBus.Text text = new IBus.Text.from_string(emoji);
m_lookup_table.append_candidate(text);
}
- show_candidate_panel();
+ m_candidate_panel_is_visible =
+ (m_lookup_table.get_number_of_candidates() > 0) ? true : false;
+ m_candidate_panel_mode = true;
+ }
+
+
+ private void update_category_list() {
+ // Always update m_lookup_table even if the contents are same
+ // because m_category_active_index needs to be kept after
+ // bring back this API from show_emoji_for_category().
+ reset_window_mode();
+ m_lookup_table.clear();
+ IBus.Text text;
+ if (m_favorites.length > 0) {
+ text = new IBus.Text.from_string(EMOJI_CATEGORY_FAVORITES);
+ m_lookup_table.append_candidate(text);
+ }
+ GLib.List<unowned string> categories =
+ m_category_to_emojis_dict.get_keys();
+ // FIXME: How to cast GLib.CompareFunc<string> to strcmp?
+ categories.sort((a, b) => {
+ if (a == EMOJI_CATEGORY_OTHERS && b != EMOJI_CATEGORY_OTHERS)
+ return 1;
+ else if (a != EMOJI_CATEGORY_OTHERS && b == EMOJI_CATEGORY_OTHERS)
+ return -1;
+ return GLib.strcmp(_(a), _(b));
+ });
+ foreach (unowned string category in categories) {
+ // "Others" category includes next unicode chars and fonts do not
+ // support the base and varints yet.
+ if (category == EMOJI_CATEGORY_OTHERS)
+ continue;
+ text = new IBus.Text.from_string(category);
+ m_lookup_table.append_candidate(text);
+ }
+ if (m_unicode_block_list.length() > 0) {
+ text = new IBus.Text.from_string(EMOJI_CATEGORY_UNICODE);
+ m_lookup_table.append_candidate(text);
+ }
+ // Do not set m_category_active_index to 0 here so that
+ // show_category_list() handles it.
+ }
+
+
+ private void update_unicode_blocks() {
+ // Always update m_lookup_table even if the contents are same
+ // because m_category_active_index needs to be kept after
+ // bring back this API from show_emoji_for_category().
+ reset_window_mode();
+ m_lookup_table.clear();
+ m_show_unicode = true;
+ foreach (unowned IBus.UnicodeBlock block in m_unicode_block_list) {
+ string name = block.get_name();
+ IBus.Text text = new IBus.Text.from_string(name);
+ m_lookup_table.append_candidate(text);
+ }
+ // Do not set m_category_active_index to 0 here so that
+ // show_unicode_blocks() handles it.
}
@@ -1283,27 +1388,27 @@ public class IBusEmojier : Gtk.ApplicationWindow {
uint page_size = m_lookup_table.get_page_size();
uint ncandidates = m_lookup_table.get_number_of_candidates();
uint cursor = m_lookup_table.get_cursor_pos();
-
uint page_start_pos = cursor / page_size * page_size;
uint page_end_pos = uint.min(page_start_pos + page_size, ncandidates);
+ Gtk.Button? backward_button = null;
if (m_backward != null) {
- string backward_desc =
- "%s (%u / %u)".printf(_(m_backward), cursor, ncandidates - 1);
+ string backward_desc = _(m_backward);
EPaddedLabelBox label =
new EPaddedLabelBox(backward_desc,
Gtk.Align.CENTER,
TravelDirection.BACKWARD);
- Gtk.Button button = new Gtk.Button();
- button.add(label);
- m_vbox.add(button);
- button.show_all();
- button.button_press_event.connect((w, e) => {
+ backward_button = new Gtk.Button();
+ backward_button.add(label);
+ backward_button.button_press_event.connect((w, e) => {
// Bring back to emoji candidate panel in case
// m_show_emoji_variant is enabled and shows variants.
- if (m_backward_index >= 0 && m_backward != null)
+ if (m_backward_index >= 0 && m_backward != null) {
show_emoji_for_category(m_backward);
- else
+ show_candidate_panel();
+ } else {
hide_candidate_panel();
+ show_all();
+ }
return true;
});
}
@@ -1385,34 +1490,60 @@ public class IBusEmojier : Gtk.ApplicationWindow {
}
if (n > 0) {
m_candidate_panel_is_visible = true;
- show_arrow_buttons();
- m_vbox.add(grid);
- grid.show_all();
- string text = m_lookup_table.get_candidate(cursor).text;
- unowned IBus.EmojiData? data = m_emoji_to_data_dict.lookup(text);
- if (data != null) {
- show_emoji_description(data, text);
- return;
+ if (!m_is_up_side_down) {
+ show_arrow_buttons();
+ if (backward_button != null) {
+ m_vbox.add(backward_button);
+ backward_button.show_all();
+ }
+ m_vbox.add(grid);
+ grid.show_all();
+ show_description();
+ if (!m_loaded_unicode)
+ show_unicode_progress_bar();
}
- if (text.char_count() <= 1) {
- unichar code = text.get_char();
- unowned IBus.UnicodeData? udata =
- m_unicode_to_data_dict.lookup(code);
- if (udata != null) {
- show_unicode_description(udata, text);
- return;
+ if (m_is_up_side_down) {
+ if (!m_loaded_unicode)
+ show_unicode_progress_bar();
+ show_description();
+ m_vbox.add(grid);
+ grid.show_all();
+ if (backward_button != null) {
+ m_vbox.add(backward_button);
+ backward_button.show_all();
}
+ show_arrow_buttons();
}
- EPaddedLabelBox widget = new EPaddedLabelBox(
- _("Description: %s").printf(_("None")),
- Gtk.Align.START);
- m_vbox.add(widget);
- widget.show_all();
- show_code_point_description(text);
}
}
+ private void show_description() {
+ uint cursor = m_lookup_table.get_cursor_pos();
+ string text = m_lookup_table.get_candidate(cursor).text;
+ unowned IBus.EmojiData? data = m_emoji_to_data_dict.lookup(text);
+ if (data != null) {
+ show_emoji_description(data, text);
+ return;
+ }
+ if (text.char_count() <= 1) {
+ unichar code = text.get_char();
+ unowned IBus.UnicodeData? udata =
+ m_unicode_to_data_dict.lookup(code);
+ if (udata != null) {
+ show_unicode_description(udata, text);
+ return;
+ }
+ }
+ EPaddedLabelBox widget = new EPaddedLabelBox(
+ _("Description: %s").printf(_("None")),
+ Gtk.Align.START);
+ m_vbox.add(widget);
+ widget.show_all();
+ show_code_point_description(text);
+ }
+
+
private void show_emoji_description(IBus.EmojiData data,
string text) {
unowned string description = data.get_description();
@@ -1473,14 +1604,17 @@ public class IBusEmojier : Gtk.ApplicationWindow {
private void hide_candidate_panel() {
+ hide();
m_enter_notify_enable = true;
- m_candidate_panel_is_visible = false;
- if (m_loop.is_running()) {
- if (m_show_unicode)
- show_unicode_blocks();
- else
- show_category_list();
- }
+ m_annotation = "";
+ // Call remove_all_children() instead of show_category_list()
+ // so that show_category_list do not remove children with
+ // PageUp/PageDown.
+ remove_all_children();
+ if (m_show_unicode)
+ update_unicode_blocks();
+ else
+ update_category_list();
}
@@ -1498,20 +1632,34 @@ public class IBusEmojier : Gtk.ApplicationWindow {
}
- private void candidate_panel_select_index(uint index) {
+ private void candidate_panel_select_index(uint index,
+ uint button) {
+ if (button == BUTTON_CLOSE_BUTTON) {
+ hide();
+ if (m_candidate_panel_mode &&
+ m_lookup_table.get_number_of_candidates() > 0) {
+ // Call remove_all_children() instead of show_category_list()
+ // so that show_category_list do not remove children with
+ // PageUp/PageDown.
+ remove_all_children();
+ }
+ m_result = "";
+ return;
+ }
string text = m_lookup_table.get_candidate(index).text;
unowned GLib.SList<string>? emojis =
m_emoji_to_emoji_variants_dict.lookup(text);
if (m_show_emoji_variant && emojis != null &&
m_backward_index < 0) {
show_emoji_variants(emojis);
+ show_all();
} else {
m_result = text;
- m_loop.quit();
- hide_candidate_panel();
+ hide();
}
}
+
private void candidate_panel_cursor_down() {
enter_notify_disable_with_timer();
uint ncandidates = m_lookup_table.get_number_of_candidates();
@@ -1523,7 +1671,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
} else {
m_lookup_table.set_cursor_pos(0);
}
- show_candidate_panel();
}
@@ -1541,7 +1688,6 @@ public class IBusEmojier : Gtk.ApplicationWindow {
} else {
m_lookup_table.set_cursor_pos(0);
}
- show_candidate_panel();
}
@@ -1558,7 +1704,9 @@ public class IBusEmojier : Gtk.ApplicationWindow {
return page_num;
}
+
private bool category_list_cursor_move(uint keyval) {
+ return_val_if_fail (m_list_box != null, false);
GLib.List<weak Gtk.Widget> list = m_list_box.get_children();
int length = (int)list.length();
if (length == 0)
@@ -1600,32 +1748,37 @@ public class IBusEmojier : Gtk.ApplicationWindow {
var row = m_list_box.get_selected_row();
if (row != null)
m_list_box.unselect_row(row);
- if (m_category_active_index >= 0) {
- row = m_list_box.get_row_at_index(m_category_active_index);
- m_list_box.select_row(row);
- } else {
- row = m_list_box.get_row_at_index(0);
- }
- Gtk.Allocation alloc = { 0, 0, 0, 0 };
- row.get_allocation(out alloc);
- var adjustment = m_scrolled_window.get_vadjustment();
- adjustment.clamp_page(alloc.y, alloc.y + alloc.height);
+ clamp_page ();
return true;
}
- private bool key_press_cursor_horizontal(uint keyval,
- uint modifiers) {
+ public bool has_variants(uint index) {
+ if (index >= m_lookup_table.get_number_of_candidates())
+ return false;
+ string text = m_lookup_table.get_candidate(index).text;
+ unowned GLib.SList<string>? emojis =
+ m_emoji_to_emoji_variants_dict.lookup(text);
+ if (m_show_emoji_variant && emojis != null &&
+ m_backward_index < 0) {
+ show_emoji_variants(emojis);
+ return true;
+ }
+ return false;
+ }
+
+
+ public bool key_press_cursor_horizontal(uint keyval,
+ uint modifiers) {
assert (keyval == Gdk.Key.Left || keyval == Gdk.Key.Right);
- uint ncandidates = m_lookup_table.get_number_of_candidates();
- if (m_candidate_panel_is_visible && ncandidates > 1) {
+ if (m_candidate_panel_mode &&
+ m_lookup_table.get_number_of_candidates() > 0) {
enter_notify_disable_with_timer();
if (keyval == Gdk.Key.Left)
m_lookup_table.cursor_up();
else if (keyval == Gdk.Key.Right)
m_lookup_table.cursor_down();
- show_candidate_panel();
} else if (m_entry.get_text().length > 0) {
int step = 0;
if (keyval == Gdk.Key.Left)
@@ -1650,8 +1803,8 @@ public class IBusEmojier : Gtk.ApplicationWindow {
}
- private bool key_press_cursor_vertical(uint keyval,
- uint modifiers) {
+ public bool key_press_cursor_vertical(uint keyval,
+ uint modifiers) {
assert (keyval == Gdk.Key.Down || keyval == Gdk.Key.Up ||
keyval == Gdk.Key.Page_Down || keyval == Gdk.Key.Page_Up);
@@ -1661,8 +1814,8 @@ public class IBusEmojier : Gtk.ApplicationWindow {
else if (keyval == Gdk.Key.Up)
keyval = Gdk.Key.Page_Up;
}
- uint ncandidates = m_lookup_table.get_number_of_candidates();
- if (m_candidate_panel_is_visible && ncandidates > 1) {
+ if ((m_candidate_panel_is_visible || m_annotation.length > 0)
+ && m_lookup_table.get_number_of_candidates() > 0) {
switch (keyval) {
case Gdk.Key.Down:
candidate_panel_cursor_down();
@@ -1673,12 +1826,10 @@ public class IBusEmojier : Gtk.ApplicationWindow {
case Gdk.Key.Page_Down:
enter_notify_disable_with_timer();
m_lookup_table.page_down();
- show_candidate_panel();
break;
case Gdk.Key.Page_Up:
enter_notify_disable_with_timer();
m_lookup_table.page_up();
- show_candidate_panel();
break;
}
} else {
@@ -1688,19 +1839,18 @@ public class IBusEmojier : Gtk.ApplicationWindow {
}
- private bool key_press_cursor_home_end(uint keyval,
- uint modifiers) {
+ public bool key_press_cursor_home_end(uint keyval,
+ uint modifiers) {
assert (keyval == Gdk.Key.Home || keyval == Gdk.Key.End);
uint ncandidates = m_lookup_table.get_number_of_candidates();
- if (m_candidate_panel_is_visible && ncandidates > 1) {
+ if (m_candidate_panel_mode && ncandidates > 0) {
enter_notify_disable_with_timer();
if (keyval == Gdk.Key.Home) {
m_lookup_table.set_cursor_pos(0);
} else if (keyval == Gdk.Key.End) {
m_lookup_table.set_cursor_pos(ncandidates - 1);
}
- show_candidate_panel();
return true;
}
if (m_entry.get_text().length > 0) {
@@ -1717,44 +1867,41 @@ public class IBusEmojier : Gtk.ApplicationWindow {
? true : false);
return true;
}
- if (!m_candidate_panel_is_visible)
- return category_list_cursor_move(keyval);
- return false;
+ return category_list_cursor_move(keyval);
}
- private bool key_press_escape() {
+ public bool key_press_escape() {
if (m_show_unicode) {
- if (m_candidate_panel_is_visible) {
- m_candidate_panel_is_visible = false;
- show_unicode_blocks();
- return true;
- } else {
+ if (!m_candidate_panel_is_visible) {
m_show_unicode = false;
m_category_active_index = -1;
- hide_candidate_panel();
- return true;
}
+ hide_candidate_panel();
+ return true;
} else if (m_backward_index >= 0 && m_backward != null) {
show_emoji_for_category(m_backward);
return true;
- } else if (m_candidate_panel_is_visible) {
- hide_candidate_panel();
- return true;
- } else if (m_entry.get_text().length == 0) {
- m_loop.quit();
+ } else if (m_candidate_panel_is_visible && m_backward != null) {
hide_candidate_panel();
return true;
}
- m_entry.delete_text(0, -1);
- return true;
+ hide();
+ if (m_candidate_panel_mode &&
+ m_lookup_table.get_number_of_candidates() > 0) {
+ // Call remove_all_children() instead of show_category_list()
+ // so that show_category_list do not remove children with
+ // PageUp/PageDown.
+ remove_all_children();
+ }
+ return false;
}
- private bool key_press_enter() {
+ public bool key_press_enter() {
if (m_candidate_panel_is_visible) {
uint index = m_lookup_table.get_cursor_pos();
- candidate_panel_select_index(index);
+ return has_variants(index);
} else if (m_category_active_index >= 0) {
Gtk.ListBoxRow gtkrow = m_list_box.get_selected_row();
EBoxRow row = gtkrow as EBoxRow;
@@ -1789,13 +1936,111 @@ public class IBusEmojier : Gtk.ApplicationWindow {
}
+ private Gdk.Rectangle get_monitor_geometry() {
+ Gdk.Rectangle monitor_area = { 0, };
+
+ // Use get_monitor_geometry() instead of get_monitor_area().
+ // get_monitor_area() excludes docks, but the lookup window should be
+ // shown over them.
+#if VALA_0_34
+ Gdk.Monitor monitor = Gdk.Display.get_default().get_monitor_at_point(
+ m_cursor_location.x,
+ m_cursor_location.y);
+ monitor_area = monitor.get_geometry();
+#else
+ Gdk.Screen screen = Gdk.Screen.get_default();
+ int monitor_num = screen.get_monitor_at_point(m_cursor_location.x,
+ m_cursor_location.y);
+ screen.get_monitor_geometry(monitor_num, out monitor_area);
+#endif
+ return monitor_area;
+ }
+
+
+ private void adjust_window_position() {
+ Gdk.Point cursor_right_bottom = {
+ m_cursor_location.x + m_cursor_location.width,
+ m_cursor_location.y + m_cursor_location.height
+ };
+
+ Gtk.Allocation allocation;
+ get_allocation(out allocation);
+ Gdk.Point window_right_bottom = {
+ cursor_right_bottom.x + allocation.width,
+ cursor_right_bottom.y + allocation.height
+ };
+
+ Gdk.Rectangle monitor_area = get_monitor_geometry();
+ int monitor_right = monitor_area.x + monitor_area.width;
+ int monitor_bottom = monitor_area.y + monitor_area.height;
+
+ int x, y;
+ if (window_right_bottom.x > monitor_right)
+ x = monitor_right - allocation.width;
+ else
+ x = cursor_right_bottom.x;
+ if (x < 0)
+ x = 0;
+
+ bool changed = false;
+ if (window_right_bottom.y > monitor_bottom) {
+ y = m_cursor_location.y - allocation.height;
+ // Do not up side down in Wayland
+ if (m_input_context_path == "") {
+ changed = (m_is_up_side_down == false);
+ m_is_up_side_down = true;
+ } else {
+ changed = (m_is_up_side_down == true);
+ m_is_up_side_down = false;
+ }
+ } else {
+ y = cursor_right_bottom.y;
+ changed = (m_is_up_side_down == true);
+ m_is_up_side_down = false;
+ }
+ if (y < 0)
+ y = 0;
+
+ move(x, y);
+ if (changed) {
+ if (m_redraw_window_id > 0)
+ GLib.Source.remove(m_redraw_window_id);
+ m_redraw_window_id = GLib.Timeout.add(100, () => {
+ m_redraw_window_id = 0;
+ this.show_all();
+ return false;
+ });
+ }
+ }
+
+
+#if 0
+ private void check_action_variant_cb(Gtk.MenuItem item) {
+ Gtk.CheckMenuItem check = item as Gtk.CheckMenuItem;
+ m_show_emoji_variant = check.get_active();
+ // Redraw emoji candidate panel for m_show_emoji_variant
+ if (m_candidate_panel_is_visible) {
+ // DOTO: queue_draw() does not effect at the real time.
+ this.queue_draw();
+ }
+ }
+#else
private void check_action_variant_cb(GLib.SimpleAction action,
GLib.Variant? parameter) {
m_show_emoji_variant = !action.get_state().get_boolean();
action.set_state(new GLib.Variant.boolean(m_show_emoji_variant));
// Redraw emoji candidate panel for m_show_emoji_variant
- if (m_candidate_panel_is_visible)
- show_candidate_panel();
+ if (m_candidate_panel_is_visible) {
+ // DOTO: queue_draw() does not effect at the real time.
+ this.queue_draw();
+ }
+ }
+#endif
+
+
+ private void action_close_cb(GLib.SimpleAction action,
+ GLib.Variant? parameter) {
+ candidate_clicked(0, BUTTON_CLOSE_BUTTON, 0);
}
@@ -1842,6 +2087,123 @@ public class IBusEmojier : Gtk.ApplicationWindow {
}
+ public void set_annotation(string annotation) {
+ m_annotation = annotation;
+ remove_all_children();
+ if (annotation.length > 0) {
+ update_candidate_window();
+ } else {
+ if (m_show_unicode)
+ update_unicode_blocks();
+ else
+ update_category_list();
+ }
+ }
+
+
+ public IBus.LookupTable get_one_dimension_lookup_table() {
+ var lookup_table = new IBus.LookupTable(EMOJI_GRID_PAGE, 0, true, true);
+ uint i = 0;
+ for (; i < m_lookup_table.get_number_of_candidates(); i++) {
+ IBus.Text text = new IBus.Text.from_string("");
+ text.copy(m_lookup_table.get_candidate(i));
+ lookup_table.append_candidate(text);
+ }
+ if (i > 0)
+ lookup_table.set_cursor_pos(m_lookup_table.get_cursor_pos());
+ return lookup_table;
+ }
+
+
+ public uint get_number_of_candidates() {
+ return m_lookup_table.get_number_of_candidates();
+ }
+
+
+ public uint get_cursor_pos() {
+ return m_lookup_table.get_cursor_pos();
+ }
+
+
+ public void set_cursor_pos(uint cursor_pos) {
+ m_lookup_table.set_cursor_pos(cursor_pos);
+ }
+
+
+ public string get_current_candidate() {
+ // If category_list mode, do not show the category name on preedit.
+ // If candidate_panel mode, the first space key does not show the
+ // lookup table but the first candidate is avaiable on preedit.
+ if (!m_candidate_panel_mode)
+ return "";
+ uint cursor = m_lookup_table.get_cursor_pos();
+ return m_lookup_table.get_candidate(cursor).text;
+ }
+
+
+ public IBus.Text get_title_text() {
+ var language = _(IBus.get_language_name(m_current_lang_id));
+ uint ncandidates = this.get_number_of_candidates();
+ string main_title = _("Emoji Choice");
+ if (m_show_unicode)
+ main_title = _("Unicode Choice");
+ var text = new IBus.Text.from_string(
+ "%s (%s) (%u / %u)".printf(
+ main_title,
+ language,
+ this.get_cursor_pos() + 1,
+ ncandidates));
+ int char_count = text.text.char_count();
+ int start_index = -1;
+ for (int i = 0; i < char_count; i++) {
+ if (text.text.utf8_offset(i).has_prefix(language)) {
+ start_index = i;
+ break;
+ }
+ }
+ if (start_index >= 0) {
+ var attr = new IBus.Attribute(
+ IBus.AttrType.FOREGROUND,
+ 0x808080,
+ start_index,
+ start_index + language.char_count());
+ var attrs = new IBus.AttrList();
+ attrs.append(attr);
+ text.set_attributes(attrs);
+ }
+ return text;
+ }
+
+
+#if 0
+ public GLib.SList<string>? get_candidates() {
+ if (m_annotation.length == 0) {
+ return null;
+ }
+ if (m_annotation.length > m_emoji_max_seq_len) {
+ return null;
+ }
+ string? unicode_point = check_unicode_point(m_annotation);
+ GLib.SList<string>? total_emojis =
+ lookup_emojis_from_annotation(m_annotation);
+ if (total_emojis == null) {
+ /* Users can type title strings against lower case.
+ * E.g. "Smile" against "smile"
+ * But the dictionary has the case sensitive annotations.
+ * E.g. ":D" and ":q"
+ * So need to call lookup_emojis_from_annotation() twice.
+ */
+ string lower_annotation = m_annotation.down();
+ total_emojis = lookup_emojis_from_annotation(lower_annotation);
+ }
+ if (unicode_point != null)
+ total_emojis.prepend(unicode_point);
+ return total_emojis;
+ }
+#endif
+
+
+#if 0
public string run(string input_context_path,
Gdk.Event event) {
assert (m_loop == null);
@@ -1915,12 +2277,34 @@ public class IBusEmojier : Gtk.ApplicationWindow {
return m_result;
}
+#endif
/* override virtual functions */
- public override void show() {
- base.show();
- set_focus_visible(true);
+ public override void show_all() {
+ base.show_all();
+ if (m_candidate_panel_mode)
+ show_candidate_panel();
+ else if (m_show_unicode)
+ show_unicode_blocks();
+ else
+ show_category_list();
+ }
+
+
+ public override void hide() {
+ base.hide();
+ m_candidate_panel_is_visible = false;
+ // m_candidate_panel_mode is not false in when you type something
+ // during enabling the candidate panel.
+ if (m_redraw_window_id > 0) {
+ GLib.Source.remove(m_redraw_window_id);
+ m_redraw_window_id = 0;
+ }
+ if (m_unicode_progress_id > 0) {
+ GLib.Source.remove(m_unicode_progress_id);
+ m_unicode_progress_id = 0;
+ }
}
@@ -1935,11 +2319,16 @@ public class IBusEmojier : Gtk.ApplicationWindow {
switch (keyval) {
case Gdk.Key.Escape:
if (key_press_escape())
- return true;
- break;
+ show_all();
+ return true;
case Gdk.Key.Return:
case Gdk.Key.KP_Enter:
- key_press_enter();
+ if (key_press_enter()) {
+ show_all();
+ } else {
+ m_result = get_current_candidate();
+ hide();
+ }
return true;
case Gdk.Key.BackSpace:
if (m_entry.get_text().length > 0) {
@@ -1977,42 +2366,49 @@ public class IBusEmojier : Gtk.ApplicationWindow {
}
else {
category_list_cursor_move(Gdk.Key.Down);
+ show_all();
}
return true;
case Gdk.Key.Right:
case Gdk.Key.KP_Right:
key_press_cursor_horizontal(Gdk.Key.Right, modifiers);
+ show_all();
return true;
case Gdk.Key.Left:
case Gdk.Key.KP_Left:
key_press_cursor_horizontal(Gdk.Key.Left, modifiers);
+ show_all();
return true;
case Gdk.Key.Down:
case Gdk.Key.KP_Down:
key_press_cursor_vertical(Gdk.Key.Down, modifiers);
+ show_all();
return true;
case Gdk.Key.Up:
case Gdk.Key.KP_Up:
key_press_cursor_vertical(Gdk.Key.Up, modifiers);
+ show_all();
return true;
case Gdk.Key.Page_Down:
case Gdk.Key.KP_Page_Down:
key_press_cursor_vertical(Gdk.Key.Page_Down, modifiers);
+ show_all();
return true;
case Gdk.Key.Page_Up:
case Gdk.Key.KP_Page_Up:
key_press_cursor_vertical(Gdk.Key.Page_Up, modifiers);
+ show_all();
return true;
case Gdk.Key.Home:
case Gdk.Key.KP_Home:
- if (key_press_cursor_home_end(Gdk.Key.Home, modifiers))
- return true;
- break;
+ key_press_cursor_home_end(Gdk.Key.Home, modifiers);
+ show_all();
+ return true;
case Gdk.Key.End:
case Gdk.Key.KP_End:
- if (key_press_cursor_home_end(Gdk.Key.End, modifiers))
- return true;
- break;
+ key_press_cursor_home_end(Gdk.Key.End, modifiers);
+ show_all();
+ return true;
case Gdk.Key.Insert:
case Gdk.Key.KP_Insert:
GLib.Signal.emit_by_name(m_entry, "toggle-overwrite");
@@ -2023,26 +2419,30 @@ public class IBusEmojier : Gtk.ApplicationWindow {
switch (keyval) {
case Gdk.Key.f:
key_press_cursor_horizontal(Gdk.Key.Right, modifiers);
+ show_all();
return true;
case Gdk.Key.b:
key_press_cursor_horizontal(Gdk.Key.Left, modifiers);
+ show_all();
return true;
case Gdk.Key.n:
case Gdk.Key.N:
key_press_cursor_vertical(Gdk.Key.Down, modifiers);
+ show_all();
return true;
case Gdk.Key.p:
case Gdk.Key.P:
key_press_cursor_vertical(Gdk.Key.Up, modifiers);
+ show_all();
return true;
case Gdk.Key.h:
- if (key_press_cursor_home_end(Gdk.Key.Home, modifiers))
- return true;
- break;
+ key_press_cursor_home_end(Gdk.Key.Home, modifiers);
+ show_all();
+ return true;
case Gdk.Key.e:
- if (key_press_cursor_home_end(Gdk.Key.End, modifiers))
- return true;
- break;
+ key_press_cursor_home_end(Gdk.Key.End, modifiers);
+ show_all();
+ return true;
case Gdk.Key.u:
if (m_entry.get_text().length > 0) {
GLib.Signal.emit_by_name(m_entry,
@@ -2103,14 +2503,41 @@ public class IBusEmojier : Gtk.ApplicationWindow {
}
+ public void set_input_context_path(string input_context_path) {
+ m_input_context_path = input_context_path;
+ if (input_context_path == "") {
+ m_warning_message = _("" +
+ "Failed to get the current text application. " +
+ "Please re-focus your application. E.g. Press Esc key " +
+ "several times to release the emoji typing mode, " +
+ "click your desktop and click your text application again."
+ );
+ } else {
+ m_warning_message = "";
+ }
+ }
+
+
public string get_selected_string() {
return m_result;
}
+ private void reset_window_mode() {
+ m_backward_index = -1;
+ m_backward = null;
+ m_candidate_panel_is_visible = false;
+ m_candidate_panel_mode = false;
+ // Do not clear m_lookup_table to work with space key later.
+ }
+
+
public void reset() {
+ reset_window_mode();
m_input_context_path = "";
m_result = null;
+ m_category_active_index = -1;
+ m_show_unicode = false;
}
@@ -2145,6 +2572,23 @@ public class IBusEmojier : Gtk.ApplicationWindow {
}
+ public void set_cursor_location(int x,
+ int y,
+ int width,
+ int height) {
+ Gdk.Rectangle location = Gdk.Rectangle(){
+ x = x, y = y, width = width, height = height };
+ if (m_cursor_location == location)
+ return;
+ m_cursor_location = location;
+ }
+
+
+ public bool is_candidate_panel_mode() {
+ return m_candidate_panel_mode;
+ }
+
+
public static bool has_loaded_emoji_dict() {
if (m_emoji_to_data_dict == null)
return false;
@@ -2165,6 +2609,10 @@ public class IBusEmojier : Gtk.ApplicationWindow {
}
+ public static string get_annotation_lang() {
+ return m_current_lang_id;
+ }
+
public static void set_emoji_font(string? emoji_font) {
return_if_fail(emoji_font != null && emoji_font != "");
Pango.FontDescription font_desc =
@@ -2182,18 +2630,21 @@ public class IBusEmojier : Gtk.ApplicationWindow {
m_has_partial_match = has_partial_match;
}
+
public static void set_partial_match_length(int length) {
if (length < 1)
return;
m_partial_match_length = length;
}
+
public static void set_partial_match_condition(int condition) {
if (condition < 0)
return;
m_partial_match_condition = condition;
}
+
public static void set_favorites(string[]? unowned_favorites,
string[]? unowned_favorite_annotations) {
m_favorites = {};
diff --git a/ui/gtk3/emojierapp.vala b/ui/gtk3/emojierapp.vala
index 9506a945..787d448f 100644
--- a/ui/gtk3/emojierapp.vala
+++ b/ui/gtk3/emojierapp.vala
@@ -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();
+ 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;
}
- 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."));
+ 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);
diff --git a/ui/gtk3/extension.vala b/ui/gtk3/extension.vala
index 7d6d76e7..c729fd7e 100644
--- a/ui/gtk3/extension.vala
+++ b/ui/gtk3/extension.vala
@@ -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);
}
diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala
index d9238c89..4c3b00ca 100644
--- a/ui/gtk3/panel.vala
+++ b/ui/gtk3/panel.vala
@@ -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
diff --git a/ui/gtk3/panelbinding.vala b/ui/gtk3/panelbinding.vala
index 581f721e..52b78c17 100644
--- a/ui/gtk3/panelbinding.vala
+++ b/ui/gtk3/panelbinding.vala
@@ -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;
}
- 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) {
+ if (m_emojier != null) {
+ m_application.remove_window(m_emojier);
m_emojier = null;
- return;
}
- this.emojier_focus_commit();
+ m_application = null;
}
- private void handle_emoji_typing(Gdk.Event event) {
- if (m_emojier != null && m_emojier.is_running()) {
- m_emojier.present_centralize(event);
+ 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;
+ }
+ }
+ 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;
+ }
+ 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;
}
- 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_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 (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);
+ if (!show_candidate) {
+ IBus.Text text = m_preedit.get_commit_text();
+ commit_text_update_favorites(text);
+ hide_emoji_lookup_table();
+ return;
+ }
+ show_preedit_and_candidate(show_candidate);
}
}