ee2c8d7
From 27788fb3bf62411c2b13d24bac45eb060483162f Mon Sep 17 00:00:00 2001
6cc8efa
From: Daiki Ueno <ueno@unixuser.org>
ee2c8d7
Date: Tue, 8 Feb 2011 18:27:00 +0900
6cc8efa
Subject: [PATCH] Support surrounding-text retrieval.
6cc8efa
6cc8efa
This change adds a new API function ibus_engine_get_surrounding_text().
6cc8efa
In the implementation, engines do not proactively requests surrounding-text to
6cc8efa
target applications via the GTK+ client (IBusIMContext) every time the
6cc8efa
function is called.  Instead, IBusIMContext retrieves surrounding-text when
6cc8efa
certain events occur in the input context (IBusInputContext) and maintains
6cc8efa
it within the input context.
6cc8efa
6cc8efa
This logic is similar to Qt's QInputContext.  The following events trigger
6cc8efa
surrounding-text retrieval:
6cc8efa
6cc8efa
- focus_in
6cc8efa
- commit_text
6cc8efa
- show_preedit_text
6cc8efa
- (just before) process_key_event
6cc8efa
6cc8efa
Also,
6cc8efa
- destroy
6cc8efa
resets the current surrounding-text.
6cc8efa
---
069aa90
 bus/engineproxy.c               |   43 ++++++++++++
cf977d1
 bus/engineproxy.h               |   11 +++
069aa90
 bus/inputcontext.c              |   36 ++++++++++
069aa90
 client/gtk2/ibusimcontext.c     |  138 ++++++++++++++++++++++++++++++++++-----
3b5789d
 configure.ac                    |   14 ++++
eb1661e
 ibus/engine.py                  |    6 ++
7232d5e
 ibus/interface/iengine.py       |    3 +
069aa90
 ibus/interface/iinputcontext.py |    5 ++
069aa90
 src/ibusengine.c                |  138 +++++++++++++++++++++++++++++++++++++++
f7291cc
 src/ibusengine.h                |   21 ++++++-
069aa90
 src/ibusenginedesc.c            |  132 +++++++++++++++++++++++++++++++++++++
fbc5579
 src/ibusenginedesc.h            |   11 +++
069aa90
 src/ibusinputcontext.c          |   91 +++++++++++++++++++++++++
069aa90
 src/ibusinputcontext.h          |   11 +++
7232d5e
 src/ibusmarshalers.list         |    1 +
069aa90
 15 files changed, 644 insertions(+), 17 deletions(-)
6cc8efa
6cc8efa
diff --git a/bus/engineproxy.c b/bus/engineproxy.c
069aa90
index f808727..b35933f 100644
6cc8efa
--- a/bus/engineproxy.c
6cc8efa
+++ b/bus/engineproxy.c
cf977d1
@@ -47,6 +47,10 @@ struct _BusEngineProxy {
cf977d1
     /* a key mapping for the engine that converts keycode into keysym. the mapping is used only when use_sys_layout is FALSE. */
3b5789d
     IBusKeymap     *keymap;
f0777a3
     /* private member */
f0777a3
+
f0777a3
+    /* surrounding text */
f0777a3
+    IBusText *surrounding_text;
f0777a3
+    guint     surrounding_cursor_pos;
f0777a3
 };
f0777a3
 
f0777a3
 struct _BusEngineProxyClass {
cf977d1
@@ -83,6 +87,8 @@ enum {
cf977d1
 
eb1661e
 static guint    engine_signals[LAST_SIGNAL] = { 0 };
eb1661e
 
eb1661e
+static IBusText *text_empty = NULL;
eb1661e
+
eb1661e
 /* functions prototype */
3b5789d
 static void     bus_engine_proxy_set_property   (BusEngineProxy      *engine,
3b5789d
                                                  guint                prop_id,
cf977d1
@@ -330,11 +336,16 @@ bus_engine_proxy_class_init (BusEngineProxyClass *class)
3b5789d
             G_TYPE_NONE,
eb1661e
             1,
eb1661e
             IBUS_TYPE_PROPERTY);
3b5789d
+
e36fdde
+    text_empty = ibus_text_new_from_static_string ("");
eb1661e
+    g_object_ref_sink (text_empty);
eb1661e
 }
eb1661e
 
eb1661e
 static void
f0777a3
 bus_engine_proxy_init (BusEngineProxy *engine)
f0777a3
 {
eb1661e
+    engine->surrounding_text = g_object_ref_sink (text_empty);
eb1661e
+    engine->surrounding_cursor_pos = 0;
eb1661e
 }
eb1661e
 
eb1661e
 static void
cf977d1
@@ -393,6 +404,11 @@ bus_engine_proxy_real_destroy (IBusProxy *proxy)
eb1661e
         engine->keymap = NULL;
eb1661e
     }
eb1661e
 
eb1661e
+    if (engine->surrounding_text) {
eb1661e
+        g_object_unref (engine->surrounding_text);
eb1661e
+        engine->surrounding_text = NULL;
eb1661e
+    }
eb1661e
+
3b5789d
     IBUS_PROXY_CLASS (bus_engine_proxy_parent_class)->destroy ((IBusProxy *)engine);
eb1661e
 }
eb1661e
 
069aa90
@@ -950,6 +966,33 @@ void bus_engine_proxy_property_hide (BusEngineProxy *engine,
f0777a3
                        NULL);
6cc8efa
 }
6cc8efa
 
e36fdde
+void bus_engine_proxy_set_surrounding_text (BusEngineProxy *engine,
e36fdde
+                                            IBusText       *text,
e36fdde
+                                            guint           cursor_pos)
6cc8efa
+{
6cc8efa
+    g_assert (BUS_IS_ENGINE_PROXY (engine));
6cc8efa
+    g_assert (text != NULL);
6cc8efa
+
eb1661e
+    if (!engine->surrounding_text ||
eb1661e
+        g_strcmp0 (text->text, engine->surrounding_text->text) != 0 ||
eb1661e
+        cursor_pos != engine->surrounding_cursor_pos) {
f0777a3
+        GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)text);
e36fdde
+        if (engine->surrounding_text)
eb1661e
+            g_object_unref (engine->surrounding_text);
eb1661e
+        engine->surrounding_text = (IBusText *) g_object_ref_sink (text);
eb1661e
+        engine->surrounding_cursor_pos = cursor_pos;
eb1661e
+
f0777a3
+        g_dbus_proxy_call ((GDBusProxy *)engine,
f0777a3
+                           "SetSurroundingText",
f0777a3
+                           g_variant_new ("(vu)", variant, cursor_pos),
f0777a3
+                           G_DBUS_CALL_FLAGS_NONE,
f0777a3
+                           -1,
f0777a3
+                           NULL,
f0777a3
+                           NULL,
f0777a3
+                           NULL);
eb1661e
+    }
6cc8efa
+}
6cc8efa
+
cf977d1
 /* a macro to generate a function to call a nullary D-Bus method. */
6cc8efa
 #define DEFINE_FUNCTION(Name, name)                         \
6cc8efa
     void                                                    \
6cc8efa
diff --git a/bus/engineproxy.h b/bus/engineproxy.h
069aa90
index 2a82fc6..0680917 100644
6cc8efa
--- a/bus/engineproxy.h
6cc8efa
+++ b/bus/engineproxy.h
069aa90
@@ -212,5 +212,16 @@ void             bus_engine_proxy_property_hide     (BusEngineProxy *engine,
cf977d1
  */
6cc8efa
 gboolean         bus_engine_proxy_is_enabled        (BusEngineProxy *engine);
cf977d1
 
cf977d1
+/**
cf977d1
+ * bus_engine_proxy_set_surrounding_text:
cf977d1
+ *
cf977d1
+ * Call "SetSurroundingText" method of an engine asynchronously.
cf977d1
+ */
6cc8efa
+void             bus_engine_proxy_set_surrounding_text
6cc8efa
+                                                    (BusEngineProxy *engine,
6cc8efa
+                                                     IBusText       *text,
6cc8efa
+                                                     guint           cursor_pos);
cf977d1
+
cf977d1
+
6cc8efa
 G_END_DECLS
6cc8efa
 #endif
6cc8efa
diff --git a/bus/inputcontext.c b/bus/inputcontext.c
069aa90
index 32d51e8..20f561c 100644
6cc8efa
--- a/bus/inputcontext.c
6cc8efa
+++ b/bus/inputcontext.c
cf977d1
@@ -247,6 +247,11 @@ static const gchar introspection_xml[] =
f0777a3
     "    <method name='GetEngine'>"
f0777a3
     "      <arg direction='out' type='v' name='desc' />"
f0777a3
     "    </method>"
f0777a3
+    "    <method name='SetSurroundingText'>"
f0777a3
+    "      <arg direction='in' type='v' name='text' />"
f0777a3
+    "      <arg direction='in' type='u' name='cursor_pos' />"
f0777a3
+    "    </method>"
f0777a3
+
f0777a3
     /* signals */
f0777a3
     "    <signal name='CommitText'>"
f0777a3
     "      <arg type='v' name='text' />"
069aa90
@@ -961,6 +966,32 @@ _ic_get_engine (BusInputContext       *context,
cf977d1
  * Handle a D-Bus method call whose destination and interface name are both "org.freedesktop.IBus.InputContext"
cf977d1
  */
f0777a3
 static void
f0777a3
+_ic_set_surrounding_text (BusInputContext       *context,
f0777a3
+                          GVariant              *parameters,
f0777a3
+                          GDBusMethodInvocation *invocation)
6cc8efa
+{
f0777a3
+    GVariant *variant = NULL;
6cc8efa
+    IBusText *text;
6cc8efa
+    guint cursor_pos = 0;
f0777a3
+
f0777a3
+    g_variant_get (parameters, "(vu)", &variant, &cursor_pos);
f0777a3
+    text = IBUS_TEXT (ibus_serializable_deserialize (variant));
f0777a3
+    g_variant_unref (variant);
6cc8efa
+
6cc8efa
+    if ((context->capabilities & IBUS_CAP_SURROUNDING_TEXT) &&
f0777a3
+         context->has_focus && context->enabled && context->engine) {
eb1661e
+        bus_engine_proxy_set_surrounding_text (context->engine,
eb1661e
+                                               text,
eb1661e
+                                               cursor_pos);
6cc8efa
+    }
6cc8efa
+
6cc8efa
+    if (g_object_is_floating (text))
6cc8efa
+        g_object_unref (text);
6cc8efa
+
f0777a3
+    g_dbus_method_invocation_return_value (invocation, NULL);
6cc8efa
+}
6cc8efa
+
f0777a3
+static void
f0777a3
 bus_input_context_service_method_call (IBusService            *service,
f0777a3
                                        GDBusConnection        *connection,
f0777a3
                                        const gchar            *sender,
069aa90
@@ -999,6 +1030,7 @@ bus_input_context_service_method_call (IBusService            *service,
f0777a3
         { "IsEnabled",         _ic_is_enabled },
f0777a3
         { "SetEngine",         _ic_set_engine },
f0777a3
         { "GetEngine",         _ic_get_engine },
f0777a3
+        { "SetSurroundingText", _ic_set_surrounding_text},
6cc8efa
     };
6cc8efa
 
f0777a3
     gint i;
069aa90
@@ -2059,6 +2091,10 @@ bus_input_context_set_engine (BusInputContext *context,
fbc5579
             bus_engine_proxy_set_cursor_location (context->engine, context->x, context->y, context->w, context->h);
fbc5579
         }
fbc5579
     }
fbc5579
+    bus_input_context_emit_signal (context,
fbc5579
+                                   "EngineChanged",
fbc5579
+                                   NULL,
fbc5579
+                                   NULL);
fbc5579
     g_signal_emit (context,
fbc5579
                    context_signals[ENGINE_CHANGED],
fbc5579
                    0);
6cc8efa
diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c
ee2c8d7
index a4e46cb..d029b5c 100644
6cc8efa
--- a/client/gtk2/ibusimcontext.c
6cc8efa
+++ b/client/gtk2/ibusimcontext.c
069aa90
@@ -27,6 +27,7 @@
069aa90
 #include <gtk/gtk.h>
069aa90
 #include <gdk/gdkkeysyms.h>
069aa90
 #include <ibus.h>
069aa90
+#include <string.h>
069aa90
 #include "ibusimcontext.h"
069aa90
 
069aa90
 #if !GTK_CHECK_VERSION (2, 91, 0)
069aa90
@@ -60,7 +61,8 @@ struct _IBusIMContext {
fbc5579
     gboolean         has_focus;
fbc5579
 
069aa90
     guint32          time;
fbc5579
-    gint             caps;
fbc5579
+    guint            supported_caps;
fbc5579
+    guint            caps;
fbc5579
 };
fbc5579
 
fbc5579
 struct _IBusIMContextClass {
ee2c8d7
@@ -111,11 +113,18 @@ static void     ibus_im_context_set_cursor_location
6cc8efa
 static void     ibus_im_context_set_use_preedit
6cc8efa
                                             (GtkIMContext           *context,
6cc8efa
                                              gboolean               use_preedit);
6cc8efa
+static void     ibus_im_context_set_surrounding
6cc8efa
+                                            (GtkIMContext  *slave,
6cc8efa
+                                             const gchar   *text,
6cc8efa
+                                             gint           len,
6cc8efa
+                                             gint           cursor_index);
6cc8efa
+
6cc8efa
 
6cc8efa
 /* static methods*/
6cc8efa
 static void     _create_input_context       (IBusIMContext      *context);
fbc5579
 static void     _set_cursor_location_internal
fbc5579
                                             (GtkIMContext       *context);
fbc5579
+static void     _negotiate_capabilities     (IBusIMContext      *context);
fbc5579
 
fbc5579
 static void     _bus_connected_cb           (IBusBus            *bus,
fbc5579
                                              IBusIMContext      *context);
ee2c8d7
@@ -129,14 +138,15 @@ static void     _slave_preedit_start_cb     (GtkIMContext       *slave,
6cc8efa
                                              IBusIMContext       *context);
6cc8efa
 static void     _slave_preedit_end_cb       (GtkIMContext       *slave,
6cc8efa
                                              IBusIMContext       *context);
6cc8efa
-static void     _slave_retrieve_surrounding_cb
6cc8efa
+static gboolean _slave_retrieve_surrounding_cb
6cc8efa
                                             (GtkIMContext       *slave,
f7291cc
-                                             IBusIMContext       *context);
6cc8efa
-static void     _slave_delete_surrounding_cb
f7291cc
+                                             IBusIMContext      *context);
6cc8efa
+static gboolean _slave_delete_surrounding_cb
6cc8efa
                                             (GtkIMContext       *slave,
f7291cc
-                                             gint               offset_from_cursor,
f7291cc
-                                             guint              nchars,
f7291cc
-                                             IBusIMContext       *context);
f7291cc
+                                             gint                offset_from_cursor,
f7291cc
+                                             guint               nchars,
f7291cc
+                                             IBusIMContext      *context);
eb1661e
+static void     _request_surrounding_text   (IBusIMContext      *context);
069aa90
 static void     _create_fake_input_context  (void);
069aa90
 
eb1661e
 
ee2c8d7
@@ -248,6 +258,17 @@ _process_key_event_done (GObject      *object,
eb1661e
 }
eb1661e
 
0610dd6
 
eb1661e
+static void
eb1661e
+_request_surrounding_text (IBusIMContext *context)
eb1661e
+{
f7291cc
+    if (context->enable &&
f7291cc
+        (context->caps & IBUS_CAP_SURROUNDING_TEXT)) {
6cc8efa
+        gboolean return_value;
eb1661e
+        g_signal_emit (context, _signal_retrieve_surrounding_id, 0,
6cc8efa
+                       &return_value);
6cc8efa
+    }
eb1661e
+}
eb1661e
+
eb1661e
 static gint
eb1661e
 _key_snooper_cb (GtkWidget   *widget,
eb1661e
                  GdkEventKey *event,
ee2c8d7
@@ -334,6 +355,8 @@ _key_snooper_cb (GtkWidget   *widget,
0610dd6
 
0610dd6
     } while (0);
eb1661e
 
eb1661e
+    _request_surrounding_text (ibusimcontext);
6cc8efa
+
0610dd6
     if (ibusimcontext != NULL) {
0610dd6
         ibusimcontext->time = event->time;
0610dd6
     }
ee2c8d7
@@ -430,6 +453,7 @@ ibus_im_context_class_init (IBusIMContextClass *class)
6cc8efa
     im_context_class->set_client_window = ibus_im_context_set_client_window;
6cc8efa
     im_context_class->set_cursor_location = ibus_im_context_set_cursor_location;
6cc8efa
     im_context_class->set_use_preedit = ibus_im_context_set_use_preedit;
6cc8efa
+    im_context_class->set_surrounding = ibus_im_context_set_surrounding;
6cc8efa
     gobject_class->finalize = ibus_im_context_finalize;
6cc8efa
 
6cc8efa
     _signal_commit_id =
ee2c8d7
@@ -577,7 +601,12 @@ ibus_im_context_init (GObject *obj)
f7291cc
     ibusimcontext->ibuscontext = NULL;
f7291cc
     ibusimcontext->has_focus = FALSE;
069aa90
     ibusimcontext->time = GDK_CURRENT_TIME;
fbc5579
-    ibusimcontext->caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS | IBUS_CAP_SURROUNDING_TEXT;
f7291cc
+#ifdef ENABLE_SURROUNDING
fbc5579
+    ibusimcontext->supported_caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS | IBUS_CAP_SURROUNDING_TEXT;
f7291cc
+#else
fbc5579
+    ibusimcontext->supported_caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS;
f7291cc
+#endif
fbc5579
+    ibusimcontext->caps = ibusimcontext->supported_caps;
f7291cc
 
f7291cc
 
f7291cc
     // Create slave im context
ee2c8d7
@@ -674,6 +703,8 @@ ibus_im_context_filter_keypress (GtkIMContext *context,
0610dd6
         if (ibusimcontext->client_window == NULL && event->window != NULL)
0610dd6
             gtk_im_context_set_client_window ((GtkIMContext *)ibusimcontext, event->window);
6cc8efa
 
eb1661e
+        _request_surrounding_text (ibusimcontext);
6cc8efa
+
0610dd6
         if (ibusimcontext != NULL) {
0610dd6
             ibusimcontext->time = event->time;
0610dd6
         }
ee2c8d7
@@ -768,6 +799,8 @@ ibus_im_context_focus_in (GtkIMContext *context)
cf977d1
                                    (gpointer *) &_focus_im_context);
6cc8efa
         _focus_im_context = context;
6cc8efa
     }
6cc8efa
+
eb1661e
+    _request_surrounding_text (ibusimcontext);
6cc8efa
 }
6cc8efa
 
6cc8efa
 static void
ee2c8d7
@@ -925,6 +958,25 @@ ibus_im_context_set_cursor_location (GtkIMContext *context, GdkRectangle *area)
fbc5579
 }
fbc5579
 
fbc5579
 static void
fbc5579
+_negotiate_capabilities (IBusIMContext *context)
fbc5579
+{
fbc5579
+    IBusEngineDesc *engine;
fbc5579
+
fbc5579
+    engine = ibus_input_context_get_engine (context->ibuscontext);
fbc5579
+    if (engine) {
fbc5579
+        context->caps = context->supported_caps &
fbc5579
+            ibus_engine_desc_get_requires (engine);
fbc5579
+        ibus_input_context_set_capabilities (context->ibuscontext,
fbc5579
+                                             context->caps);
fbc5579
+        IDEBUG ("engine %s: supported caps = %u, engine wants = %u, caps = %u",
fbc5579
+                ibus_engine_desc_get_name (engine)
fbc5579
+                context->supported_caps,
fbc5579
+                ibus_engine_desc_get_requires (engine),
fbc5579
+                context->caps);
fbc5579
+    }
fbc5579
+}
fbc5579
+
fbc5579
+static void
fbc5579
 ibus_im_context_set_use_preedit (GtkIMContext *context, gboolean use_preedit)
fbc5579
 {
fbc5579
     IDEBUG ("%s", __FUNCTION__);
ee2c8d7
@@ -933,17 +985,50 @@ ibus_im_context_set_use_preedit (GtkIMContext *context, gboolean use_preedit)
fbc5579
 
fbc5579
     if(ibusimcontext->ibuscontext) {
fbc5579
         if (use_preedit) {
fbc5579
-            ibusimcontext->caps |= IBUS_CAP_PREEDIT_TEXT;
fbc5579
+            ibusimcontext->supported_caps |= IBUS_CAP_PREEDIT_TEXT;
fbc5579
         }
fbc5579
         else {
fbc5579
-            ibusimcontext->caps &= ~IBUS_CAP_PREEDIT_TEXT;
fbc5579
+            ibusimcontext->supported_caps &= ~IBUS_CAP_PREEDIT_TEXT;
fbc5579
         }
fbc5579
-        ibus_input_context_set_capabilities (ibusimcontext->ibuscontext, ibusimcontext->caps);
fbc5579
+        _negotiate_capabilities (ibusimcontext);
fbc5579
     }
fbc5579
     gtk_im_context_set_use_preedit (ibusimcontext->slave, use_preedit);
6cc8efa
 }
6cc8efa
 
6cc8efa
 static void
6cc8efa
+ibus_im_context_set_surrounding (GtkIMContext  *context,
6cc8efa
+                                 const gchar   *text,
6cc8efa
+                                 gint           len,
6cc8efa
+                                 gint           cursor_index)
6cc8efa
+{
6cc8efa
+    g_return_if_fail (context != NULL);
6cc8efa
+    g_return_if_fail (IBUS_IS_IM_CONTEXT (context));
eb1661e
+    g_return_if_fail (text != NULL);
eb1661e
+    g_return_if_fail (strlen (text) >= len);
eb1661e
+    g_return_if_fail (0 <= cursor_index && cursor_index <= len);
6cc8efa
+
e36fdde
+    IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (context);
6cc8efa
+
6cc8efa
+    if (ibusimcontext->enable && ibusimcontext->ibuscontext) {
6cc8efa
+        IBusText *ibustext;
6cc8efa
+        guint cursor_pos;
eb1661e
+        gchar *p;
6cc8efa
+
eb1661e
+        p = g_strndup (text, len);
eb1661e
+        cursor_pos = g_utf8_strlen (p, cursor_index);
eb1661e
+        ibustext = ibus_text_new_from_string (p);
eb1661e
+        g_free (p);
6cc8efa
+        ibus_input_context_set_surrounding_text (ibusimcontext->ibuscontext,
6cc8efa
+                                                 ibustext,
6cc8efa
+                                                 cursor_pos);
6cc8efa
+    }
6cc8efa
+    gtk_im_context_set_surrounding (ibusimcontext->slave,
6cc8efa
+                                    text,
6cc8efa
+                                    len,
6cc8efa
+                                    cursor_index);
6cc8efa
+}
6cc8efa
+
6cc8efa
+static void
6cc8efa
 _bus_connected_cb (IBusBus          *bus,
6cc8efa
                    IBusIMContext    *ibusimcontext)
6cc8efa
 {
ee2c8d7
@@ -962,6 +1047,8 @@ _ibus_context_commit_text_cb (IBusInputContext *ibuscontext,
6cc8efa
     IDEBUG ("%s", __FUNCTION__);
6cc8efa
 
6cc8efa
     g_signal_emit (ibusimcontext, _signal_commit_id, 0, text->text);
6cc8efa
+
eb1661e
+    _request_surrounding_text (ibusimcontext);
6cc8efa
 }
6cc8efa
 
6cc8efa
 static gboolean
ee2c8d7
@@ -1256,6 +1343,8 @@ _ibus_context_show_preedit_text_cb (IBusInputContext   *ibuscontext,
6cc8efa
     ibusimcontext->preedit_visible = TRUE;
6cc8efa
     g_signal_emit (ibusimcontext, _signal_preedit_start_id, 0);
6cc8efa
     g_signal_emit (ibusimcontext, _signal_preedit_changed_id, 0);
6cc8efa
+
eb1661e
+    _request_surrounding_text (ibusimcontext);
6cc8efa
 }
6cc8efa
 
6cc8efa
 static void
ee2c8d7
@@ -1321,6 +1410,14 @@ _ibus_context_destroy_cb (IBusInputContext *ibuscontext,
fbc5579
 }
fbc5579
 
fbc5579
 static void
fbc5579
+_ibus_context_engine_changed_cb (IBusInputContext *ibuscontext,
fbc5579
+                                 IBusIMContext    *ibusimcontext)
fbc5579
+{
fbc5579
+    IDEBUG ("%s", __FUNCTION__);
fbc5579
+    _negotiate_capabilities (ibusimcontext);
fbc5579
+}
fbc5579
+
fbc5579
+static void
fbc5579
 _create_input_context (IBusIMContext *ibusimcontext)
fbc5579
 {
fbc5579
     IDEBUG ("%s", __FUNCTION__);
ee2c8d7
@@ -1363,6 +1460,10 @@ _create_input_context (IBusIMContext *ibusimcontext)
fbc5579
                       "disabled",
fbc5579
                       G_CALLBACK (_ibus_context_disabled_cb),
fbc5579
                       ibusimcontext);
fbc5579
+    g_signal_connect (ibusimcontext->ibuscontext,
fbc5579
+                      "engine-changed",
fbc5579
+                      G_CALLBACK (_ibus_context_engine_changed_cb),
fbc5579
+                      ibusimcontext);
fbc5579
     g_signal_connect (ibusimcontext->ibuscontext, "destroy",
fbc5579
                       G_CALLBACK (_ibus_context_destroy_cb),
fbc5579
                       ibusimcontext);
ee2c8d7
@@ -1419,17 +1520,21 @@ _slave_preedit_end_cb (GtkIMContext  *slave,
6cc8efa
     g_signal_emit (ibusimcontext, _signal_preedit_end_id, 0);
6cc8efa
 }
6cc8efa
 
6cc8efa
-static void
6cc8efa
+static gboolean
6cc8efa
 _slave_retrieve_surrounding_cb (GtkIMContext  *slave,
6cc8efa
                                 IBusIMContext *ibusimcontext)
6cc8efa
 {
6cc8efa
+    gboolean return_value;
6cc8efa
+
6cc8efa
     if (ibusimcontext->enable && ibusimcontext->ibuscontext) {
6cc8efa
-        return;
6cc8efa
+        return FALSE;
6cc8efa
     }
6cc8efa
-    g_signal_emit (ibusimcontext, _signal_retrieve_surrounding_id, 0);
6cc8efa
+    g_signal_emit (ibusimcontext, _signal_retrieve_surrounding_id, 0,
6cc8efa
+                   &return_value);
6cc8efa
+    return return_value;
6cc8efa
 }
6cc8efa
 
6cc8efa
-static void
6cc8efa
+static gboolean
6cc8efa
 _slave_delete_surrounding_cb (GtkIMContext  *slave,
6cc8efa
                               gint           offset_from_cursor,
6cc8efa
                               guint          nchars,
ee2c8d7
@@ -1438,9 +1543,10 @@ _slave_delete_surrounding_cb (GtkIMContext  *slave,
6cc8efa
     gboolean return_value;
6cc8efa
 
6cc8efa
     if (ibusimcontext->enable && ibusimcontext->ibuscontext) {
6cc8efa
-        return;
6cc8efa
+        return FALSE;
6cc8efa
     }
6cc8efa
     g_signal_emit (ibusimcontext, _signal_delete_surrounding_id, 0, offset_from_cursor, nchars, &return_value);
6cc8efa
+    return return_value;
6cc8efa
 }
6cc8efa
 
069aa90
 #ifdef OS_CHROMEOS
f7291cc
diff --git a/configure.ac b/configure.ac
ee2c8d7
index 81efe81..e5fe925 100644
f7291cc
--- a/configure.ac
f7291cc
+++ b/configure.ac
ee2c8d7
@@ -364,6 +364,19 @@ AC_ARG_WITH(sync-mode-apps,
ee2c8d7
 AC_DEFINE_UNQUOTED(SYNC_MODE_APPS, "$SYNC_MODE_APPS",
ee2c8d7
     [Enbale sync mode in those applications])
f7291cc
 
f7291cc
+# option for enable surrounding-text
f7291cc
+AC_ARG_ENABLE(surrounding-text,
f7291cc
+    AS_HELP_STRING([--enable-surrounding-text],
f7291cc
+        [Enable surrounding-text support]),
f7291cc
+    [enable_surrounding_text=$enableval],
f7291cc
+    [enable_surrounding_text=no]
f7291cc
+)
f7291cc
+if test x"$enable_surrounding_text" = x"yes"; then
f7291cc
+    AC_DEFINE(ENABLE_SURROUNDING, TRUE, [Enable surrounding-text support])
f7291cc
+else
f7291cc
+    enable_surrounding_text="no (disabled, use --enable-surrounding-text to enable)"
f7291cc
+fi
f7291cc
+
f7291cc
 # check iso-codes
f7291cc
 PKG_CHECK_MODULES(ISOCODES, [
f7291cc
     iso-codes
ee2c8d7
@@ -435,5 +448,6 @@ Build options:
f7291cc
   Enable key snooper        $enable_key_snooper
ee2c8d7
   Sync mode regexes         "$SYNC_MODE_APPS"
f7291cc
   No snooper regexes        "$NO_SNOOPER_APPS"
f7291cc
+  Enable surrounding-text   $enable_surrounding_text
f7291cc
 ])
f7291cc
 
7232d5e
diff --git a/ibus/engine.py b/ibus/engine.py
7232d5e
index b1df2fe..ec42fa4 100644
7232d5e
--- a/ibus/engine.py
7232d5e
+++ b/ibus/engine.py
7232d5e
@@ -46,6 +46,9 @@ class EngineBase(object.Object):
7232d5e
     def set_cursor_location(self, x, y, w, h):
7232d5e
         pass
7232d5e
 
7232d5e
+    def set_surrounding_text(self, text, cursor_index):
7232d5e
+        pass
7232d5e
+
7232d5e
     def set_capabilities(self, cap):
7232d5e
         pass
7232d5e
 
7232d5e
@@ -163,6 +166,9 @@ class EngineProxy(interface.IEngine):
7232d5e
     def SetCursorLocation(self, x, y, w, h):
7232d5e
         return self.__engine.set_cursor_location(x, y, w, h)
7232d5e
 
7232d5e
+    def SetSurroundingText(self, text, cursor_index):
7232d5e
+        return self.__engine.set_surrounding_text(text, cursor_index)
7232d5e
+
7232d5e
     def SetCapabilities(self, caps):
7232d5e
         return self.__engine.set_capabilities(caps)
7232d5e
 
7232d5e
diff --git a/ibus/interface/iengine.py b/ibus/interface/iengine.py
f0777a3
index 2386c0f..5db2012 100644
7232d5e
--- a/ibus/interface/iengine.py
7232d5e
+++ b/ibus/interface/iengine.py
7232d5e
@@ -50,6 +50,9 @@ class IEngine(dbus.service.Object):
7232d5e
     @method(in_signature="iiii")
7232d5e
     def SetCursorLocation(self, x, y, w, h): pass
7232d5e
 
7232d5e
+    @method(in_signature="vu")
7232d5e
+    def SetSurroundingText(self, text, cursor_index): pass
7232d5e
+
7232d5e
     @method(in_signature="u")
7232d5e
     def SetCapabilities(self, cap): pass
7232d5e
 
7232d5e
diff --git a/ibus/interface/iinputcontext.py b/ibus/interface/iinputcontext.py
069aa90
index 89f6dbd..bb25c5a 100644
7232d5e
--- a/ibus/interface/iinputcontext.py
7232d5e
+++ b/ibus/interface/iinputcontext.py
069aa90
@@ -49,6 +49,9 @@ class IInputContext(dbus.service.Object):
7232d5e
     @method(in_signature="iiii")
7232d5e
     def SetCursorLocation(self, x, y, w, h): pass
7232d5e
 
7232d5e
+    @method(in_signature="vu")
7232d5e
+    def SetSurroundingText(self, text, cursor_index): pass
7232d5e
+
7232d5e
     @method()
7232d5e
     def FocusIn(self): pass
7232d5e
 
069aa90
@@ -137,4 +140,6 @@ class IInputContext(dbus.service.Object):
fbc5579
     @signal(signature="v")
fbc5579
     def UpdateProperty(self, prop): pass
fbc5579
 
fbc5579
+    @signal()
fbc5579
+    def EngineChanged(self): pass
fbc5579
 
6cc8efa
diff --git a/src/ibusengine.c b/src/ibusengine.c
0610dd6
index 519d7ca..fc129a2 100644
6cc8efa
--- a/src/ibusengine.c
6cc8efa
+++ b/src/ibusengine.c
eb1661e
@@ -45,6 +45,7 @@ enum {
6cc8efa
     PROPERTY_SHOW,
6cc8efa
     PROPERTY_HIDE,
6cc8efa
     CANDIDATE_CLICKED,
6cc8efa
+    SET_SURROUNDING_TEXT,
6cc8efa
     LAST_SIGNAL,
6cc8efa
 };
6cc8efa
 
f0777a3
@@ -58,10 +59,15 @@ enum {
6cc8efa
 struct _IBusEnginePrivate {
f0777a3
     gchar *engine_name;
f0777a3
     GDBusConnection *connection;
6cc8efa
+
6cc8efa
+    IBusText *surrounding_text;
6cc8efa
+    guint surrounding_cursor_pos;
6cc8efa
 };
6cc8efa
 
6cc8efa
 static guint            engine_signals[LAST_SIGNAL] = { 0 };
6cc8efa
 
6cc8efa
+static IBusText *text_empty = NULL;
6cc8efa
+
6cc8efa
 /* functions prototype */
f0777a3
 static void      ibus_engine_destroy         (IBusEngine         *engine);
f0777a3
 static void      ibus_engine_set_property    (IBusEngine         *engine,
f0777a3
@@ -135,6 +141,10 @@ static void      ibus_engine_property_show   (IBusEngine         *engine,
f0777a3
                                               const gchar        *prop_name);
f0777a3
 static void      ibus_engine_property_hide   (IBusEngine         *engine,
f0777a3
                                               const gchar        *prop_name);
f0777a3
+static void      ibus_engine_set_surrounding_text
6cc8efa
+                                            (IBusEngine         *engine,
6cc8efa
+                                             IBusText           *text,
6cc8efa
+                                             guint               cursor_pos);
f0777a3
 static void      ibus_engine_emit_signal     (IBusEngine         *engine,
f0777a3
                                               const gchar        *signal_name,
f0777a3
                                               GVariant           *parameters);
0610dd6
@@ -185,6 +195,10 @@ static const gchar introspection_xml[] =
fc455bb
     "    <method name='PageDown' />"
fc455bb
     "    <method name='CursorUp' />"
fc455bb
     "    <method name='CursorDown' />"
fc455bb
+    "    <method name='SetSurroundingText'>"
fc455bb
+    "      <arg direction='in'  type='v' name='text' />"
fc455bb
+    "      <arg direction='in'  type='u' name='cursor_pos' />"
fc455bb
+    "    </method>"
fc455bb
     /* FIXME signals */
fc455bb
     "    <signal name='CommitText'>"
fc455bb
     "      <arg type='v' name='text' />"
0610dd6
@@ -250,6 +264,7 @@ ibus_engine_class_init (IBusEngineClass *class)
f0777a3
     class->property_hide        = ibus_engine_property_hide;
f0777a3
     class->set_cursor_location  = ibus_engine_set_cursor_location;
f0777a3
     class->set_capabilities     = ibus_engine_set_capabilities;
f0777a3
+    class->set_surrounding_text = ibus_engine_set_surrounding_text;
6cc8efa
 
6cc8efa
     /* install properties */
f0777a3
     /**
0610dd6
@@ -616,12 +631,39 @@ ibus_engine_class_init (IBusEngineClass *class)
6cc8efa
             G_TYPE_STRING);
6cc8efa
 
f0777a3
     g_type_class_add_private (class, sizeof (IBusEnginePrivate));
f0777a3
+
6cc8efa
+    /**
6cc8efa
+     * IBusEngine::set-surrounding-text:
6cc8efa
+     * @engine: An IBusEngine.
6cc8efa
+     *
6cc8efa
+     * Emitted when a surrounding text is set.
6cc8efa
+     * Implement the member function set_surrounding_text() in extended class to receive this signal.
6cc8efa
+     *
6cc8efa
+     * <note><para>Argument @user_data is ignored in this function.</para></note>
6cc8efa
+     */
6cc8efa
+    engine_signals[SET_SURROUNDING_TEXT] =
6cc8efa
+        g_signal_new (I_("set-surrounding-text"),
6cc8efa
+            G_TYPE_FROM_CLASS (gobject_class),
6cc8efa
+            G_SIGNAL_RUN_LAST,
6cc8efa
+            G_STRUCT_OFFSET (IBusEngineClass, set_surrounding_text),
6cc8efa
+            NULL, NULL,
f0777a3
+            _ibus_marshal_VOID__OBJECT_UINT,
6cc8efa
+            G_TYPE_NONE,
6cc8efa
+            2,
eb1661e
+            G_TYPE_OBJECT,
6cc8efa
+            G_TYPE_UINT);
6cc8efa
+
e36fdde
+    text_empty = ibus_text_new_from_static_string ("");
eb1661e
+    g_object_ref_sink (text_empty);
6cc8efa
 }
6cc8efa
 
6cc8efa
 static void
f0777a3
 ibus_engine_init (IBusEngine *engine)
f0777a3
 {
f0777a3
     engine->priv = IBUS_ENGINE_GET_PRIVATE (engine);
6cc8efa
+
f0777a3
+    engine->priv->surrounding_text = g_object_ref_sink (text_empty);
f0777a3
+    engine->priv->surrounding_cursor_pos = 0;
6cc8efa
 }
6cc8efa
 
6cc8efa
 static void
0610dd6
@@ -630,6 +672,11 @@ ibus_engine_destroy (IBusEngine *engine)
f0777a3
     g_free (engine->priv->engine_name);
f0777a3
     engine->priv->engine_name = NULL;
6cc8efa
 
f0777a3
+    if (engine->priv->surrounding_text) {
f0777a3
+        g_object_unref (engine->priv->surrounding_text);
f0777a3
+        engine->priv->surrounding_text = NULL;
6cc8efa
+    }
6cc8efa
+
6cc8efa
     IBUS_OBJECT_CLASS(ibus_engine_parent_class)->destroy (IBUS_OBJECT (engine));
6cc8efa
 }
6cc8efa
 
0610dd6
@@ -801,6 +848,25 @@ ibus_engine_service_method_call (IBusService           *service,
f0777a3
         return;
f0777a3
     }
f0777a3
 
f0777a3
+    if (g_strcmp0 (method_name, "SetSurroundingText") == 0) {
f0777a3
+        GVariant *variant = NULL;
f0777a3
+        IBusText *text;
f0777a3
+        guint cursor_pos;
f0777a3
+
f0777a3
+        g_variant_get (parameters, "(vu)", &variant, &cursor_pos);
f0777a3
+        text = IBUS_TEXT (ibus_serializable_deserialize (variant));
f0777a3
+        g_variant_unref (variant);
f0777a3
+
f0777a3
+        g_signal_emit (engine, engine_signals[SET_SURROUNDING_TEXT], 0,
f0777a3
+                       text,
f0777a3
+                       cursor_pos);
f0777a3
+        if (g_object_is_floating (text)) {
f0777a3
+            g_object_unref (text);
6cc8efa
+        }
f0777a3
+        g_dbus_method_invocation_return_value (invocation, NULL);
f0777a3
+        return;
f0777a3
+    }
f0777a3
+
f0777a3
     /* should not be reached */
f0777a3
     g_return_if_reached ();
f0777a3
 }
0610dd6
@@ -955,6 +1021,26 @@ ibus_engine_property_hide (IBusEngine *engine, const gchar *prop_name)
6cc8efa
 }
6cc8efa
 
6cc8efa
 static void
6cc8efa
+ibus_engine_set_surrounding_text (IBusEngine *engine,
6cc8efa
+                                  IBusText   *text,
6cc8efa
+                                  guint       cursor_pos)
6cc8efa
+{
e36fdde
+    g_assert (IBUS_IS_ENGINE (engine));
e36fdde
+
e36fdde
+    IBusEnginePrivate *priv;
e36fdde
+
e36fdde
+    priv = IBUS_ENGINE_GET_PRIVATE (engine);
e36fdde
+
e36fdde
+    if (priv->surrounding_text) {
e36fdde
+        g_object_unref (priv->surrounding_text);
e36fdde
+    }
e36fdde
+
e36fdde
+    priv->surrounding_text = (IBusText *) g_object_ref_sink (text ? text : text_empty);
e36fdde
+    priv->surrounding_cursor_pos = cursor_pos;
6cc8efa
+    // g_debug ("set-surrounding-text ('%s', %d)", text->text, cursor_pos);
6cc8efa
+}
6cc8efa
+
6cc8efa
+static void
f0777a3
 ibus_engine_emit_signal (IBusEngine  *engine,
f0777a3
                          const gchar *signal_name,
f0777a3
                          GVariant    *parameters)
0610dd6
@@ -1138,14 +1224,66 @@ void ibus_engine_delete_surrounding_text (IBusEngine      *engine,
e36fdde
                                           gint             offset_from_cursor,
e36fdde
                                           guint            nchars)
eb1661e
 {
e36fdde
+    IBusEnginePrivate *priv;
e36fdde
+
f0777a3
     g_return_if_fail (IBUS_IS_ENGINE (engine));
f0777a3
 
eb1661e
+    priv = IBUS_ENGINE_GET_PRIVATE (engine);
eb1661e
+
f7291cc
+    /* Update surrounding-text cache.  This is necessary since some
f7291cc
+       engines call ibus_engine_get_surrounding_text() immediately
f7291cc
+       after ibus_engine_delete_surrounding_text(). */
eb1661e
+    if (priv->surrounding_text) {
f7291cc
+        IBusText *text;
f7291cc
+        glong cursor_pos, len;
f7291cc
+
f7291cc
+        cursor_pos = priv->surrounding_cursor_pos + offset_from_cursor;
f7291cc
+        len = ibus_text_get_length (priv->surrounding_text);
f7291cc
+        if (cursor_pos >= 0 && len - cursor_pos >= nchars) {
f7291cc
+            gunichar *ucs;
f7291cc
+
f7291cc
+            ucs = g_utf8_to_ucs4_fast (priv->surrounding_text->text,
f7291cc
+                                       -1,
f7291cc
+                                       NULL);
f7291cc
+            memmove (&ucs[cursor_pos],
f7291cc
+                     &ucs[cursor_pos + nchars],
f7291cc
+                     sizeof(gunichar) * (len - cursor_pos - nchars));
f7291cc
+            ucs[len - nchars] = 0;
f7291cc
+            text = ibus_text_new_from_ucs4 (ucs);
f7291cc
+            g_free (ucs);
f7291cc
+            priv->surrounding_cursor_pos = cursor_pos;
f7291cc
+        } else {
f7291cc
+            text = text_empty;
f7291cc
+            priv->surrounding_cursor_pos = 0;
f7291cc
+        }
f7291cc
+
eb1661e
+        g_object_unref (priv->surrounding_text);
f7291cc
+        priv->surrounding_text = g_object_ref_sink (text);
eb1661e
+    }
eb1661e
+
f0777a3
     ibus_engine_emit_signal (engine,
f0777a3
                              "DeleteSurroundingText",
f0777a3
                              g_variant_new ("(iu)", offset_from_cursor, nchars));
eb1661e
 }
eb1661e
 
eb1661e
 void
eb1661e
+ibus_engine_get_surrounding_text (IBusEngine   *engine,
eb1661e
+                                  IBusText    **text,
eb1661e
+                                  guint        *cursor_pos)
6cc8efa
+{
6cc8efa
+    IBusEnginePrivate *priv;
6cc8efa
+
e36fdde
+    g_return_if_fail (IBUS_IS_ENGINE (engine));
eb1661e
+    g_return_if_fail (text != NULL);
eb1661e
+    g_return_if_fail (cursor_pos != NULL);
eb1661e
+
6cc8efa
+    priv = IBUS_ENGINE_GET_PRIVATE (engine);
6cc8efa
+
e36fdde
+    *text = g_object_ref (priv->surrounding_text);
6cc8efa
+    *cursor_pos = priv->surrounding_cursor_pos;
6cc8efa
+}
6cc8efa
+
eb1661e
+void
6cc8efa
 ibus_engine_register_properties (IBusEngine   *engine,
6cc8efa
                                  IBusPropList *prop_list)
eb1661e
 {
6cc8efa
diff --git a/src/ibusengine.h b/src/ibusengine.h
f0777a3
index 46d0a04..a5f5aea 100644
6cc8efa
--- a/src/ibusengine.h
6cc8efa
+++ b/src/ibusengine.h
f0777a3
@@ -136,10 +136,14 @@ struct _IBusEngineClass {
6cc8efa
                                      guint           index,
6cc8efa
                                      guint           button,
6cc8efa
                                      guint           state);
6cc8efa
+    void        (* set_surrounding_text)
6cc8efa
+                                    (IBusEngine     *engine,
6cc8efa
+                                     IBusText       *text,
6cc8efa
+                                     guint           cursor_index);
6cc8efa
 
6cc8efa
     /*< private >*/
6cc8efa
     /* padding */
6cc8efa
-    gpointer pdummy[8];
6cc8efa
+    gpointer pdummy[7];
6cc8efa
 };
6cc8efa
 
6cc8efa
 GType        ibus_engine_get_type       (void);
f0777a3
@@ -394,6 +398,21 @@ void ibus_engine_delete_surrounding_text(IBusEngine         *engine,
6cc8efa
                                          guint               nchars);
6cc8efa
 
6cc8efa
 /**
6cc8efa
+ * ibus_engine_get_surrounding_text:
6cc8efa
+ * @engine: An IBusEngine.
6cc8efa
+ * @text: Location to store surrounding text.
6cc8efa
+ * @cursor_pos: Cursor position in characters in @text.
6cc8efa
+ *
6cc8efa
+ * Get surrounding text.
6cc8efa
+ *
6cc8efa
+ * @see_also #IBusEngine::set-surrounding-text
6cc8efa
+ */
6cc8efa
+void ibus_engine_get_surrounding_text(IBusEngine         *engine,
6cc8efa
+                                      IBusText          **text,
6cc8efa
+                                      guint              *cursor_pos);
6cc8efa
+
e36fdde
+
6cc8efa
+/**
6cc8efa
  * ibus_engine_get_name:
6cc8efa
  * @engine: An IBusEngine.
6cc8efa
  * @returns: Name of IBusEngine.
fbc5579
diff --git a/src/ibusenginedesc.c b/src/ibusenginedesc.c
069aa90
index ca5ef60..956ce73 100644
fbc5579
--- a/src/ibusenginedesc.c
fbc5579
+++ b/src/ibusenginedesc.c
fbc5579
@@ -22,6 +22,7 @@
fbc5579
 #include <stdlib.h>
fbc5579
 #include "ibusenginedesc.h"
fbc5579
 #include "ibusxml.h"
fbc5579
+#include "ibusenumtypes.h"
fbc5579
 
fbc5579
 enum {
fbc5579
     LAST_SIGNAL,
fbc5579
@@ -39,6 +40,7 @@ enum {
fbc5579
     PROP_LAYOUT,
fbc5579
     PROP_RANK,
fbc5579
     PROP_HOTKEYS,
fbc5579
+    PROP_REQUIRES,
fbc5579
 };
fbc5579
 
fbc5579
 
fbc5579
@@ -54,6 +56,7 @@ struct _IBusEngineDescPrivate {
fbc5579
     gchar      *layout;
fbc5579
     guint       rank;
fbc5579
     gchar      *hotkeys;
fbc5579
+    guint       requires;
fbc5579
 };
fbc5579
 
fbc5579
 #define IBUS_ENGINE_DESC_GET_PRIVATE(o)  \
069aa90
@@ -79,9 +82,20 @@ static gboolean     ibus_engine_desc_copy           (IBusEngineDesc         *des
fbc5579
                                                      const IBusEngineDesc   *src);
fbc5579
 static gboolean     ibus_engine_desc_parse_xml_node (IBusEngineDesc         *desc,
fbc5579
                                                      XMLNode                *node);
fbc5579
+static void         ibus_engine_desc_output_capabilities
fbc5579
+                                                    (guint                   caps,
fbc5579
+                                                     GString                *output,
fbc5579
+                                                     gint                    indent);
fbc5579
+static guint        ibus_engine_desc_parse_capabilities
fbc5579
+                                                    (XMLNode                *node);
fbc5579
 
fbc5579
 G_DEFINE_TYPE (IBusEngineDesc, ibus_engine_desc, IBUS_TYPE_SERIALIZABLE)
fbc5579
 
fbc5579
+#define DEFAULT_REQUIRES (IBUS_CAP_PREEDIT_TEXT |   \
fbc5579
+                          IBUS_CAP_AUXILIARY_TEXT | \
fbc5579
+                          IBUS_CAP_LOOKUP_TABLE |   \
fbc5579
+                          IBUS_CAP_FOCUS |          \
fbc5579
+                          IBUS_CAP_PROPERTY)
fbc5579
 
fbc5579
 static void
fbc5579
 ibus_engine_desc_class_init (IBusEngineDescClass *class)
069aa90
@@ -232,6 +246,21 @@ ibus_engine_desc_class_init (IBusEngineDescClass *class)
fbc5579
                         "The hotkeys of engine description",
fbc5579
                         "",
fbc5579
                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
fbc5579
+
fbc5579
+    /**
fbc5579
+     * IBusEngineDesc:requires:
fbc5579
+     *
fbc5579
+     * The required capabilities of engine description
fbc5579
+     */
fbc5579
+    g_object_class_install_property (gobject_class,
fbc5579
+                    PROP_REQUIRES,
fbc5579
+                    g_param_spec_uint ("requires",
fbc5579
+                        "description requires",
fbc5579
+                        "The required capabilities of engine description",
fbc5579
+                        0,
fbc5579
+                        G_MAXUINT,
fbc5579
+                        DEFAULT_REQUIRES,
fbc5579
+                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
fbc5579
 }
fbc5579
 
fbc5579
 static void
069aa90
@@ -249,6 +278,7 @@ ibus_engine_desc_init (IBusEngineDesc *desc)
fbc5579
     desc->priv->layout = NULL;
fbc5579
     desc->priv->rank = 0;
fbc5579
     desc->priv->hotkeys = NULL;
fbc5579
+    desc->priv->requires = DEFAULT_REQUIRES;
fbc5579
 }
fbc5579
 
fbc5579
 static void
069aa90
@@ -313,6 +343,9 @@ ibus_engine_desc_set_property (IBusEngineDesc *desc,
fbc5579
         g_assert (desc->priv->hotkeys == NULL);
fbc5579
         desc->priv->hotkeys = g_value_dup_string (value);
fbc5579
         break;
fbc5579
+    case PROP_REQUIRES:
fbc5579
+        desc->priv->requires = g_value_get_uint (value);
fbc5579
+        break;
fbc5579
     default:
fbc5579
         G_OBJECT_WARN_INVALID_PROPERTY_ID (desc, prop_id, pspec);
fbc5579
     }
069aa90
@@ -355,6 +388,9 @@ ibus_engine_desc_get_property (IBusEngineDesc *desc,
fbc5579
     case PROP_HOTKEYS:
fbc5579
         g_value_set_string (value, ibus_engine_desc_get_hotkeys (desc));
fbc5579
         break;
fbc5579
+    case PROP_REQUIRES:
fbc5579
+        g_value_set_uint (value, ibus_engine_desc_get_requires (desc));
fbc5579
+        break;
fbc5579
     default:
fbc5579
         G_OBJECT_WARN_INVALID_PROPERTY_ID (desc, prop_id, pspec);
fbc5579
     }
069aa90
@@ -382,9 +418,28 @@ ibus_engine_desc_serialize (IBusEngineDesc  *desc,
fbc5579
     g_variant_builder_add (builder, "u", desc->priv->rank);
fbc5579
     g_variant_builder_add (builder, "s", NOTNULL (desc->priv->hotkeys));
fbc5579
 #undef NOTNULL
fbc5579
+
fbc5579
+    /* append extra properties */
fbc5579
+    GVariantBuilder array;
fbc5579
+    g_variant_builder_init (&array, G_VARIANT_TYPE ("a{sv}"));
fbc5579
+    g_variant_builder_add (&array, "{sv}", "requires", g_variant_new_uint32 (desc->priv->requires));
fbc5579
+    g_variant_builder_add (builder, "v", g_variant_builder_end (&array));
fbc5579
+
fbc5579
     return TRUE;
fbc5579
 }
fbc5579
 
fbc5579
+static gboolean
fbc5579
+ibus_engine_desc_deserialize_property (IBusEngineDesc *desc,
fbc5579
+                                       const gchar    *name,
fbc5579
+                                       GVariant       *variant)
fbc5579
+{
fbc5579
+    if (g_strcmp0 (name, "requires") == 0) {
fbc5579
+        g_variant_get (variant, "u", &desc->priv->requires);
fbc5579
+        return TRUE;
fbc5579
+    }
fbc5579
+    return FALSE;
fbc5579
+}
fbc5579
+
fbc5579
 static gint
fbc5579
 ibus_engine_desc_deserialize (IBusEngineDesc *desc,
fbc5579
                               GVariant       *variant)
069aa90
@@ -405,6 +460,23 @@ ibus_engine_desc_deserialize (IBusEngineDesc *desc,
fbc5579
     g_variant_get_child (variant, retval++, "u", &desc->priv->rank);
fbc5579
     g_variant_get_child (variant, retval++, "s", &desc->priv->hotkeys);
fbc5579
 
fbc5579
+    /* extract extra properties */
fbc5579
+    GVariantIter iter;
fbc5579
+    GVariant *child, *array;
fbc5579
+
fbc5579
+    g_variant_get_child (variant, retval++, "v", &array);
fbc5579
+    g_variant_iter_init (&iter, array);
fbc5579
+    while ((child = g_variant_iter_next_value (&iter))) {
fbc5579
+        gchar *name;
fbc5579
+        GVariant *value;
fbc5579
+        g_variant_get (child, "{sv}", &name, &value);
fbc5579
+        if (ibus_engine_desc_deserialize_property (desc, name, value))
fbc5579
+            retval++;
fbc5579
+        g_free (name);
fbc5579
+        g_variant_unref (value);
fbc5579
+        g_variant_unref (child);
fbc5579
+    }
fbc5579
+
fbc5579
     return retval;
fbc5579
 }
fbc5579
 
069aa90
@@ -428,6 +500,7 @@ ibus_engine_desc_copy (IBusEngineDesc       *dest,
fbc5579
     dest->priv->layout           = g_strdup (src->priv->layout);
fbc5579
     dest->priv->rank             = src->priv->rank;
fbc5579
     dest->priv->hotkeys          = g_strdup (src->priv->hotkeys);
fbc5579
+    dest->priv->requires         = src->priv->requires;
fbc5579
     return TRUE;
fbc5579
 }
fbc5579
 
069aa90
@@ -439,6 +512,52 @@ ibus_engine_desc_copy (IBusEngineDesc       *dest,
fbc5579
         }                                       \
fbc5579
     }
fbc5579
 
fbc5579
+static void
fbc5579
+ibus_engine_desc_output_capabilities (guint    caps,
fbc5579
+                                      GString *output,
fbc5579
+                                      gint     indent)
fbc5579
+{
fbc5579
+    GFlagsClass *flags_class;
fbc5579
+    gint i;
fbc5579
+
fbc5579
+    flags_class = g_type_class_ref (IBUS_TYPE_CAPABILITE);
fbc5579
+    g_return_if_fail (G_TYPE_IS_FLAGS (IBUS_TYPE_CAPABILITE));
fbc5579
+    for (i = 0; i < flags_class->n_values; i++) {
fbc5579
+        GFlagsValue *flags_value = &flags_class->values[i];
fbc5579
+        if (caps & flags_value->value) {
fbc5579
+            g_string_append_indent (output, indent + 1);
fbc5579
+            g_string_append_printf (output, "<capability>%s</capability>\n",
fbc5579
+                                    flags_value->value_nick);
fbc5579
+        }
fbc5579
+    }
fbc5579
+    g_type_class_unref (flags_class);
fbc5579
+}
fbc5579
+
fbc5579
+static guint
fbc5579
+ibus_engine_desc_parse_capabilities (XMLNode *node)
fbc5579
+{
fbc5579
+    GFlagsClass *flags_class;
fbc5579
+    guint caps = 0;
fbc5579
+    GList *p;
fbc5579
+
fbc5579
+    flags_class = g_type_class_ref (IBUS_TYPE_CAPABILITE);
fbc5579
+    for (p = node->sub_nodes; p != NULL; p = p->next) {
fbc5579
+        XMLNode *sub_node = (XMLNode *) p->data;
fbc5579
+
fbc5579
+        if (g_strcmp0 (sub_node->name, "capability") == 0) {
fbc5579
+            gint i;
fbc5579
+            for (i = 0; i < flags_class->n_values; i++) {
fbc5579
+                GFlagsValue *flags_value = &flags_class->values[i];
fbc5579
+                if (g_strcmp0 (flags_value->value_nick, sub_node->text) == 0)
fbc5579
+                    caps |= flags_value->value;
fbc5579
+            }
fbc5579
+        }
fbc5579
+    }
fbc5579
+    g_type_class_unref (flags_class);
fbc5579
+
fbc5579
+    return caps;
fbc5579
+}
fbc5579
+
fbc5579
 void
fbc5579
 ibus_engine_desc_output (IBusEngineDesc *desc,
fbc5579
                          GString        *output,
069aa90
@@ -467,6 +586,13 @@ ibus_engine_desc_output (IBusEngineDesc *desc,
fbc5579
     OUTPUT_ENTRY_1(hotkeys);
fbc5579
     g_string_append_indent (output, indent + 1);
fbc5579
     g_string_append_printf (output, "<rank>%u</rank>\n", desc->priv->rank);
fbc5579
+
fbc5579
+    g_string_append_indent (output, indent + 1);
fbc5579
+    g_string_append (output, "<requires>\n");
fbc5579
+    ibus_engine_desc_output_capabilities (desc->priv->requires, output,
fbc5579
+                                          indent + 2);
fbc5579
+    g_string_append_indent (output, indent + 1);
fbc5579
+    g_string_append (output, "</requires>\n");
fbc5579
 #undef OUTPUT_ENTRY
fbc5579
 #undef OUTPUT_ENTRY_1
fbc5579
     g_string_append_indent (output, indent);
069aa90
@@ -504,6 +630,11 @@ ibus_engine_desc_parse_xml_node (IBusEngineDesc *desc,
fbc5579
             desc->priv->rank = atoi (sub_node->text);
fbc5579
             continue;
fbc5579
         }
fbc5579
+        if (g_strcmp0 (sub_node->name , "requires") == 0) {
fbc5579
+            desc->priv->requires =
fbc5579
+                ibus_engine_desc_parse_capabilities (sub_node);
fbc5579
+            continue;
fbc5579
+        }
fbc5579
         g_warning ("<engines> element contains invalidate element <%s>", sub_node->name);
fbc5579
     }
fbc5579
     return TRUE;
069aa90
@@ -526,6 +657,7 @@ IBUS_ENGINE_DESC_GET_PROPERTY (icon, const gchar *)
fbc5579
 IBUS_ENGINE_DESC_GET_PROPERTY (layout, const gchar *)
fbc5579
 IBUS_ENGINE_DESC_GET_PROPERTY (rank, guint)
fbc5579
 IBUS_ENGINE_DESC_GET_PROPERTY (hotkeys, const gchar *)
fbc5579
+IBUS_ENGINE_DESC_GET_PROPERTY (requires, guint)
fbc5579
 #undef IBUS_ENGINE_DESC_GET_PROPERTY
fbc5579
 
fbc5579
 IBusEngineDesc *
fbc5579
diff --git a/src/ibusenginedesc.h b/src/ibusenginedesc.h
069aa90
index 9718b15..209d460 100644
fbc5579
--- a/src/ibusenginedesc.h
fbc5579
+++ b/src/ibusenginedesc.h
069aa90
@@ -90,6 +90,8 @@ typedef struct _IBusEngineDescClass IBusEngineDescClass;
fbc5579
  * the front.
fbc5579
  * hotkeys: One or more hotkeys for switching to this engine, separated by
fbc5579
  *  semi-colon.
fbc5579
+ * requires: Capabilities this engine will utilize.  The value is the
fbc5579
+ * union of the IBusCapability flags.
fbc5579
  */
fbc5579
 struct _IBusEngineDesc {
fbc5579
     IBusSerializable parent;
069aa90
@@ -249,6 +251,15 @@ guint            ibus_engine_desc_get_rank      (IBusEngineDesc *info);
fbc5579
 const gchar     *ibus_engine_desc_get_hotkeys   (IBusEngineDesc *info);
fbc5579
 
fbc5579
 /**
fbc5579
+ * ibus_engine_desc_get_requires:
fbc5579
+ * @info: An IBusEngineDesc
fbc5579
+ * @returns: request capabilites property in IBusEngineDesc
fbc5579
+ *
fbc5579
+ * Return the capabilites property in IBusEngineDesc.
fbc5579
+ */
fbc5579
+guint            ibus_engine_desc_get_requires  (IBusEngineDesc *info);
fbc5579
+
fbc5579
+/**
fbc5579
  * ibus_engine_desc_output:
fbc5579
  * @info: An IBusEngineDesc
fbc5579
  * @output: XML-formatted Input method engine description.
069aa90
diff --git a/src/ibusinputcontext.c b/src/ibusinputcontext.c
0610dd6
index f8f5848..9556df3 100644
069aa90
--- a/src/ibusinputcontext.c
069aa90
+++ b/src/ibusinputcontext.c
069aa90
@@ -28,6 +28,9 @@
069aa90
 #include "ibuslookuptable.h"
069aa90
 #include "ibusproplist.h"
069aa90
 
069aa90
+#define IBUS_INPUT_CONTEXT_GET_PRIVATE(o)  \
069aa90
+   (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_INPUT_CONTEXT, IBusInputContextPrivate))
069aa90
+
069aa90
 enum {
069aa90
     ENABLED,
069aa90
     DISABLED,
069aa90
@@ -49,12 +52,25 @@ enum {
069aa90
     CURSOR_DOWN_LOOKUP_TABLE,
069aa90
     REGISTER_PROPERTIES,
069aa90
     UPDATE_PROPERTY,
069aa90
+    ENGINE_CHANGED,
069aa90
     LAST_SIGNAL,
069aa90
 };
069aa90
 
069aa90
+/* BusInputContextPrivate */
069aa90
+struct _IBusInputContextPrivate {
069aa90
+    /* surrounding text */
069aa90
+    IBusText *surrounding_text;
069aa90
+    guint     surrounding_cursor_pos;
069aa90
+};
069aa90
+
069aa90
+typedef struct _IBusInputContextPrivate IBusInputContextPrivate;
069aa90
+
069aa90
 static guint            context_signals[LAST_SIGNAL] = { 0 };
069aa90
 
069aa90
+static IBusText *text_empty = NULL;
069aa90
+
069aa90
 /* functions prototype */
069aa90
+static void     ibus_input_context_real_destroy (IBusProxy              *context);
069aa90
 static void     ibus_input_context_g_signal     (GDBusProxy             *proxy,
069aa90
                                                  const gchar            *sender_name,
069aa90
                                                  const gchar            *signal_name,
069aa90
@@ -65,8 +81,13 @@ G_DEFINE_TYPE (IBusInputContext, ibus_input_context, IBUS_TYPE_PROXY)
069aa90
 static void
069aa90
 ibus_input_context_class_init (IBusInputContextClass *class)
069aa90
 {
069aa90
+    IBusProxyClass *ibus_proxy_class = IBUS_PROXY_CLASS (class);
069aa90
     GDBusProxyClass *g_dbus_proxy_class = G_DBUS_PROXY_CLASS (class);
069aa90
 
069aa90
+    g_type_class_add_private (class, sizeof (IBusInputContextPrivate));
069aa90
+
069aa90
+    ibus_proxy_class->destroy = ibus_input_context_real_destroy;
069aa90
+
069aa90
     g_dbus_proxy_class->g_signal = ibus_input_context_g_signal;
069aa90
 
069aa90
     /* install signals */
069aa90
@@ -101,6 +122,21 @@ ibus_input_context_class_init (IBusInputContextClass *class)
069aa90
             G_TYPE_NONE, 0);
069aa90
 
069aa90
     /**
069aa90
+     * IBusInputContext::engine-changed:
069aa90
+     * @context: An IBusInputContext.
069aa90
+     *
069aa90
+     * Emitted when an IME is changed.
069aa90
+     */
069aa90
+    context_signals[ENGINE_CHANGED] =
069aa90
+        g_signal_new (I_("engine-changed"),
069aa90
+            G_TYPE_FROM_CLASS (class),
069aa90
+            G_SIGNAL_RUN_LAST,
069aa90
+            0,
069aa90
+            NULL, NULL,
069aa90
+            _ibus_marshal_VOID__VOID,
069aa90
+            G_TYPE_NONE, 0);
069aa90
+
069aa90
+    /**
069aa90
      * IBusInputContext::commit-text:
069aa90
      * @context: An IBusInputContext.
069aa90
      * @text: Text to be committed.
069aa90
@@ -429,11 +465,33 @@ ibus_input_context_class_init (IBusInputContextClass *class)
069aa90
             G_TYPE_NONE,
069aa90
             1,
069aa90
             IBUS_TYPE_PROPERTY);
069aa90
+
069aa90
+    text_empty = ibus_text_new_from_static_string ("");
069aa90
+    g_object_ref_sink (text_empty);
069aa90
 }
069aa90
 
069aa90
 static void
069aa90
 ibus_input_context_init (IBusInputContext *context)
069aa90
 {
069aa90
+    IBusInputContextPrivate *priv;
069aa90
+
069aa90
+    priv = IBUS_INPUT_CONTEXT_GET_PRIVATE (context);
069aa90
+    priv->surrounding_text = g_object_ref_sink (text_empty);
069aa90
+    priv->surrounding_cursor_pos = 0;
069aa90
+}
069aa90
+
069aa90
+static void
069aa90
+ibus_input_context_real_destroy (IBusProxy *context)
069aa90
+{
069aa90
+    IBusInputContextPrivate *priv;
069aa90
+    priv = IBUS_INPUT_CONTEXT_GET_PRIVATE (IBUS_INPUT_CONTEXT (context));
069aa90
+
069aa90
+    if (priv->surrounding_text) {
069aa90
+        g_object_unref (priv->surrounding_text);
069aa90
+        priv->surrounding_text = NULL;
069aa90
+    }
069aa90
+
069aa90
+    IBUS_PROXY_CLASS(ibus_input_context_parent_class)->destroy (context);
069aa90
 }
069aa90
 
069aa90
 static void
069aa90
@@ -463,6 +521,7 @@ ibus_input_context_g_signal (GDBusProxy  *proxy,
069aa90
         { "PageDownLookupTable",    PAGE_DOWN_LOOKUP_TABLE   },
069aa90
         { "CursorUpLookupTable",    CURSOR_UP_LOOKUP_TABLE   },
069aa90
         { "CursorDownLookupTable",  CURSOR_DOWN_LOOKUP_TABLE },
069aa90
+        { "EngineChanged",          ENGINE_CHANGED           },
069aa90
     };
069aa90
 
069aa90
     if (g_strcmp0 (signal_name, "CommitText") == 0) {
0610dd6
@@ -848,6 +907,38 @@ ibus_input_context_property_hide (IBusInputContext *context,
069aa90
                        );
069aa90
 }
069aa90
 
069aa90
+void
069aa90
+ibus_input_context_set_surrounding_text (IBusInputContext   *context,
069aa90
+                                         IBusText           *text,
069aa90
+                                         guint32             cursor_pos)
069aa90
+{
069aa90
+    g_assert (IBUS_IS_INPUT_CONTEXT (context));
069aa90
+    g_assert (IBUS_IS_TEXT (text));
069aa90
+
069aa90
+    IBusInputContextPrivate *priv;
069aa90
+    priv = IBUS_INPUT_CONTEXT_GET_PRIVATE (context);
069aa90
+
069aa90
+    if (priv->surrounding_text == NULL ||
069aa90
+        g_strcmp0 (text->text, priv->surrounding_text->text) != 0 ||
069aa90
+        cursor_pos != priv->surrounding_cursor_pos) {
069aa90
+        GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)text);
069aa90
+        if (priv->surrounding_text)
069aa90
+            g_object_unref (priv->surrounding_text);
069aa90
+        priv->surrounding_text = (IBusText *) g_object_ref_sink (text);
069aa90
+        priv->surrounding_cursor_pos = cursor_pos;
069aa90
+
069aa90
+        g_dbus_proxy_call ((GDBusProxy *) context,
069aa90
+                         "SetSurroundingText",              /* method_name */
069aa90
+                         g_variant_new ("(vu)", variant, cursor_pos), /* parameters */
069aa90
+                         G_DBUS_CALL_FLAGS_NONE,            /* flags */
069aa90
+                         -1,                                /* timeout */
069aa90
+                         NULL,                              /* cancellable */
069aa90
+                         NULL,                              /* callback */
069aa90
+                         NULL                               /* user_data */
069aa90
+                         );
069aa90
+    }
069aa90
+}
069aa90
+
069aa90
 gboolean
069aa90
 ibus_input_context_is_enabled (IBusInputContext *context)
069aa90
 {
069aa90
diff --git a/src/ibusinputcontext.h b/src/ibusinputcontext.h
0610dd6
index 96fd00e..1f8ae86 100644
069aa90
--- a/src/ibusinputcontext.h
069aa90
+++ b/src/ibusinputcontext.h
069aa90
@@ -41,6 +41,7 @@
069aa90
 
069aa90
 #include "ibusproxy.h"
069aa90
 #include "ibusenginedesc.h"
069aa90
+#include "ibustext.h"
069aa90
 
069aa90
 /*
069aa90
  * Type macros.
0610dd6
@@ -324,6 +325,16 @@ IBusEngineDesc
069aa90
 void         ibus_input_context_set_engine  (IBusInputContext   *context,
069aa90
                                              const gchar        *name);
069aa90
 
069aa90
+/**
069aa90
+ * ibus_input_context_set_surrounding_text:
069aa90
+ * @context: An IBusInputContext.
069aa90
+ * @text: An IBusText surrounding the current cursor on the application.
069aa90
+ * @cursor_po: Current cursor position in characters in @text.
069aa90
+*/
069aa90
+void         ibus_input_context_set_surrounding_text
069aa90
+                                            (IBusInputContext   *context,
069aa90
+                                             IBusText           *text,
069aa90
+                                             guint32             cursor_pos);
069aa90
 
069aa90
 G_END_DECLS
069aa90
 #endif
069aa90
diff --git a/src/ibusmarshalers.list b/src/ibusmarshalers.list
069aa90
index 5184278..5dc7fc2 100644
069aa90
--- a/src/ibusmarshalers.list
069aa90
+++ b/src/ibusmarshalers.list
069aa90
@@ -13,6 +13,7 @@ VOID:INT,INT,INT,INT
069aa90
 VOID:UINT,UINT
069aa90
 VOID:INT,UINT
069aa90
 VOID:UINT,UINT,UINT
069aa90
+VOID:OBJECT,UINT
069aa90
 VOID:OBJECT,UINT,BOOL
069aa90
 VOID:OBJECT,UINT,BOOL,UINT
069aa90
 VOID:OBJECT,BOOL
6cc8efa
-- 
cf977d1
1.7.3.2
6cc8efa