1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2000 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 #include <string.h>
20 #include "gtkimcontext.h"
21 #include "gtkprivate.h"
22 #include "gtktypebuiltins.h"
23 #include "gtkmarshalers.h"
24 #include "gtkintl.h"
25 
26 /**
27  * SECTION:gtkimcontext
28  * @title: GtkIMContext
29  * @short_description: Base class for input method contexts
30  * @include: gtk/gtk.h,gtk/gtkimmodule.h
31  *
32  * #GtkIMContext defines the interface for GTK+ input methods. An input method
33  * is used by GTK+ text input widgets like #GtkEntry to map from key events to
34  * Unicode character strings.
35  *
36  * The default input method can be set programmatically via the
37  * #GtkSettings:gtk-im-module GtkSettings property. Alternatively, you may set
38  * the GTK_IM_MODULE environment variable as documented in
39  * [Running GTK+ Applications][gtk-running].
40  *
41  * The #GtkEntry #GtkEntry:im-module and #GtkTextView #GtkTextView:im-module
42  * properties may also be used to set input methods for specific widget
43  * instances. For instance, a certain entry widget might be expected to contain
44  * certain characters which would be easier to input with a certain input
45  * method.
46  *
47  * An input method may consume multiple key events in sequence and finally
48  * output the composed result. This is called preediting, and an input method
49  * may provide feedback about this process by displaying the intermediate
50  * composition states as preedit text. For instance, the default GTK+ input
51  * method implements the input of arbitrary Unicode code points by holding down
52  * the Control and Shift keys and then typing “U” followed by the hexadecimal
53  * digits of the code point.  When releasing the Control and Shift keys,
54  * preediting ends and the character is inserted as text. Ctrl+Shift+u20AC for
55  * example results in the € sign.
56  *
57  * Additional input methods can be made available for use by GTK+ widgets as
58  * loadable modules. An input method module is a small shared library which
59  * implements a subclass of #GtkIMContext or #GtkIMContextSimple and exports
60  * these four functions:
61  *
62  * |[<!-- language="C" -->
63  * void im_module_init(GTypeModule *module);
64  * ]|
65  * This function should register the #GType of the #GtkIMContext subclass which
66  * implements the input method by means of g_type_module_register_type(). Note
67  * that g_type_register_static() cannot be used as the type needs to be
68  * registered dynamically.
69  *
70  * |[<!-- language="C" -->
71  * void im_module_exit(void);
72  * ]|
73  * Here goes any cleanup code your input method might require on module unload.
74  *
75  * |[<!-- language="C" -->
76  * void im_module_list(const GtkIMContextInfo ***contexts, int *n_contexts)
77  * {
78  *   *contexts = info_list;
79  *   *n_contexts = G_N_ELEMENTS (info_list);
80  * }
81  * ]|
82  * This function returns the list of input methods provided by the module. The
83  * example implementation above shows a common solution and simply returns a
84  * pointer to statically defined array of #GtkIMContextInfo items for each
85  * provided input method.
86  *
87  * |[<!-- language="C" -->
88  * GtkIMContext * im_module_create(const gchar *context_id);
89  * ]|
90  * This function should return a pointer to a newly created instance of the
91  * #GtkIMContext subclass identified by @context_id. The context ID is the same
92  * as specified in the #GtkIMContextInfo array returned by im_module_list().
93  *
94  * After a new loadable input method module has been installed on the system,
95  * the configuration file `gtk.immodules` needs to be
96  * regenerated by [gtk-query-immodules-3.0][gtk-query-immodules-3.0],
97  * in order for the new input method to become available to GTK+ applications.
98  */
99 
100 enum {
101   PREEDIT_START,
102   PREEDIT_END,
103   PREEDIT_CHANGED,
104   COMMIT,
105   RETRIEVE_SURROUNDING,
106   DELETE_SURROUNDING,
107   LAST_SIGNAL
108 };
109 
110 enum {
111   PROP_INPUT_PURPOSE = 1,
112   PROP_INPUT_HINTS,
113   LAST_PROPERTY
114 };
115 
116 static guint im_context_signals[LAST_SIGNAL] = { 0, };
117 static GParamSpec *properties[LAST_PROPERTY] = { NULL, };
118 
119 typedef struct _GtkIMContextPrivate GtkIMContextPrivate;
120 struct _GtkIMContextPrivate {
121   GtkInputPurpose purpose;
122   GtkInputHints hints;
123 };
124 
125 static void     gtk_im_context_real_get_preedit_string (GtkIMContext   *context,
126 							gchar         **str,
127 							PangoAttrList **attrs,
128 							gint           *cursor_pos);
129 static gboolean gtk_im_context_real_filter_keypress    (GtkIMContext   *context,
130 							GdkEventKey    *event);
131 static gboolean gtk_im_context_real_get_surrounding    (GtkIMContext   *context,
132 							gchar         **text,
133 							gint           *cursor_index);
134 static void     gtk_im_context_real_set_surrounding    (GtkIMContext   *context,
135 							const char     *text,
136 							gint            len,
137 							gint            cursor_index);
138 
139 static void     gtk_im_context_get_property            (GObject        *obj,
140                                                         guint           property_id,
141                                                         GValue         *value,
142                                                         GParamSpec     *pspec);
143 static void     gtk_im_context_set_property            (GObject        *obj,
144                                                         guint           property_id,
145                                                         const GValue   *value,
146                                                         GParamSpec     *pspec);
147 
148 
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(GtkIMContext,gtk_im_context,G_TYPE_OBJECT)149 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkIMContext, gtk_im_context, G_TYPE_OBJECT)
150 
151 /**
152  * GtkIMContextClass:
153  * @preedit_start: Default handler of the #GtkIMContext::preedit-start signal.
154  * @preedit_end: Default handler of the #GtkIMContext::preedit-end signal.
155  * @preedit_changed: Default handler of the #GtkIMContext::preedit-changed
156  *   signal.
157  * @commit: Default handler of the #GtkIMContext::commit signal.
158  * @retrieve_surrounding: Default handler of the
159  *   #GtkIMContext::retrieve-surrounding signal.
160  * @delete_surrounding: Default handler of the
161  *   #GtkIMContext::delete-surrounding signal.
162  * @set_client_window: Called via gtk_im_context_set_client_window() when the
163  *   input window where the entered text will appear changes. Override this to
164  *   keep track of the current input window, for instance for the purpose of
165  *   positioning a status display of your input method.
166  * @get_preedit_string: Called via gtk_im_context_get_preedit_string() to
167  *   retrieve the text currently being preedited for display at the cursor
168  *   position. Any input method which composes complex characters or any
169  *   other compositions from multiple sequential key presses should override
170  *   this method to provide feedback.
171  * @filter_keypress: Called via gtk_im_context_filter_keypress() on every
172  *   key press or release event. Every non-trivial input method needs to
173  *   override this in order to implement the mapping from key events to text.
174  *   A return value of %TRUE indicates to the caller that the event was
175  *   consumed by the input method. In that case, the #GtkIMContext::commit
176  *   signal should be emitted upon completion of a key sequence to pass the
177  *   resulting text back to the input widget. Alternatively, %FALSE may be
178  *   returned to indicate that the event wasn’t handled by the input method.
179  *   If a builtin mapping exists for the key, it is used to produce a
180  *   character.
181  * @focus_in: Called via gtk_im_context_focus_in() when the input widget
182  *   has gained focus. May be overridden to keep track of the current focus.
183  * @focus_out: Called via gtk_im_context_focus_out() when the input widget
184  *   has lost focus. May be overridden to keep track of the current focus.
185  * @reset: Called via gtk_im_context_reset() to signal a change such as a
186  *   change in cursor position. An input method that implements preediting
187  *   should override this method to clear the preedit state on reset.
188  * @set_cursor_location: Called via gtk_im_context_set_cursor_location()
189  *   to inform the input method of the current cursor location relative to
190  *   the client window. May be overridden to implement the display of popup
191  *   windows at the cursor position.
192  * @set_use_preedit: Called via gtk_im_context_set_use_preedit() to control
193  *   the use of the preedit string. Override this to display feedback by some
194  *   other means if turned off.
195  * @set_surrounding: Called via gtk_im_context_set_surrounding() in response
196  *   to signal #GtkIMContext::retrieve-surrounding to update the input
197  *   method’s idea of the context around the cursor. It is not necessary to
198  *   override this method even with input methods which implement
199  *   context-dependent behavior. The base implementation is sufficient for
200  *   gtk_im_context_get_surrounding() to work.
201  * @get_surrounding: Called via gtk_im_context_get_surrounding() to update
202  *   the context around the cursor location. It is not necessary to override
203  *   this method even with input methods which implement context-dependent
204  *   behavior. The base implementation emits
205  *   #GtkIMContext::retrieve-surrounding and records the context received
206  *   by the subsequent invocation of @get_surrounding.
207  */
208 static void
209 gtk_im_context_class_init (GtkIMContextClass *klass)
210 {
211   GObjectClass *object_class = G_OBJECT_CLASS (klass);
212 
213   object_class->get_property = gtk_im_context_get_property;
214   object_class->set_property = gtk_im_context_set_property;
215 
216   klass->get_preedit_string = gtk_im_context_real_get_preedit_string;
217   klass->filter_keypress = gtk_im_context_real_filter_keypress;
218   klass->get_surrounding = gtk_im_context_real_get_surrounding;
219   klass->set_surrounding = gtk_im_context_real_set_surrounding;
220 
221   /**
222    * GtkIMContext::preedit-start:
223    * @context: the object on which the signal is emitted
224    *
225    * The ::preedit-start signal is emitted when a new preediting sequence
226    * starts.
227    */
228   im_context_signals[PREEDIT_START] =
229     g_signal_new (I_("preedit-start"),
230 		  G_TYPE_FROM_CLASS (klass),
231 		  G_SIGNAL_RUN_LAST,
232 		  G_STRUCT_OFFSET (GtkIMContextClass, preedit_start),
233 		  NULL, NULL,
234 		  NULL,
235 		  G_TYPE_NONE, 0);
236   /**
237    * GtkIMContext::preedit-end:
238    * @context: the object on which the signal is emitted
239    *
240    * The ::preedit-end signal is emitted when a preediting sequence
241    * has been completed or canceled.
242    */
243   im_context_signals[PREEDIT_END] =
244     g_signal_new (I_("preedit-end"),
245 		  G_TYPE_FROM_CLASS (klass),
246 		  G_SIGNAL_RUN_LAST,
247 		  G_STRUCT_OFFSET (GtkIMContextClass, preedit_end),
248 		  NULL, NULL,
249 		  NULL,
250 		  G_TYPE_NONE, 0);
251   /**
252    * GtkIMContext::preedit-changed:
253    * @context: the object on which the signal is emitted
254    *
255    * The ::preedit-changed signal is emitted whenever the preedit sequence
256    * currently being entered has changed.  It is also emitted at the end of
257    * a preedit sequence, in which case
258    * gtk_im_context_get_preedit_string() returns the empty string.
259    */
260   im_context_signals[PREEDIT_CHANGED] =
261     g_signal_new (I_("preedit-changed"),
262 		  G_TYPE_FROM_CLASS (klass),
263 		  G_SIGNAL_RUN_LAST,
264 		  G_STRUCT_OFFSET (GtkIMContextClass, preedit_changed),
265 		  NULL, NULL,
266 		  NULL,
267 		  G_TYPE_NONE, 0);
268   /**
269    * GtkIMContext::commit:
270    * @context: the object on which the signal is emitted
271    * @str: the completed character(s) entered by the user
272    *
273    * The ::commit signal is emitted when a complete input sequence
274    * has been entered by the user. This can be a single character
275    * immediately after a key press or the final result of preediting.
276    */
277   im_context_signals[COMMIT] =
278     g_signal_new (I_("commit"),
279 		  G_TYPE_FROM_CLASS (klass),
280 		  G_SIGNAL_RUN_LAST,
281 		  G_STRUCT_OFFSET (GtkIMContextClass, commit),
282 		  NULL, NULL,
283 		  NULL,
284 		  G_TYPE_NONE, 1,
285 		  G_TYPE_STRING);
286   /**
287    * GtkIMContext::retrieve-surrounding:
288    * @context: the object on which the signal is emitted
289    *
290    * The ::retrieve-surrounding signal is emitted when the input method
291    * requires the context surrounding the cursor.  The callback should set
292    * the input method surrounding context by calling the
293    * gtk_im_context_set_surrounding() method.
294    *
295    * Returns: %TRUE if the signal was handled.
296    */
297   im_context_signals[RETRIEVE_SURROUNDING] =
298     g_signal_new (I_("retrieve-surrounding"),
299                   G_TYPE_FROM_CLASS (klass),
300                   G_SIGNAL_RUN_LAST,
301                   G_STRUCT_OFFSET (GtkIMContextClass, retrieve_surrounding),
302                   _gtk_boolean_handled_accumulator, NULL,
303                   _gtk_marshal_BOOLEAN__VOID,
304                   G_TYPE_BOOLEAN, 0);
305   g_signal_set_va_marshaller (im_context_signals[RETRIEVE_SURROUNDING],
306                               G_TYPE_FROM_CLASS (klass),
307                               _gtk_marshal_BOOLEAN__VOIDv);
308   /**
309    * GtkIMContext::delete-surrounding:
310    * @context: the object on which the signal is emitted
311    * @offset:  the character offset from the cursor position of the text
312    *           to be deleted. A negative value indicates a position before
313    *           the cursor.
314    * @n_chars: the number of characters to be deleted
315    *
316    * The ::delete-surrounding signal is emitted when the input method
317    * needs to delete all or part of the context surrounding the cursor.
318    *
319    * Returns: %TRUE if the signal was handled.
320    */
321   im_context_signals[DELETE_SURROUNDING] =
322     g_signal_new (I_("delete-surrounding"),
323                   G_TYPE_FROM_CLASS (klass),
324                   G_SIGNAL_RUN_LAST,
325                   G_STRUCT_OFFSET (GtkIMContextClass, delete_surrounding),
326                   _gtk_boolean_handled_accumulator, NULL,
327                   _gtk_marshal_BOOLEAN__INT_INT,
328                   G_TYPE_BOOLEAN, 2,
329                   G_TYPE_INT,
330                   G_TYPE_INT);
331   g_signal_set_va_marshaller (im_context_signals[DELETE_SURROUNDING],
332                               G_TYPE_FROM_CLASS (klass),
333                               _gtk_marshal_BOOLEAN__INT_INTv);
334 
335   properties[PROP_INPUT_PURPOSE] =
336     g_param_spec_enum ("input-purpose",
337                          P_("Purpose"),
338                          P_("Purpose of the text field"),
339                          GTK_TYPE_INPUT_PURPOSE,
340                          GTK_INPUT_PURPOSE_FREE_FORM,
341                          G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
342 
343   properties[PROP_INPUT_HINTS] =
344     g_param_spec_flags ("input-hints",
345                          P_("hints"),
346                          P_("Hints for the text field behaviour"),
347                          GTK_TYPE_INPUT_HINTS,
348                          GTK_INPUT_HINT_NONE,
349                          G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
350 
351   g_object_class_install_properties (object_class, LAST_PROPERTY, properties);
352 }
353 
354 static void
gtk_im_context_init(GtkIMContext * im_context)355 gtk_im_context_init (GtkIMContext *im_context)
356 {
357 }
358 
359 static void
gtk_im_context_real_get_preedit_string(GtkIMContext * context,gchar ** str,PangoAttrList ** attrs,gint * cursor_pos)360 gtk_im_context_real_get_preedit_string (GtkIMContext       *context,
361 					gchar             **str,
362 					PangoAttrList     **attrs,
363 					gint               *cursor_pos)
364 {
365   if (str)
366     *str = g_strdup ("");
367   if (attrs)
368     *attrs = pango_attr_list_new ();
369   if (cursor_pos)
370     *cursor_pos = 0;
371 }
372 
373 static gboolean
gtk_im_context_real_filter_keypress(GtkIMContext * context,GdkEventKey * event)374 gtk_im_context_real_filter_keypress (GtkIMContext       *context,
375 				     GdkEventKey        *event)
376 {
377   return FALSE;
378 }
379 
380 typedef struct
381 {
382   gchar *text;
383   gint cursor_index;
384 } SurroundingInfo;
385 
386 static void
gtk_im_context_real_set_surrounding(GtkIMContext * context,const gchar * text,gint len,gint cursor_index)387 gtk_im_context_real_set_surrounding (GtkIMContext  *context,
388 				     const gchar   *text,
389 				     gint           len,
390 				     gint           cursor_index)
391 {
392   SurroundingInfo *info = g_object_get_data (G_OBJECT (context),
393                                              "gtk-im-surrounding-info");
394 
395   if (info)
396     {
397       g_free (info->text);
398       info->text = g_strndup (text, len);
399       info->cursor_index = cursor_index;
400     }
401 }
402 
403 static gboolean
gtk_im_context_real_get_surrounding(GtkIMContext * context,gchar ** text,gint * cursor_index)404 gtk_im_context_real_get_surrounding (GtkIMContext *context,
405 				     gchar       **text,
406 				     gint         *cursor_index)
407 {
408   gboolean result;
409   gboolean info_is_local = FALSE;
410   SurroundingInfo local_info = { NULL, 0 };
411   SurroundingInfo *info;
412 
413   info = g_object_get_data (G_OBJECT (context), "gtk-im-surrounding-info");
414   if (!info)
415     {
416       info = &local_info;
417       g_object_set_data (G_OBJECT (context), I_("gtk-im-surrounding-info"), info);
418       info_is_local = TRUE;
419     }
420 
421   g_signal_emit (context,
422 		 im_context_signals[RETRIEVE_SURROUNDING], 0,
423 		 &result);
424 
425   if (result)
426     {
427       *text = g_strdup (info->text ? info->text : "");
428       *cursor_index = info->cursor_index;
429     }
430   else
431     {
432       *text = NULL;
433       *cursor_index = 0;
434     }
435 
436   if (info_is_local)
437     {
438       g_free (info->text);
439       g_object_set_data (G_OBJECT (context), I_("gtk-im-surrounding-info"), NULL);
440     }
441 
442   return result;
443 }
444 
445 /**
446  * gtk_im_context_set_client_window:
447  * @context: a #GtkIMContext
448  * @window: (allow-none):  the client window. This may be %NULL to indicate
449  *           that the previous client window no longer exists.
450  *
451  * Set the client window for the input context; this is the
452  * #GdkWindow in which the input appears. This window is
453  * used in order to correctly position status windows, and may
454  * also be used for purposes internal to the input method.
455  **/
456 void
gtk_im_context_set_client_window(GtkIMContext * context,GdkWindow * window)457 gtk_im_context_set_client_window (GtkIMContext *context,
458 				  GdkWindow    *window)
459 {
460   GtkIMContextClass *klass;
461 
462   g_return_if_fail (GTK_IS_IM_CONTEXT (context));
463 
464   klass = GTK_IM_CONTEXT_GET_CLASS (context);
465   if (klass->set_client_window)
466     klass->set_client_window (context, window);
467 }
468 
469 /**
470  * gtk_im_context_get_preedit_string:
471  * @context:    a #GtkIMContext
472  * @str:        (out) (transfer full): location to store the retrieved
473  *              string. The string retrieved must be freed with g_free().
474  * @attrs:      (out) (transfer full): location to store the retrieved
475  *              attribute list.  When you are done with this list, you
476  *              must unreference it with pango_attr_list_unref().
477  * @cursor_pos: (out): location to store position of cursor (in characters)
478  *              within the preedit string.
479  *
480  * Retrieve the current preedit string for the input context,
481  * and a list of attributes to apply to the string.
482  * This string should be displayed inserted at the insertion
483  * point.
484  **/
485 void
gtk_im_context_get_preedit_string(GtkIMContext * context,gchar ** str,PangoAttrList ** attrs,gint * cursor_pos)486 gtk_im_context_get_preedit_string (GtkIMContext   *context,
487 				   gchar         **str,
488 				   PangoAttrList **attrs,
489 				   gint           *cursor_pos)
490 {
491   GtkIMContextClass *klass;
492 
493   g_return_if_fail (GTK_IS_IM_CONTEXT (context));
494 
495   klass = GTK_IM_CONTEXT_GET_CLASS (context);
496   klass->get_preedit_string (context, str, attrs, cursor_pos);
497   g_return_if_fail (str == NULL || g_utf8_validate (*str, -1, NULL));
498 }
499 
500 /**
501  * gtk_im_context_filter_keypress:
502  * @context: a #GtkIMContext
503  * @event: the key event
504  *
505  * Allow an input method to internally handle key press and release
506  * events. If this function returns %TRUE, then no further processing
507  * should be done for this key event.
508  *
509  * Returns: %TRUE if the input method handled the key event.
510  *
511  **/
512 gboolean
gtk_im_context_filter_keypress(GtkIMContext * context,GdkEventKey * key)513 gtk_im_context_filter_keypress (GtkIMContext *context,
514 				GdkEventKey  *key)
515 {
516   GtkIMContextClass *klass;
517 
518   g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), FALSE);
519   g_return_val_if_fail (key != NULL, FALSE);
520 
521   klass = GTK_IM_CONTEXT_GET_CLASS (context);
522   return klass->filter_keypress (context, key);
523 }
524 
525 /**
526  * gtk_im_context_focus_in:
527  * @context: a #GtkIMContext
528  *
529  * Notify the input method that the widget to which this
530  * input context corresponds has gained focus. The input method
531  * may, for example, change the displayed feedback to reflect
532  * this change.
533  **/
534 void
gtk_im_context_focus_in(GtkIMContext * context)535 gtk_im_context_focus_in (GtkIMContext   *context)
536 {
537   GtkIMContextClass *klass;
538 
539   g_return_if_fail (GTK_IS_IM_CONTEXT (context));
540 
541   klass = GTK_IM_CONTEXT_GET_CLASS (context);
542   if (klass->focus_in)
543     klass->focus_in (context);
544 }
545 
546 /**
547  * gtk_im_context_focus_out:
548  * @context: a #GtkIMContext
549  *
550  * Notify the input method that the widget to which this
551  * input context corresponds has lost focus. The input method
552  * may, for example, change the displayed feedback or reset the contexts
553  * state to reflect this change.
554  **/
555 void
gtk_im_context_focus_out(GtkIMContext * context)556 gtk_im_context_focus_out (GtkIMContext   *context)
557 {
558   GtkIMContextClass *klass;
559 
560   g_return_if_fail (GTK_IS_IM_CONTEXT (context));
561 
562   klass = GTK_IM_CONTEXT_GET_CLASS (context);
563   if (klass->focus_out)
564     klass->focus_out (context);
565 }
566 
567 /**
568  * gtk_im_context_reset:
569  * @context: a #GtkIMContext
570  *
571  * Notify the input method that a change such as a change in cursor
572  * position has been made. This will typically cause the input
573  * method to clear the preedit state.
574  **/
575 void
gtk_im_context_reset(GtkIMContext * context)576 gtk_im_context_reset (GtkIMContext   *context)
577 {
578   GtkIMContextClass *klass;
579 
580   g_return_if_fail (GTK_IS_IM_CONTEXT (context));
581 
582   klass = GTK_IM_CONTEXT_GET_CLASS (context);
583   if (klass->reset)
584     klass->reset (context);
585 }
586 
587 
588 /**
589  * gtk_im_context_set_cursor_location:
590  * @context: a #GtkIMContext
591  * @area: new location
592  *
593  * Notify the input method that a change in cursor
594  * position has been made. The location is relative to the client
595  * window.
596  **/
597 void
gtk_im_context_set_cursor_location(GtkIMContext * context,const GdkRectangle * area)598 gtk_im_context_set_cursor_location (GtkIMContext       *context,
599 				    const GdkRectangle *area)
600 {
601   GtkIMContextClass *klass;
602 
603   g_return_if_fail (GTK_IS_IM_CONTEXT (context));
604 
605   klass = GTK_IM_CONTEXT_GET_CLASS (context);
606   if (klass->set_cursor_location)
607     klass->set_cursor_location (context, (GdkRectangle *) area);
608 }
609 
610 /**
611  * gtk_im_context_set_use_preedit:
612  * @context: a #GtkIMContext
613  * @use_preedit: whether the IM context should use the preedit string.
614  *
615  * Sets whether the IM context should use the preedit string
616  * to display feedback. If @use_preedit is FALSE (default
617  * is TRUE), then the IM context may use some other method to display
618  * feedback, such as displaying it in a child of the root window.
619  **/
620 void
gtk_im_context_set_use_preedit(GtkIMContext * context,gboolean use_preedit)621 gtk_im_context_set_use_preedit (GtkIMContext *context,
622 				gboolean      use_preedit)
623 {
624   GtkIMContextClass *klass;
625 
626   g_return_if_fail (GTK_IS_IM_CONTEXT (context));
627 
628   klass = GTK_IM_CONTEXT_GET_CLASS (context);
629   if (klass->set_use_preedit)
630     klass->set_use_preedit (context, use_preedit);
631 }
632 
633 /**
634  * gtk_im_context_set_surrounding:
635  * @context: a #GtkIMContext
636  * @text: text surrounding the insertion point, as UTF-8.
637  *        the preedit string should not be included within
638  *        @text.
639  * @len: the length of @text, or -1 if @text is nul-terminated
640  * @cursor_index: the byte index of the insertion cursor within @text.
641  *
642  * Sets surrounding context around the insertion point and preedit
643  * string. This function is expected to be called in response to the
644  * GtkIMContext::retrieve_surrounding signal, and will likely have no
645  * effect if called at other times.
646  **/
647 void
gtk_im_context_set_surrounding(GtkIMContext * context,const gchar * text,gint len,gint cursor_index)648 gtk_im_context_set_surrounding (GtkIMContext  *context,
649 				const gchar   *text,
650 				gint           len,
651 				gint           cursor_index)
652 {
653   GtkIMContextClass *klass;
654 
655   g_return_if_fail (GTK_IS_IM_CONTEXT (context));
656   g_return_if_fail (text != NULL || len == 0);
657 
658   if (text == NULL && len == 0)
659     text = "";
660   if (len < 0)
661     len = strlen (text);
662 
663   g_return_if_fail (cursor_index >= 0 && cursor_index <= len);
664 
665   klass = GTK_IM_CONTEXT_GET_CLASS (context);
666   if (klass->set_surrounding)
667     klass->set_surrounding (context, text, len, cursor_index);
668 }
669 
670 /**
671  * gtk_im_context_get_surrounding:
672  * @context: a #GtkIMContext
673  * @text: (out) (transfer full): location to store a UTF-8 encoded
674  *        string of text holding context around the insertion point.
675  *        If the function returns %TRUE, then you must free the result
676  *        stored in this location with g_free().
677  * @cursor_index: (out): location to store byte index of the insertion
678  *        cursor within @text.
679  *
680  * Retrieves context around the insertion point. Input methods
681  * typically want context in order to constrain input text based on
682  * existing text; this is important for languages such as Thai where
683  * only some sequences of characters are allowed.
684  *
685  * This function is implemented by emitting the
686  * GtkIMContext::retrieve_surrounding signal on the input method; in
687  * response to this signal, a widget should provide as much context as
688  * is available, up to an entire paragraph, by calling
689  * gtk_im_context_set_surrounding(). Note that there is no obligation
690  * for a widget to respond to the ::retrieve_surrounding signal, so input
691  * methods must be prepared to function without context.
692  *
693  * Returns: %TRUE if surrounding text was provided; in this case
694  *    you must free the result stored in *text.
695  **/
696 gboolean
gtk_im_context_get_surrounding(GtkIMContext * context,gchar ** text,gint * cursor_index)697 gtk_im_context_get_surrounding (GtkIMContext *context,
698 				gchar       **text,
699 				gint         *cursor_index)
700 {
701   GtkIMContextClass *klass;
702   gchar *local_text = NULL;
703   gint local_index;
704   gboolean result = FALSE;
705 
706   g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), FALSE);
707 
708   klass = GTK_IM_CONTEXT_GET_CLASS (context);
709   if (klass->get_surrounding)
710     result = klass->get_surrounding (context,
711 				     text ? text : &local_text,
712 				     cursor_index ? cursor_index : &local_index);
713 
714   if (result)
715     g_free (local_text);
716 
717   return result;
718 }
719 
720 /**
721  * gtk_im_context_delete_surrounding:
722  * @context: a #GtkIMContext
723  * @offset: offset from cursor position in chars;
724  *    a negative value means start before the cursor.
725  * @n_chars: number of characters to delete.
726  *
727  * Asks the widget that the input context is attached to to delete
728  * characters around the cursor position by emitting the
729  * GtkIMContext::delete_surrounding signal. Note that @offset and @n_chars
730  * are in characters not in bytes which differs from the usage other
731  * places in #GtkIMContext.
732  *
733  * In order to use this function, you should first call
734  * gtk_im_context_get_surrounding() to get the current context, and
735  * call this function immediately afterwards to make sure that you
736  * know what you are deleting. You should also account for the fact
737  * that even if the signal was handled, the input context might not
738  * have deleted all the characters that were requested to be deleted.
739  *
740  * This function is used by an input method that wants to make
741  * subsitutions in the existing text in response to new input. It is
742  * not useful for applications.
743  *
744  * Returns: %TRUE if the signal was handled.
745  **/
746 gboolean
gtk_im_context_delete_surrounding(GtkIMContext * context,gint offset,gint n_chars)747 gtk_im_context_delete_surrounding (GtkIMContext *context,
748 				   gint          offset,
749 				   gint          n_chars)
750 {
751   gboolean result;
752 
753   g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), FALSE);
754 
755   g_signal_emit (context,
756 		 im_context_signals[DELETE_SURROUNDING], 0,
757 		 offset, n_chars, &result);
758 
759   return result;
760 }
761 
762 static void
gtk_im_context_get_property(GObject * obj,guint property_id,GValue * value,GParamSpec * pspec)763 gtk_im_context_get_property (GObject    *obj,
764                              guint       property_id,
765                              GValue     *value,
766                              GParamSpec *pspec)
767 {
768   GtkIMContextPrivate *priv = gtk_im_context_get_instance_private (GTK_IM_CONTEXT (obj));
769 
770   switch (property_id)
771     {
772     case PROP_INPUT_PURPOSE:
773       g_value_set_enum (value, priv->purpose);
774       break;
775     case PROP_INPUT_HINTS:
776       g_value_set_flags (value, priv->hints);
777       break;
778     default:
779       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
780       break;
781     }
782 }
783 
784 static void
gtk_im_context_set_property(GObject * obj,guint property_id,const GValue * value,GParamSpec * pspec)785 gtk_im_context_set_property (GObject      *obj,
786                              guint         property_id,
787                              const GValue *value,
788                              GParamSpec   *pspec)
789 {
790   GtkIMContextPrivate *priv = gtk_im_context_get_instance_private (GTK_IM_CONTEXT (obj));
791 
792   switch (property_id)
793     {
794     case PROP_INPUT_PURPOSE:
795       if (priv->purpose != g_value_get_enum (value))
796         {
797           priv->purpose = g_value_get_enum (value);
798           g_object_notify_by_pspec (obj, pspec);
799         }
800       break;
801     case PROP_INPUT_HINTS:
802       if (priv->hints != g_value_get_flags (value))
803         {
804           priv->hints = g_value_get_flags (value);
805           g_object_notify_by_pspec (obj, pspec);
806         }
807       break;
808     default:
809       G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
810       break;
811     }
812 }
813