Adding context to GtkIMContextIM



Figuring that any feature that MS Office has is clearly essential for
the GTK+-2.0 release, I went ahead and created a patch for retrieving
and deleting context to GtkIMContext, so that we can have ultra-cool
Thai input methods. I'll send the patch adding a talking paperclip
next.

I went with names like gtk_im_context_get_surrounding() rather
than gtk_im_context_get_context(), because the latter is clearly
too confusing.

The patch adds to GtkIMContext:

 - A ::retrieve_surrounding signal. In response to this, the
   application/widget, if it chooses to handle the signal, should call
   gtk_im_context_set_surrounding() with as much context as available
   and return TRUE.

 - A ::delete_surrounding signal. In response to this, the
   application/widget, if it chooses to handle the signal, should
   delete the text specified as arguments to the signal and return
   TRUE.

 - A function gtk_im_context_get_surrounding() which emits
   ::retrieve_surrounding and returns any text set.

 - A function gtk_im_context_set_surrounding()

 - A wrapper function gtk_im_context_delete_surrounding for the
   ::delete_surrounding signal.
 
Elsewhere:

 - Support for this in GtkIMMultiContext

 - Support for both signal for GtkEntry and GtkTextView

And:

 - A change to GtkIMContextSimple so that any time the word "own" is
   typed, it changes it to "Owen". I know that some people will find
   this a bit inconvenient, but it's very useful for me.

I think this works pretty well. It's going to only bridge to a very
limited subset of the XIM context and reconversion API, but it should
handle the needs of context-sensitive input well.

Some possible questions about the details of the patch:

 - Does it make sense to have a boolean return from the
   ::retrieve_surrounding signal, or should the boolean return from
   gtk_im_context_get_surrounding() be based only on whether
   gtk_im_context_set_surrounding() was called during the emission?

   Having the boolean return value (with a TRUE-stops-emit signal
   collector) means that we get a first-handler-wins policy rather
   than a last-handler-wins situation; multiple handlers for this
   signal could be considered somewhat pathological.

 - Right now a ::delete_surrounding handler is supposed to return TRUE
   if it supports deletion of surrounding text, without regard to what
   it deletes in this particular instance.  An alternate possibility
   would be that the handler should only delete text and return TRUE
   if it can delete exactly what is specified as arguments to the
   signal.

 - Is it too strange to make the arguments to ::delete_surrounding a
   character offset and character count rather then byte offset and
   byte length, as elsewhere used in GtkIMContext?  The reason why I
   used characters rather than bytes was so that widgets/applications
   didn't have to deal with the possibility of the specified deletion
   not properly corresponding to character boundaries.

Regards,
                                        Owen

? gmenu
? gmenu.tar
? docs/reference/gtk/tmpl/gtkaccessible.sgml
Index: gtk/gtkentry.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkentry.c,v
retrieving revision 1.162
diff -u -p -r1.162 gtkentry.c
--- gtk/gtkentry.c	2001/10/31 23:49:09	1.162
+++ gtk/gtkentry.c	2001/11/01 02:39:55
@@ -232,11 +232,18 @@ static void gtk_entry_keymap_direction_c
 						GtkEntry  *entry);
 /* IM Context Callbacks
  */
-static void gtk_entry_commit_cb           (GtkIMContext      *context,
-					   const gchar       *str,
-					   GtkEntry          *entry);
-static void gtk_entry_preedit_changed_cb  (GtkIMContext      *context,
-					   GtkEntry          *entry);
+static void     gtk_entry_commit_cb               (GtkIMContext *context,
+						   const gchar  *str,
+						   GtkEntry     *entry);
+static void     gtk_entry_preedit_changed_cb      (GtkIMContext *context,
+						   GtkEntry     *entry);
+static gboolean gtk_entry_retrieve_surrounding_cb (GtkIMContext *context,
+						   GtkEntry     *entry);
+static gboolean gtk_entry_delete_surrounding_cb   (GtkIMContext *context,
+						   gint          offset,
+						   gint          n_chars,
+						   GtkEntry     *entry);
+
 /* Internal routines
  */
 static void         gtk_entry_set_positions            (GtkEntry       *entry,
@@ -919,6 +926,10 @@ gtk_entry_init (GtkEntry *entry)
 		    G_CALLBACK (gtk_entry_commit_cb), entry);
   g_signal_connect (G_OBJECT (entry->im_context), "preedit_changed",
 		    G_CALLBACK (gtk_entry_preedit_changed_cb), entry);
+  g_signal_connect (G_OBJECT (entry->im_context), "retrieve_surrounding",
+		    G_CALLBACK (gtk_entry_retrieve_surrounding_cb), entry);
+  g_signal_connect (G_OBJECT (entry->im_context), "delete_surrounding",
+		    G_CALLBACK (gtk_entry_delete_surrounding_cb), entry);
 }
 
 static void
@@ -2270,6 +2281,31 @@ gtk_entry_preedit_changed_cb (GtkIMConte
   g_free (preedit_string);
 
   gtk_entry_recompute (entry);
+}
+
+static gboolean
+gtk_entry_retrieve_surrounding_cb (GtkIMContext *context,
+			       GtkEntry     *entry)
+{
+  gtk_im_context_set_surrounding (context,
+				  entry->text,
+				  entry->n_bytes,
+				  g_utf8_offset_to_pointer (entry->text, entry->current_pos) - entry->text);
+
+  return TRUE;
+}
+
+static gboolean
+gtk_entry_delete_surrounding_cb (GtkIMContext *slave,
+				 gint          offset,
+				 gint          n_chars,
+				 GtkEntry     *entry)
+{
+  gtk_editable_delete_text (GTK_EDITABLE (entry),
+			    entry->current_pos + offset,
+			    entry->current_pos + offset + n_chars);
+
+  return TRUE;
 }
 
 /* Internal functions
Index: gtk/gtkimcontext.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkimcontext.c,v
retrieving revision 1.17
diff -u -p -r1.17 gtkimcontext.c
--- gtk/gtkimcontext.c	2001/10/19 19:28:26	1.17
+++ gtk/gtkimcontext.c	2001/11/01 02:39:55
@@ -18,13 +18,17 @@
  */
 
 #include "gtkimcontext.h"
+#include "gtkmain.h"		/* For _gtk_boolean_handled_accumulator */
 #include "gtksignal.h"
+#include "string.h"
 
 enum {
   PREEDIT_START,
   PREEDIT_END,
   PREEDIT_CHANGED,
   COMMIT,
+  RETRIEVE_SURROUNDING,
+  DELETE_SURROUNDING,
   LAST_SIGNAL
 };
 
@@ -33,12 +37,19 @@ static guint im_context_signals[LAST_SIG
 static void gtk_im_context_class_init (GtkIMContextClass *class);
 static void gtk_im_context_init (GtkIMContext *im_context);
 
-static void     gtk_im_context_real_get_preedit_string (GtkIMContext       *context,
-							gchar             **str,
-							PangoAttrList     **attrs,
-							gint               *cursor_pos);
-static gboolean gtk_im_context_real_filter_keypress    (GtkIMContext       *context,
-							GdkEventKey        *event);
+static void     gtk_im_context_real_get_preedit_string (GtkIMContext   *context,
+							gchar         **str,
+							PangoAttrList **attrs,
+							gint           *cursor_pos);
+static gboolean gtk_im_context_real_filter_keypress    (GtkIMContext   *context,
+							GdkEventKey    *event);
+static gboolean gtk_im_context_real_get_surrounding    (GtkIMContext   *context,
+							gchar         **text,
+							gint           *cursor_index);
+static void     gtk_im_context_real_set_surrounding    (GtkIMContext   *context,
+							const char     *text,
+							gint            len,
+							gint            cursor_index);
 
 GtkType
 gtk_im_context_get_type (void)
@@ -77,6 +88,8 @@ gtk_im_context_class_init (GtkIMContextC
 
   klass->get_preedit_string = gtk_im_context_real_get_preedit_string;
   klass->filter_keypress = gtk_im_context_real_filter_keypress;
+  klass->get_surrounding = gtk_im_context_real_get_surrounding;
+  klass->set_surrounding = gtk_im_context_real_set_surrounding;
 
   im_context_signals[PREEDIT_START] =
     gtk_signal_new ("preedit_start",
@@ -110,6 +123,25 @@ gtk_im_context_class_init (GtkIMContextC
 		    gtk_marshal_VOID__STRING,
 		    GTK_TYPE_NONE, 1,
 		    GTK_TYPE_STRING);
+
+  im_context_signals[RETRIEVE_SURROUNDING] =
+    g_signal_new ("retrieve_surrounding",
+                  GTK_CLASS_TYPE (object_class),
+                  GTK_RUN_LAST,
+                  GTK_SIGNAL_OFFSET (GtkIMContextClass, retrieve_surrounding),
+                  _gtk_boolean_handled_accumulator, NULL,
+                  gtk_marshal_BOOLEAN__VOID,
+                  GTK_TYPE_BOOL, 0);
+  im_context_signals[DELETE_SURROUNDING] =
+    g_signal_new ("delete_surrounding",
+                  GTK_CLASS_TYPE (object_class),
+                  GTK_RUN_LAST,
+                  GTK_SIGNAL_OFFSET (GtkIMContextClass, delete_surrounding),
+                  _gtk_boolean_handled_accumulator, NULL,
+                  gtk_marshal_BOOLEAN__INT_INT,
+                  GTK_TYPE_BOOL, 2,
+                  GTK_TYPE_INT,
+		  GTK_TYPE_INT);
 }
 
 static void
@@ -138,6 +170,67 @@ gtk_im_context_real_filter_keypress (Gtk
   return FALSE;
 }
 
+typedef struct
+{
+  gchar *text;
+  gint cursor_index;
+} SurroundingInfo;
+
+static void
+gtk_im_context_real_set_surrounding (GtkIMContext  *context,
+				     const gchar   *text,
+				     gint           len,
+				     gint           cursor_index)
+{
+  SurroundingInfo *info = g_object_get_data (G_OBJECT (context), "gtk-im-surrounding-info");
+
+  if (info)
+    {
+      g_free (info->text);
+      info->text = g_strndup (text, len);
+      info->cursor_index = cursor_index;
+    }
+}
+
+static gboolean
+gtk_im_context_real_get_surrounding (GtkIMContext *context,
+				     gchar       **text,
+				     gint         *cursor_index)
+{
+  gboolean result;
+  gboolean info_is_local = FALSE;
+  SurroundingInfo local_info = { NULL, 0 };
+  SurroundingInfo *info;
+  
+  info = g_object_get_data (G_OBJECT (context), "gtk-im-surrounding-info");
+  if (!info)
+    {
+      info = &local_info;
+      g_object_set_data (G_OBJECT (context), "gtk-im-surrounding-info", info);
+      info_is_local = TRUE;
+    }
+  
+  g_signal_emit (context,
+		 im_context_signals[RETRIEVE_SURROUNDING], 0,
+		 &result);
+
+  if (result)
+    {
+      *text = g_strdup (info->text ? info->text : "");
+      *cursor_index = info->cursor_index;
+    }
+  else
+    {
+      *text = NULL;
+      *cursor_index = 0;
+    }
+
+  if (info_is_local)
+    g_free (info->text);
+  
+  return result;
+}
+
 /**
  * gtk_im_context_set_client_window:
  * @context: a #GtkIMContext
@@ -324,3 +417,133 @@ gtk_im_context_set_use_preedit (GtkIMCon
   if (klass->set_use_preedit)
     klass->set_use_preedit (context, use_preedit);
 }
+
+/**
+ * gtk_im_context_set_surrounding:
+ * @context: a #GtkIMContext 
+ * @text: text surrounding the insertion point, as UTF-8.
+ *        the preedit string should not be included within
+ *        @text.
+ * @len: the length of @text, or -1 if @text is nul-terminated
+ * @cursor_index: the byte index of the insertion cursor within @text.
+ * 
+ * Sets surrounding context around the insertion point and preedit
+ * string. This function is expected to be called in response to the
+ * GtkIMContext::retrieve_context signal, and will likely have no
+ * effect if called at other times.
+ **/
+void
+gtk_im_context_set_surrounding (GtkIMContext  *context,
+				const gchar   *text,
+				gint           len,
+				gint           cursor_index)
+{
+  GtkIMContextClass *klass;
+  
+  g_return_if_fail (GTK_IS_IM_CONTEXT (context));
+  g_return_if_fail (text != NULL || len == 0);
+
+  if (text == NULL && len == 0)
+    text = "";
+  if (len < 0)
+    len = strlen (text);
+
+  g_return_if_fail (cursor_index >= 0 && cursor_index <= len);
+
+  klass = GTK_IM_CONTEXT_GET_CLASS (context);
+  if (klass->set_surrounding)
+    klass->set_surrounding (context, text, len, cursor_index);
+}
+
+/**
+ * gtk_im_context_get_surrounding:
+ * @context: a #GtkIMContext
+ * @text: location to store a UTF-8 encoded string of text
+ *        holding context around the insertion point.
+ *        If the function returns %TRUE, then you must free
+ *        the result stored in this location with g_free().
+ * @cursor_index: location to store byte index of the insertion cursor
+ *        within @text.
+ * 
+ * Retrieves context around the insertion point. Input methods
+ * typically want context in order to constrain input text based on
+ * existing text; this is important for languages such as Thai where
+ * only some sequences of characters are allowed.
+ *
+ * This function is implemented by emitting the
+ * GtkIMContext::retrieve_context signal on the input method; in
+ * response to this signal, a widget should provide as much context as
+ * is available, up to an entire paragraph, by calling
+ * gtk_im_context_set_surrounding. Note that there is no obligation
+ * for a widget to respond to the ::retrieve_context signal, so input
+ * methods must be prepared to function without context.
+ *
+ * Return value: %TRUE if surrounding text was provided; in this case
+ *    you must free the result stored in *text.
+ **/
+gboolean
+gtk_im_context_get_surrounding (GtkIMContext *context,
+				gchar       **text,
+				gint         *cursor_index)
+{
+  GtkIMContextClass *klass;
+  gchar *local_text = NULL;
+  gint local_index;
+  gboolean result = FALSE;
+  
+  g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), FALSE);
+
+  klass = GTK_IM_CONTEXT_GET_CLASS (context);
+  if (klass->get_surrounding)
+    result = klass->get_surrounding (context,
+				     text ? text : &local_text,
+				     cursor_index ? cursor_index : &local_index);
+
+  if (result)
+    g_free (local_text);
+
+  return result;
+}
+
+/**
+ * gtk_im_context_delete_surrounding:
+ * @context: a #GtkIMContext
+ * @offset: offset from cursor position in chars;
+ *    a negative value means start before the cursor.
+ * @n_chars: number of characters to delete.
+ * 
+ * Asks the widget that the input context is attached to to delete
+ * characters around the cursor position by emitting the
+ * GtkIMContext::delete_context signal. Note that @offset and @n_chars
+ * are in characters not in bytes, which differs from the usage other
+ * places in #GtkIMContext.
+ *
+ * In order to use this function, you should first call
+ * gtk_im_context_get_surrounding() to get the current context, and
+ * call this function immediately afterwards to make sure that you
+ * know what you are deleting. You should also account for the fact
+ * that even if the signal was handled, the input context might not
+ * have deleted all the characters that were requested to be deleted.
+ *
+ * This function is used by an input method that wants to make
+ * subsitutions in the existing text in response to new input. It is
+ * not useful for applications.
+ * 
+ * Return value: %TRUE if the signal was handled.
+ **/
+gboolean
+gtk_im_context_delete_surrounding (GtkIMContext *context,
+				   gint          offset,
+				   gint          n_chars)
+{
+  gboolean result;
+  
+  g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), FALSE);
+
+  g_signal_emit (context,
+		 im_context_signals[DELETE_SURROUNDING], 0,
+		 offset, n_chars, &result);
+
+  return result;
+}
+
Index: gtk/gtkimcontext.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkimcontext.h,v
retrieving revision 1.9
diff -u -p -r1.9 gtkimcontext.h
--- gtk/gtkimcontext.h	2001/10/28 14:17:49	1.9
+++ gtk/gtkimcontext.h	2001/11/01 02:39:55
@@ -50,10 +50,14 @@ struct _GtkIMContextClass
   GtkObjectClass parent_class;
 
   /* Signals */
-  void (*preedit_start)   (GtkIMContext *context);
-  void (*preedit_end)     (GtkIMContext *context);
-  void (*preedit_changed) (GtkIMContext *context);
-  void (*commit)          (GtkIMContext *context, const gchar *str);
+  void     (*preedit_start)        (GtkIMContext *context);
+  void     (*preedit_end)          (GtkIMContext *context);
+  void     (*preedit_changed)      (GtkIMContext *context);
+  void     (*commit)               (GtkIMContext *context, const gchar *str);
+  gboolean (*retrieve_surrounding) (GtkIMContext *context);
+  gboolean (*delete_surrounding)   (GtkIMContext *context,
+				    gint          offset,
+				    gint          n_chars);
 
   /* Virtual functions */
   void     (*set_client_window)   (GtkIMContext   *context,
@@ -71,25 +75,43 @@ struct _GtkIMContextClass
 				   GdkRectangle   *area);
   void     (*set_use_preedit)     (GtkIMContext   *context,
 				   gboolean        use_preedit);
+  void     (*set_surrounding)     (GtkIMContext   *context,
+				   const gchar    *text,
+				   gint            len,
+				   gint            cursor_index);
+  gboolean (*get_surrounding)     (GtkIMContext   *context,
+				   gchar         **text,
+				   gint           *cursor_index);
 };
 
 GtkType       gtk_im_context_get_type           (void) G_GNUC_CONST;
 
-void          gtk_im_context_set_client_window  (GtkIMContext   *context,
-						 GdkWindow      *window);
-void          gtk_im_context_get_preedit_string (GtkIMContext   *context,
-						 gchar         **str,
-						 PangoAttrList **attrs,
-						 gint           *cursor_pos);
-gboolean      gtk_im_context_filter_keypress    (GtkIMContext   *context,
-						 GdkEventKey    *event);
-void          gtk_im_context_focus_in           (GtkIMContext   *context);
-void          gtk_im_context_focus_out          (GtkIMContext   *context);
-void          gtk_im_context_reset              (GtkIMContext   *context);
-void          gtk_im_context_set_cursor_location (GtkIMContext   *context,
-						  GdkRectangle	*area);
-void          gtk_im_context_set_use_preedit     (GtkIMContext   *context,
-						  gboolean        use_preedit);
+void     gtk_im_context_set_client_window   (GtkIMContext   *context,
+					     GdkWindow      *window);
+void     gtk_im_context_get_preedit_string  (GtkIMContext   *context,
+					     gchar         **str,
+					     PangoAttrList **attrs,
+					     gint           *cursor_pos);
+gboolean gtk_im_context_filter_keypress     (GtkIMContext   *context,
+					     GdkEventKey    *event);
+void     gtk_im_context_focus_in            (GtkIMContext   *context);
+void     gtk_im_context_focus_out           (GtkIMContext   *context);
+void     gtk_im_context_reset               (GtkIMContext   *context);
+void     gtk_im_context_set_cursor_location (GtkIMContext   *context,
+					     GdkRectangle   *area);
+void     gtk_im_context_set_use_preedit     (GtkIMContext   *context,
+					     gboolean        use_preedit);
+void     gtk_im_context_set_surrounding     (GtkIMContext   *context,
+					     const gchar    *text,
+					     gint            len,
+					     gint            cursor_index);
+gboolean gtk_im_context_get_surrounding     (GtkIMContext   *context,
+					     gchar         **text,
+					     gint           *cursor_index);
+gboolean gtk_im_context_delete_surrounding  (GtkIMContext   *context,
+					     gint            offset,
+					     gint            n_chars);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
Index: gtk/gtkimcontextsimple.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkimcontextsimple.c,v
retrieving revision 1.18
diff -u -p -r1.18 gtkimcontextsimple.c
--- gtk/gtkimcontextsimple.c	2001/10/28 21:15:33	1.18
+++ gtk/gtkimcontextsimple.c	2001/11/01 02:39:55
@@ -854,6 +854,45 @@ gtk_im_context_simple_commit_char (GtkIM
     }
 
   g_signal_emit_by_name (context, "commit", &buf);
+
+  /* Check if the result left the cursor pointing within the word
+   * "owned", and if so, change to "owen".
+   */
+  {
+    gchar *surrounding;
+    gint pos;
+
+    if (gtk_im_context_get_surrounding (context, &surrounding, &pos))
+      {
+	const char *start = surrounding + pos;
+	const char *end;
+
+	while (start > surrounding)
+	  {
+	    const char *prev = g_utf8_prev_char (start);
+	    if (!g_unichar_isalpha (g_utf8_get_char (prev)))
+	      break;
+	    start = prev;
+	  }
+
+	end = start;
+
+	while (*end)
+	  {
+	    if (!g_unichar_isalpha (g_utf8_get_char (end)))
+	      break;
+	    end = g_utf8_next_char (end);
+	  }
+
+	if (end - start == 3 && !g_ascii_strncasecmp (start, "own", 3))
+	  {
+	    if (gtk_im_context_delete_surrounding (context, start - (surrounding + pos), end - start))
+	      g_signal_emit_by_name (context, "commit", "Owen");
+	  }
+	
+	g_free (surrounding);
+      }
+  }
 }
 
 static int
Index: gtk/gtkimmulticontext.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkimmulticontext.c,v
retrieving revision 1.14
diff -u -p -r1.14 gtkimmulticontext.c
--- gtk/gtkimmulticontext.c	2001/10/28 21:15:33	1.14
+++ gtk/gtkimmulticontext.c	2001/11/01 02:39:55
@@ -45,17 +45,29 @@ static void     gtk_im_multicontext_focu
 static void     gtk_im_multicontext_reset              (GtkIMContext            *context);
 static void     gtk_im_multicontext_set_cursor_location (GtkIMContext            *context,
 							GdkRectangle		*area);
-
-static void     gtk_im_multicontext_preedit_start_cb   (GtkIMContext            *slave,
-							GtkIMMulticontext       *multicontext);
-static void     gtk_im_multicontext_preedit_end_cb     (GtkIMContext            *slave,
-							GtkIMMulticontext       *multicontext);
-static void     gtk_im_multicontext_preedit_changed_cb (GtkIMContext            *slave,
-							GtkIMMulticontext       *multicontext);
-void            gtk_im_multicontext_commit_cb          (GtkIMContext            *slave,
-							const gchar             *str,
-							GtkIMMulticontext       *multicontext);
-
+static gboolean gtk_im_multicontext_get_surrounding    (GtkIMContext            *context,
+							gchar                  **text,
+							gint                    *cursor_index);
+static void     gtk_im_multicontext_set_surrounding    (GtkIMContext            *context,
+							const char              *text,
+							gint                     len,
+							gint                     cursor_index);
+
+static void     gtk_im_multicontext_preedit_start_cb        (GtkIMContext      *slave,
+							     GtkIMMulticontext *multicontext);
+static void     gtk_im_multicontext_preedit_end_cb          (GtkIMContext      *slave,
+							     GtkIMMulticontext *multicontext);
+static void     gtk_im_multicontext_preedit_changed_cb      (GtkIMContext      *slave,
+							     GtkIMMulticontext *multicontext);
+static void     gtk_im_multicontext_commit_cb               (GtkIMContext      *slave,
+							     const gchar       *str,
+							     GtkIMMulticontext *multicontext);
+static gboolean gtk_im_multicontext_retrieve_surrounding_cb (GtkIMContext      *slave,
+							     GtkIMMulticontext *multicontext);
+static gboolean gtk_im_multicontext_delete_surrounding_cb   (GtkIMContext      *slave,
+							     gint               offset,
+							     gint               n_chars,
+							     GtkIMMulticontext *multicontext);
 static GtkIMContextClass *parent_class;
 
 static const gchar *global_context_id = NULL;
@@ -103,6 +115,8 @@ gtk_im_multicontext_class_init (GtkIMMul
   im_context_class->focus_out = gtk_im_multicontext_focus_out;
   im_context_class->reset = gtk_im_multicontext_reset;
   im_context_class->set_cursor_location = gtk_im_multicontext_set_cursor_location;
+  im_context_class->set_surrounding = gtk_im_multicontext_set_surrounding;
+  im_context_class->get_surrounding = gtk_im_multicontext_get_surrounding;
 
   gobject_class->finalize = gtk_im_multicontext_finalize;
 }
@@ -174,6 +188,12 @@ gtk_im_multicontext_set_slave (GtkIMMult
       g_signal_connect (multicontext->slave, "commit",
 			G_CALLBACK (gtk_im_multicontext_commit_cb),
 			multicontext);
+      g_signal_connect (multicontext->slave, "retrieve_surrounding",
+			G_CALLBACK (gtk_im_multicontext_retrieve_surrounding_cb),
+			multicontext);
+      g_signal_connect (multicontext->slave, "delete_surrounding",
+			G_CALLBACK (gtk_im_multicontext_delete_surrounding_cb),
+			multicontext);
       
       if (multicontext->client_window)
 	gtk_im_context_set_client_window (slave, multicontext->client_window);
@@ -301,7 +321,41 @@ gtk_im_multicontext_set_cursor_location 
     gtk_im_context_set_cursor_location (slave, area);
 }
 
+static gboolean
+gtk_im_multicontext_get_surrounding (GtkIMContext  *context,
+				     gchar        **text,
+				     gint          *cursor_index)
+{
+  GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
+  GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
+
+  if (slave)
+    return gtk_im_context_get_surrounding (context, text, cursor_index);
+  else
+    {
+      if (text)
+	*text = NULL;
+      if (cursor_index)
+	*cursor_index = 0;
+
+      return FALSE;
+    }
+}
+
 static void
+gtk_im_multicontext_set_surrounding (GtkIMContext *context,
+				     const char   *text,
+				     gint          len,
+				     gint          cursor_index)
+{
+  GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
+  GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
+
+  if (slave)
+    gtk_im_context_set_surrounding (slave, text, len, cursor_index);
+}
+
+static void
 gtk_im_multicontext_preedit_start_cb   (GtkIMContext      *slave,
 					GtkIMMulticontext *multicontext)
 {
@@ -322,12 +376,37 @@ gtk_im_multicontext_preedit_changed_cb (
   g_signal_emit_by_name (multicontext, "preedit_changed");
 }
 
-void
+static void
 gtk_im_multicontext_commit_cb (GtkIMContext      *slave,
 			       const gchar       *str,
 			       GtkIMMulticontext *multicontext)
 {
   g_signal_emit_by_name (multicontext, "commit", str);;
+}
+
+static gboolean
+gtk_im_multicontext_retrieve_surrounding_cb (GtkIMContext      *slave,
+					     GtkIMMulticontext *multicontext)
+{
+  gboolean result;
+  
+  g_signal_emit_by_name (multicontext, "retrieve_surrounding", &result);
+
+  return result;
+}
+
+static gboolean
+gtk_im_multicontext_delete_surrounding_cb (GtkIMContext      *slave,
+					   gint               offset,
+					   gint               n_chars,
+					   GtkIMMulticontext *multicontext)
+{
+  gboolean result;
+  
+  g_signal_emit_by_name (multicontext, "delete_surrounding",
+			 offset, n_chars, &result);
+
+  return result;
 }
 
 static void
Index: gtk/gtkmarshal.list
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmarshal.list,v
retrieving revision 1.39
diff -u -p -r1.39 gtkmarshal.list
--- gtk/gtkmarshal.list	2001/09/25 16:44:38	1.39
+++ gtk/gtkmarshal.list	2001/11/01 02:39:55
@@ -27,6 +27,7 @@ BOOLEAN:ENUM
 BOOLEAN:OBJECT,INT,INT,UINT
 BOOLEAN:OBJECT,STRING,STRING,BOXED
 BOOLEAN:OBJECT,BOXED,BOXED
+BOOLEAN:INT,INT
 BOOLEAN:VOID
 BOOLEAN:BOOLEAN
 ENUM:ENUM
Index: gtk/gtktextview.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtktextview.c,v
retrieving revision 1.138
diff -u -p -r1.138 gtktextview.c
--- gtk/gtktextview.c	2001/10/31 23:49:09	1.138
+++ gtk/gtktextview.c	2001/11/01 02:39:56
@@ -257,13 +257,19 @@ static void     gtk_text_view_check_curs
 static void     gtk_text_view_pend_cursor_blink      (GtkTextView        *text_view);
 static void     gtk_text_view_stop_cursor_blink      (GtkTextView        *text_view);
 
-static void gtk_text_view_value_changed           (GtkAdjustment *adj,
-						   GtkTextView   *view);
-static void gtk_text_view_commit_handler          (GtkIMContext  *context,
-						   const gchar   *str,
-						   GtkTextView   *text_view);
-static void gtk_text_view_preedit_changed_handler (GtkIMContext  *context,
-						   GtkTextView   *text_view);
+static void     gtk_text_view_value_changed                (GtkAdjustment *adj,
+							    GtkTextView   *view);
+static void     gtk_text_view_commit_handler               (GtkIMContext  *context,
+							    const gchar   *str,
+							    GtkTextView   *text_view);
+static void     gtk_text_view_preedit_changed_handler      (GtkIMContext  *context,
+							    GtkTextView   *text_view);
+static gboolean gtk_text_view_retrieve_surrounding_handler (GtkIMContext  *context,
+							    GtkTextView   *text_view);
+static gboolean gtk_text_view_delete_surrounding_handler   (GtkIMContext  *context,
+							    gint           offset,
+							    gint           n_chars,
+							    GtkTextView   *text_view);
 
 static void gtk_text_view_mark_set_handler       (GtkTextBuffer     *buffer,
                                                   const GtkTextIter *location,
@@ -956,9 +962,12 @@ gtk_text_view_init (GtkTextView *text_vi
 
   g_signal_connect (G_OBJECT (text_view->im_context), "commit",
                     G_CALLBACK (gtk_text_view_commit_handler), text_view);
-
   g_signal_connect (G_OBJECT (text_view->im_context), "preedit_changed",
  		    G_CALLBACK (gtk_text_view_preedit_changed_handler), text_view);
+  g_signal_connect (G_OBJECT (text_view->im_context), "retrieve_surrounding",
+ 		    G_CALLBACK (gtk_text_view_retrieve_surrounding_handler), text_view);
+  g_signal_connect (G_OBJECT (text_view->im_context), "delete_surrounding",
+ 		    G_CALLBACK (gtk_text_view_delete_surrounding_handler), text_view);
 
   text_view->cursor_visible = TRUE;
 
@@ -5307,6 +5316,51 @@ gtk_text_view_preedit_changed_handler (G
 
   pango_attr_list_unref (attrs);
   g_free (str);
+}
+
+static gboolean
+gtk_text_view_retrieve_surrounding_handler (GtkIMContext  *context,
+					    GtkTextView   *text_view)
+{
+  GtkTextIter start;
+  GtkTextIter end;
+  gint pos;
+  gchar *text;
+
+  gtk_text_buffer_get_iter_at_mark (text_view->buffer, &start,  
+				    gtk_text_buffer_get_insert (text_view->buffer));
+  end = start;
+
+  pos = gtk_text_iter_get_line_index (&start);
+  gtk_text_iter_set_line_offset (&start, 0);
+  gtk_text_iter_forward_to_line_end (&end);
+
+  text = gtk_text_iter_get_slice (&start, &end);
+  gtk_im_context_set_surrounding (context, text, -1, pos);
+  g_free (text);
+
+  return TRUE;
+}
+
+static gboolean
+gtk_text_view_delete_surrounding_handler (GtkIMContext  *context,
+					  gint           offset,
+					  gint           n_chars,
+					  GtkTextView   *text_view)
+{
+  GtkTextIter start;
+  GtkTextIter end;
+
+  gtk_text_buffer_get_iter_at_mark (text_view->buffer, &start,  
+				    gtk_text_buffer_get_insert (text_view->buffer));
+  end = start;
+
+  gtk_text_iter_forward_chars (&start, offset);
+  gtk_text_iter_forward_chars (&end, offset + n_chars);
+
+  gtk_text_buffer_delete (text_view->buffer, &start, &end);
+
+  return TRUE;
 }
 
 static void


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]