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