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#include "gdk/gdkeventsprivate.h"
27
28/**
29 * GtkIMContext:
30 *
31 * `GtkIMContext` defines the interface for GTK input methods.
32 *
33 * `GtkIMContext` is used by GTK text input widgets like `GtkText`
34 * to map from key events to Unicode character strings.
35 *
36 * An input method may consume multiple key events in sequence before finally
37 * outputting the composed result. This is called *preediting*, and an input
38 * method may provide feedback about this process by displaying the intermediate
39 * composition states as preedit text. To do so, the `GtkIMContext` will emit
40 * [signal@Gtk.IMContext::preedit-start], [signal@Gtk.IMContext::preedit-changed]
41 * and [signal@Gtk.IMContext::preedit-end] signals.
42 *
43 * For instance, the built-in GTK input method [class@Gtk.IMContextSimple]
44 * implements the input of arbitrary Unicode code points by holding down the
45 * <kbd>Control</kbd> and <kbd>Shift</kbd> keys and then typing <kbd>u</kbd>
46 * followed by the hexadecimal digits of the code point. When releasing the
47 * <kbd>Control</kbd> and <kbd>Shift</kbd> keys, preediting ends and the
48 * character is inserted as text. For example,
49 *
50 * Ctrl+Shift+u 2 0 A C
51 *
52 * results in the € sign.
53 *
54 * Additional input methods can be made available for use by GTK widgets as
55 * loadable modules. An input method module is a small shared library which
56 * provides a `GIOExtension` for the extension point named "gtk-im-module".
57 *
58 * To connect a widget to the users preferred input method, you should use
59 * [class@Gtk.IMMulticontext].
60 */
61
62enum {
63 PREEDIT_START,
64 PREEDIT_END,
65 PREEDIT_CHANGED,
66 COMMIT,
67 RETRIEVE_SURROUNDING,
68 DELETE_SURROUNDING,
69 LAST_SIGNAL
70};
71
72enum {
73 PROP_INPUT_PURPOSE = 1,
74 PROP_INPUT_HINTS,
75 LAST_PROPERTY
76};
77
78static guint im_context_signals[LAST_SIGNAL] = { 0, };
79static GParamSpec *properties[LAST_PROPERTY] = { NULL, };
80
81typedef struct _GtkIMContextPrivate GtkIMContextPrivate;
82struct _GtkIMContextPrivate {
83 GtkInputPurpose purpose;
84 GtkInputHints hints;
85};
86
87static void gtk_im_context_real_get_preedit_string (GtkIMContext *context,
88 char **str,
89 PangoAttrList **attrs,
90 int *cursor_pos);
91static gboolean gtk_im_context_real_filter_keypress (GtkIMContext *context,
92 GdkEvent *event);
93
94static gboolean gtk_im_context_real_get_surrounding_with_selection
95 (GtkIMContext *context,
96 char **text,
97 int *cursor_index,
98 int *selection_bound);
99static void gtk_im_context_real_set_surrounding_with_selection
100 (GtkIMContext *context,
101 const char *text,
102 int len,
103 int cursor_index,
104 int selection_bound);
105
106static void gtk_im_context_get_property (GObject *obj,
107 guint property_id,
108 GValue *value,
109 GParamSpec *pspec);
110static void gtk_im_context_set_property (GObject *obj,
111 guint property_id,
112 const GValue *value,
113 GParamSpec *pspec);
114
115
116G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkIMContext, gtk_im_context, G_TYPE_OBJECT)
117
118/**
119 * GtkIMContextClass:
120 * @preedit_start: Default handler of the [signal@Gtk.IMContext::preedit-start] signal.
121 * @preedit_end: Default handler of the [signal@Gtk.IMContext::preedit-end] signal.
122 * @preedit_changed: Default handler of the [signal@Gtk.IMContext::preedit-changed]
123 * signal.
124 * @commit: Default handler of the [signal@Gtk.IMContext::commit] signal.
125 * @retrieve_surrounding: Default handler of the
126 * [signal@Gtk.IMContext::retrieve-surrounding] signal.
127 * @delete_surrounding: Default handler of the
128 * [signal@Gtk.IMContext::delete-surrounding] signal.
129 * @set_client_widget: Called via [method@Gtk.IMContext.set_client_widget] when
130 * the input window where the entered text will appear changes. Override this
131 * to keep track of the current input window, for instance for the purpose of
132 * positioning a status display of your input method.
133 * @get_preedit_string: Called via [method@Gtk.IMContext.get_preedit_string]
134 * to retrieve the text currently being preedited for display at the cursor
135 * position. Any input method which composes complex characters or any
136 * other compositions from multiple sequential key presses should override
137 * this method to provide feedback.
138 * @filter_keypress: Called via [method@Gtk.IMContext.filter_keypress] on every
139 * key press or release event. Every non-trivial input method needs to
140 * override this in order to implement the mapping from key events to text.
141 * A return value of %TRUE indicates to the caller that the event was
142 * consumed by the input method. In that case, the [signal@Gtk.IMContext::commit]
143 * signal should be emitted upon completion of a key sequence to pass the
144 * resulting text back to the input widget. Alternatively, %FALSE may be
145 * returned to indicate that the event wasn’t handled by the input method.
146 * If a builtin mapping exists for the key, it is used to produce a
147 * character.
148 * @focus_in: Called via [method@Gtk.IMContext.focus_in] when the input widget
149 * has gained focus. May be overridden to keep track of the current focus.
150 * @focus_out: Called via [method@Gtk.IMContext.focus_out] when the input widget
151 * has lost focus. May be overridden to keep track of the current focus.
152 * @reset: Called via [method@Gtk.IMContext.reset] to signal a change such as a
153 * change in cursor position. An input method that implements preediting
154 * should override this method to clear the preedit state on reset.
155 * @set_cursor_location: Called via [method@Gtk.IMContext.set_cursor_location]
156 * to inform the input method of the current cursor location relative to
157 * the client window. May be overridden to implement the display of popup
158 * windows at the cursor position.
159 * @set_use_preedit: Called via [method@Gtk.IMContext.set_use_preedit] to control
160 * the use of the preedit string. Override this to display feedback by some
161 * other means if turned off.
162 * @set_surrounding: Called via [method@Gtk.IMContext.set_surrounding] in
163 * response to [signal@Gtk.IMContext::retrieve-surrounding] signal to update
164 * the input method’s idea of the context around the cursor. It is not necessary
165 * to override this method even with input methods which implement
166 * context-dependent behavior. The base implementation is sufficient for
167 * [method@Gtk.IMContext.get_surrounding] to work.
168 * @get_surrounding: Called via [method@Gtk.IMContext.get_surrounding] to update
169 * the context around the cursor location. It is not necessary to override this
170 * method even with input methods which implement context-dependent behavior.
171 * The base implementation emits [signal@Gtk.IMContext::retrieve-surrounding]
172 * and records the context received by the subsequent invocation of
173 * [vfunc@Gtk.IMContext.get_surrounding].
174 * @set_surrounding_with_selection: Called via
175 * [method@Gtk.IMContext.set_surrounding_with_selection] in response to the
176 * [signal@Gtk.IMContext::retrieve-surrounding] signal to update the input
177 * method’s idea of the context around the cursor. It is not necessary to
178 * override this method even with input methods which implement
179 * context-dependent behavior. The base implementation is sufficient for
180 * [method@Gtk.IMContext.get_surrounding] to work.
181 * @get_surrounding_with_selection: Called via
182 * [method@Gtk.IMContext.get_surrounding_with_selection] to update the
183 * context around the cursor location. It is not necessary to override
184 * this method even with input methods which implement context-dependent
185 * behavior. The base implementation emits
186 * [signal@Gtk.IMContext::retrieve-surrounding] and records the context
187 * received by the subsequent invocation of [vfunc@Gtk.IMContext.get_surrounding].
188 */
189static void
190gtk_im_context_class_init (GtkIMContextClass *klass)
191{
192 GObjectClass *object_class = G_OBJECT_CLASS (klass);
193
194 object_class->get_property = gtk_im_context_get_property;
195 object_class->set_property = gtk_im_context_set_property;
196
197 klass->get_preedit_string = gtk_im_context_real_get_preedit_string;
198 klass->filter_keypress = gtk_im_context_real_filter_keypress;
199 klass->get_surrounding_with_selection = gtk_im_context_real_get_surrounding_with_selection;
200 klass->set_surrounding_with_selection = gtk_im_context_real_set_surrounding_with_selection;
201
202 /**
203 * GtkIMContext::preedit-start:
204 * @context: the object on which the signal is emitted
205 *
206 * The ::preedit-start signal is emitted when a new preediting sequence
207 * starts.
208 */
209 im_context_signals[PREEDIT_START] =
210 g_signal_new (I_("preedit-start"),
211 G_TYPE_FROM_CLASS (klass),
212 signal_flags: G_SIGNAL_RUN_LAST,
213 G_STRUCT_OFFSET (GtkIMContextClass, preedit_start),
214 NULL, NULL,
215 NULL,
216 G_TYPE_NONE, n_params: 0);
217
218 /**
219 * GtkIMContext::preedit-end:
220 * @context: the object on which the signal is emitted
221 *
222 * The ::preedit-end signal is emitted when a preediting sequence
223 * has been completed or canceled.
224 */
225 im_context_signals[PREEDIT_END] =
226 g_signal_new (I_("preedit-end"),
227 G_TYPE_FROM_CLASS (klass),
228 signal_flags: G_SIGNAL_RUN_LAST,
229 G_STRUCT_OFFSET (GtkIMContextClass, preedit_end),
230 NULL, NULL,
231 NULL,
232 G_TYPE_NONE, n_params: 0);
233
234 /**
235 * GtkIMContext::preedit-changed:
236 * @context: the object on which the signal is emitted
237 *
238 * The ::preedit-changed signal is emitted whenever the preedit sequence
239 * currently being entered has changed.
240 *
241 * It is also emitted at the end of a preedit sequence, in which case
242 * [method@Gtk.IMContext.get_preedit_string] returns the empty string.
243 */
244 im_context_signals[PREEDIT_CHANGED] =
245 g_signal_new (I_("preedit-changed"),
246 G_TYPE_FROM_CLASS (klass),
247 signal_flags: G_SIGNAL_RUN_LAST,
248 G_STRUCT_OFFSET (GtkIMContextClass, preedit_changed),
249 NULL, NULL,
250 NULL,
251 G_TYPE_NONE, n_params: 0);
252
253 /**
254 * GtkIMContext::commit:
255 * @context: the object on which the signal is emitted
256 * @str: the completed character(s) entered by the user
257 *
258 * The ::commit signal is emitted when a complete input sequence
259 * has been entered by the user.
260 *
261 * If the commit comes after a preediting sequence, the
262 * ::commit signal is emitted after ::preedit-end.
263 *
264 * This can be a single character immediately after a key press or
265 * the final result of preediting.
266 */
267 im_context_signals[COMMIT] =
268 g_signal_new (I_("commit"),
269 G_TYPE_FROM_CLASS (klass),
270 signal_flags: G_SIGNAL_RUN_LAST,
271 G_STRUCT_OFFSET (GtkIMContextClass, commit),
272 NULL, NULL,
273 NULL,
274 G_TYPE_NONE, n_params: 1,
275 G_TYPE_STRING);
276
277 /**
278 * GtkIMContext::retrieve-surrounding:
279 * @context: the object on which the signal is emitted
280 *
281 * The ::retrieve-surrounding signal is emitted when the input method
282 * requires the context surrounding the cursor.
283 *
284 * The callback should set the input method surrounding context by
285 * calling the [method@Gtk.IMContext.set_surrounding] method.
286 *
287 * Returns: %TRUE if the signal was handled.
288 */
289 im_context_signals[RETRIEVE_SURROUNDING] =
290 g_signal_new (I_("retrieve-surrounding"),
291 G_TYPE_FROM_CLASS (klass),
292 signal_flags: G_SIGNAL_RUN_LAST,
293 G_STRUCT_OFFSET (GtkIMContextClass, retrieve_surrounding),
294 accumulator: _gtk_boolean_handled_accumulator, NULL,
295 c_marshaller: _gtk_marshal_BOOLEAN__VOID,
296 G_TYPE_BOOLEAN, n_params: 0);
297 g_signal_set_va_marshaller (signal_id: im_context_signals[RETRIEVE_SURROUNDING],
298 G_TYPE_FROM_CLASS (klass),
299 va_marshaller: _gtk_marshal_BOOLEAN__VOIDv);
300
301 /**
302 * GtkIMContext::delete-surrounding:
303 * @context: the object on which the signal is emitted
304 * @offset: the character offset from the cursor position of the text
305 * to be deleted. A negative value indicates a position before
306 * the cursor.
307 * @n_chars: the number of characters to be deleted
308 *
309 * The ::delete-surrounding signal is emitted when the input method
310 * needs to delete all or part of the context surrounding the cursor.
311 *
312 * Returns: %TRUE if the signal was handled.
313 */
314 im_context_signals[DELETE_SURROUNDING] =
315 g_signal_new (I_("delete-surrounding"),
316 G_TYPE_FROM_CLASS (klass),
317 signal_flags: G_SIGNAL_RUN_LAST,
318 G_STRUCT_OFFSET (GtkIMContextClass, delete_surrounding),
319 accumulator: _gtk_boolean_handled_accumulator, NULL,
320 c_marshaller: _gtk_marshal_BOOLEAN__INT_INT,
321 G_TYPE_BOOLEAN, n_params: 2,
322 G_TYPE_INT,
323 G_TYPE_INT);
324 g_signal_set_va_marshaller (signal_id: im_context_signals[DELETE_SURROUNDING],
325 G_TYPE_FROM_CLASS (klass),
326 va_marshaller: _gtk_marshal_BOOLEAN__INT_INTv);
327
328 /**
329 * GtkIMContext:input-purpose:
330 *
331 * The purpose of the text field that the `GtkIMContext is connected to.
332 *
333 * This property can be used by on-screen keyboards and other input
334 * methods to adjust their behaviour.
335 */
336 properties[PROP_INPUT_PURPOSE] =
337 g_param_spec_enum (name: "input-purpose",
338 P_("Purpose"),
339 P_("Purpose of the text field"),
340 enum_type: GTK_TYPE_INPUT_PURPOSE,
341 default_value: GTK_INPUT_PURPOSE_FREE_FORM,
342 flags: G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
343
344 /**
345 * GtkIMContext:input-hints:
346 *
347 * Additional hints that allow input methods to fine-tune
348 * their behaviour.
349 */
350 properties[PROP_INPUT_HINTS] =
351 g_param_spec_flags (name: "input-hints",
352 P_("hints"),
353 P_("Hints for the text field behaviour"),
354 flags_type: GTK_TYPE_INPUT_HINTS,
355 default_value: GTK_INPUT_HINT_NONE,
356 flags: G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
357
358 g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROPERTY, pspecs: properties);
359}
360
361static void
362gtk_im_context_init (GtkIMContext *im_context)
363{
364}
365
366static void
367gtk_im_context_real_get_preedit_string (GtkIMContext *context,
368 char **str,
369 PangoAttrList **attrs,
370 int *cursor_pos)
371{
372 if (str)
373 *str = g_strdup (str: "");
374 if (attrs)
375 *attrs = pango_attr_list_new ();
376 if (cursor_pos)
377 *cursor_pos = 0;
378}
379
380static gboolean
381gtk_im_context_real_filter_keypress (GtkIMContext *context,
382 GdkEvent *event)
383{
384 return FALSE;
385}
386
387typedef struct
388{
389 char *text;
390 int cursor_index;
391 int selection_bound;
392} SurroundingInfo;
393
394static void
395gtk_im_context_real_set_surrounding_with_selection (GtkIMContext *context,
396 const char *text,
397 int len,
398 int cursor_index,
399 int selection_bound)
400{
401 SurroundingInfo *info = g_object_get_data (G_OBJECT (context),
402 key: "gtk-im-surrounding-info");
403
404 if (info)
405 {
406 g_free (mem: info->text);
407 info->text = g_strndup (str: text, n: len);
408 info->cursor_index = cursor_index;
409 info->selection_bound = selection_bound;
410 }
411}
412
413static gboolean
414gtk_im_context_real_get_surrounding_with_selection (GtkIMContext *context,
415 char **text,
416 int *cursor_index,
417 int *selection_bound)
418{
419 gboolean result;
420 gboolean info_is_local = FALSE;
421 SurroundingInfo local_info = { NULL, 0 };
422 SurroundingInfo *info;
423
424 info = g_object_get_data (G_OBJECT (context), key: "gtk-im-surrounding-info");
425 if (!info)
426 {
427 info = &local_info;
428 g_object_set_data (G_OBJECT (context), I_("gtk-im-surrounding-info"), data: info);
429 info_is_local = TRUE;
430 }
431
432 g_signal_emit (instance: context,
433 signal_id: im_context_signals[RETRIEVE_SURROUNDING], detail: 0,
434 &result);
435
436 if (result)
437 {
438 *text = g_strdup (str: info->text ? info->text : "");
439 *cursor_index = info->cursor_index;
440 *selection_bound = info->selection_bound;
441 }
442 else
443 {
444 *text = NULL;
445 *cursor_index = 0;
446 *selection_bound = 0;
447 }
448
449 if (info_is_local)
450 {
451 g_free (mem: info->text);
452 g_object_set_data (G_OBJECT (context), I_("gtk-im-surrounding-info"), NULL);
453 }
454
455 return result;
456}
457
458/**
459 * gtk_im_context_set_client_widget:
460 * @context: a `GtkIMContext`
461 * @widget: (nullable): the client widget. This may be %NULL to indicate
462 * that the previous client widget no longer exists.
463 *
464 * Set the client widget for the input context.
465 *
466 * This is the `GtkWidget` holding the input focus. This widget is
467 * used in order to correctly position status windows, and may
468 * also be used for purposes internal to the input method.
469 */
470void
471gtk_im_context_set_client_widget (GtkIMContext *context,
472 GtkWidget *widget)
473{
474 GtkIMContextClass *klass;
475
476 g_return_if_fail (GTK_IS_IM_CONTEXT (context));
477
478 klass = GTK_IM_CONTEXT_GET_CLASS (context);
479 if (klass->set_client_widget)
480 klass->set_client_widget (context, widget);
481}
482
483/**
484 * gtk_im_context_get_preedit_string:
485 * @context: a `GtkIMContext`
486 * @str: (out) (transfer full): location to store the retrieved
487 * string. The string retrieved must be freed with g_free().
488 * @attrs: (out) (transfer full): location to store the retrieved
489 * attribute list. When you are done with this list, you
490 * must unreference it with [method@Pango.AttrList.unref].
491 * @cursor_pos: (out): location to store position of cursor
492 * (in characters) within the preedit string.
493 *
494 * Retrieve the current preedit string for the input context,
495 * and a list of attributes to apply to the string.
496 *
497 * This string should be displayed inserted at the insertion point.
498 */
499void
500gtk_im_context_get_preedit_string (GtkIMContext *context,
501 char **str,
502 PangoAttrList **attrs,
503 int *cursor_pos)
504{
505 GtkIMContextClass *klass;
506
507 g_return_if_fail (GTK_IS_IM_CONTEXT (context));
508
509 klass = GTK_IM_CONTEXT_GET_CLASS (context);
510 klass->get_preedit_string (context, str, attrs, cursor_pos);
511 g_return_if_fail (str == NULL || g_utf8_validate (*str, -1, NULL));
512}
513
514/**
515 * gtk_im_context_filter_keypress:
516 * @context: a `GtkIMContext`
517 * @event: the key event
518 *
519 * Allow an input method to internally handle key press and release
520 * events.
521 *
522 * If this function returns %TRUE, then no further processing
523 * should be done for this key event.
524 *
525 * Returns: %TRUE if the input method handled the key event.
526 */
527gboolean
528gtk_im_context_filter_keypress (GtkIMContext *context,
529 GdkEvent *key)
530{
531 GtkIMContextClass *klass;
532
533 g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), FALSE);
534 g_return_val_if_fail (key != NULL, FALSE);
535
536 klass = GTK_IM_CONTEXT_GET_CLASS (context);
537 return klass->filter_keypress (context, key);
538}
539
540/**
541 * gtk_im_context_filter_key:
542 * @context: a `GtkIMContext`
543 * @press: whether to forward a key press or release event
544 * @surface: the surface the event is for
545 * @device: the device that the event is for
546 * @time: the timestamp for the event
547 * @keycode: the keycode for the event
548 * @state: modifier state for the event
549 * @group: the active keyboard group for the event
550 *
551 * Allow an input method to forward key press and release events
552 * to another input method without necessarily having a `GdkEvent`
553 * available.
554 *
555 * Returns: %TRUE if the input method handled the key event.
556 */
557gboolean
558gtk_im_context_filter_key (GtkIMContext *context,
559 gboolean press,
560 GdkSurface *surface,
561 GdkDevice *device,
562 guint32 time,
563 guint keycode,
564 GdkModifierType state,
565 int group)
566{
567 GdkTranslatedKey translated, no_lock;
568 GdkEvent *key;
569 gboolean ret;
570 guint keyval;
571 int layout;
572 int level;
573 GdkModifierType consumed;
574
575 g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), FALSE);
576
577 if (!gdk_display_translate_key (display: gdk_surface_get_display (surface),
578 keycode,
579 state,
580 group,
581 keyval: &keyval,
582 effective_group: &layout,
583 level: &level,
584 consumed: &consumed))
585 return FALSE;
586
587 translated.keyval = keyval;
588 translated.layout = layout;
589 translated.level = level;
590 translated.consumed = consumed;
591
592 if (!gdk_display_translate_key (display: gdk_surface_get_display (surface),
593 keycode,
594 state: state & ~GDK_LOCK_MASK,
595 group,
596 keyval: &keyval,
597 effective_group: &layout,
598 level: &level,
599 consumed: &consumed))
600 return FALSE;
601
602 no_lock.keyval = keyval;
603 no_lock.layout = layout;
604 no_lock.level = level;
605 no_lock.consumed = consumed;
606
607 key = gdk_key_event_new (type: press ? GDK_KEY_PRESS : GDK_KEY_RELEASE,
608 surface,
609 device,
610 time,
611 keycode,
612 modifiers: state,
613 FALSE, /* FIXME */
614 translated: &translated,
615 no_lock: &no_lock);
616
617 ret = GTK_IM_CONTEXT_GET_CLASS (context)->filter_keypress (context, key);
618
619 gdk_event_unref (event: key);
620
621 return ret;
622}
623
624/**
625 * gtk_im_context_focus_in:
626 * @context: a `GtkIMContext`
627 *
628 * Notify the input method that the widget to which this
629 * input context corresponds has gained focus.
630 *
631 * The input method may, for example, change the displayed
632 * feedback to reflect this change.
633 */
634void
635gtk_im_context_focus_in (GtkIMContext *context)
636{
637 GtkIMContextClass *klass;
638
639 g_return_if_fail (GTK_IS_IM_CONTEXT (context));
640
641 klass = GTK_IM_CONTEXT_GET_CLASS (context);
642 if (klass->focus_in)
643 klass->focus_in (context);
644}
645
646/**
647 * gtk_im_context_focus_out:
648 * @context: a `GtkIMContext`
649 *
650 * Notify the input method that the widget to which this
651 * input context corresponds has lost focus.
652 *
653 * The input method may, for example, change the displayed
654 * feedback or reset the contexts state to reflect this change.
655 */
656void
657gtk_im_context_focus_out (GtkIMContext *context)
658{
659 GtkIMContextClass *klass;
660
661 g_return_if_fail (GTK_IS_IM_CONTEXT (context));
662
663 klass = GTK_IM_CONTEXT_GET_CLASS (context);
664 if (klass->focus_out)
665 klass->focus_out (context);
666}
667
668/**
669 * gtk_im_context_reset:
670 * @context: a `GtkIMContext`
671 *
672 * Notify the input method that a change such as a change in cursor
673 * position has been made.
674 *
675 * This will typically cause the input method to clear the preedit state.
676 */
677void
678gtk_im_context_reset (GtkIMContext *context)
679{
680 GtkIMContextClass *klass;
681
682 g_return_if_fail (GTK_IS_IM_CONTEXT (context));
683
684 klass = GTK_IM_CONTEXT_GET_CLASS (context);
685 if (klass->reset)
686 klass->reset (context);
687}
688
689
690/**
691 * gtk_im_context_set_cursor_location:
692 * @context: a `GtkIMContext`
693 * @area: new location
694 *
695 * Notify the input method that a change in cursor
696 * position has been made.
697 *
698 * The location is relative to the client widget.
699 */
700void
701gtk_im_context_set_cursor_location (GtkIMContext *context,
702 const GdkRectangle *area)
703{
704 GtkIMContextClass *klass;
705
706 g_return_if_fail (GTK_IS_IM_CONTEXT (context));
707
708 klass = GTK_IM_CONTEXT_GET_CLASS (context);
709 if (klass->set_cursor_location)
710 klass->set_cursor_location (context, (GdkRectangle *) area);
711}
712
713/**
714 * gtk_im_context_set_use_preedit:
715 * @context: a `GtkIMContext`
716 * @use_preedit: whether the IM context should use the preedit string.
717 *
718 * Sets whether the IM context should use the preedit string
719 * to display feedback.
720 *
721 * If @use_preedit is %FALSE (default is %TRUE), then the IM context
722 * may use some other method to display feedback, such as displaying
723 * it in a child of the root window.
724 */
725void
726gtk_im_context_set_use_preedit (GtkIMContext *context,
727 gboolean use_preedit)
728{
729 GtkIMContextClass *klass;
730
731 g_return_if_fail (GTK_IS_IM_CONTEXT (context));
732
733 klass = GTK_IM_CONTEXT_GET_CLASS (context);
734 if (klass->set_use_preedit)
735 klass->set_use_preedit (context, use_preedit);
736}
737
738/**
739 * gtk_im_context_set_surrounding:
740 * @context: a `GtkIMContext`
741 * @text: text surrounding the insertion point, as UTF-8.
742 * the preedit string should not be included within @text
743 * @len: the length of @text, or -1 if @text is nul-terminated
744 * @cursor_index: the byte index of the insertion cursor within @text.
745 *
746 * Sets surrounding context around the insertion point and preedit
747 * string.
748 *
749 * This function is expected to be called in response to the
750 * [signal@Gtk.IMContext::retrieve-surrounding] signal, and will
751 * likely have no effect if called at other times.
752 *
753 * Deprecated: 4.2: Use [method@Gtk.IMContext.set_surrounding_with_selection] instead
754 */
755void
756gtk_im_context_set_surrounding (GtkIMContext *context,
757 const char *text,
758 int len,
759 int cursor_index)
760{
761 gtk_im_context_set_surrounding_with_selection (context, text, len, cursor_index, anchor_index: cursor_index);
762}
763
764/**
765 * gtk_im_context_set_surrounding_with_selection:
766 * @context: a `GtkIMContext`
767 * @text: text surrounding the insertion point, as UTF-8.
768 * the preedit string should not be included within @text
769 * @len: the length of @text, or -1 if @text is nul-terminated
770 * @cursor_index: the byte index of the insertion cursor within @text
771 * @anchor_index: the byte index of the selection bound within @text
772 *
773 * Sets surrounding context around the insertion point and preedit
774 * string. This function is expected to be called in response to the
775 * [signal@Gtk.IMContext::retrieve_surrounding] signal, and will likely
776 * have no effect if called at other times.
777 *
778 * Since: 4.2
779 */
780void
781gtk_im_context_set_surrounding_with_selection (GtkIMContext *context,
782 const char *text,
783 int len,
784 int cursor_index,
785 int anchor_index)
786{
787 GtkIMContextClass *klass;
788
789 g_return_if_fail (GTK_IS_IM_CONTEXT (context));
790 g_return_if_fail (text != NULL || len == 0);
791
792 if (text == NULL && len == 0)
793 text = "";
794 if (len < 0)
795 len = strlen (s: text);
796
797 g_return_if_fail (cursor_index >= 0 && cursor_index <= len);
798
799 klass = GTK_IM_CONTEXT_GET_CLASS (context);
800 if (klass->set_surrounding_with_selection)
801 klass->set_surrounding_with_selection (context, text, len, cursor_index, anchor_index);
802 else if (klass->set_surrounding)
803 klass->set_surrounding (context, text, len, cursor_index);
804}
805
806/**
807 * gtk_im_context_get_surrounding:
808 * @context: a `GtkIMContext`
809 * @text: (out) (transfer full): location to store a UTF-8 encoded
810 * string of text holding context around the insertion point.
811 * If the function returns %TRUE, then you must free the result
812 * stored in this location with g_free().
813 * @cursor_index: (out): location to store byte index of the insertion
814 * cursor within @text.
815 *
816 * Retrieves context around the insertion point.
817 *
818 * Input methods typically want context in order to constrain input text
819 * based on existing text; this is important for languages such as Thai
820 * where only some sequences of characters are allowed.
821 *
822 * This function is implemented by emitting the
823 * [signal@Gtk.IMContext::retrieve-surrounding] signal on the input method;
824 * in response to this signal, a widget should provide as much context as
825 * is available, up to an entire paragraph, by calling
826 * [method@Gtk.IMContext.set_surrounding].
827 *
828 * Note that there is no obligation for a widget to respond to the
829 * `::retrieve-surrounding` signal, so input methods must be prepared to
830 * function without context.
831 *
832 * Returns: `TRUE` if surrounding text was provided; in this case
833 * you must free the result stored in `text`.
834 *
835 * Deprecated: 4.2: Use [method@Gtk.IMContext.get_surrounding_with_selection] instead.
836 */
837gboolean
838gtk_im_context_get_surrounding (GtkIMContext *context,
839 char **text,
840 int *cursor_index)
841{
842 return gtk_im_context_get_surrounding_with_selection (context,
843 text,
844 cursor_index,
845 NULL);
846}
847
848/**
849 * gtk_im_context_get_surrounding_with_selection:
850 * @context: a `GtkIMContext`
851 * @text: (out) (transfer full): location to store a UTF-8 encoded
852 * string of text holding context around the insertion point.
853 * If the function returns %TRUE, then you must free the result
854 * stored in this location with g_free().
855 * @cursor_index: (out): location to store byte index of the insertion
856 * cursor within @text.
857 * @anchor_index: (out): location to store byte index of the selection
858 * bound within @text
859 *
860 * Retrieves context around the insertion point.
861 *
862 * Input methods typically want context in order to constrain input
863 * text based on existing text; this is important for languages such
864 * as Thai where only some sequences of characters are allowed.
865 *
866 * This function is implemented by emitting the
867 * [signal@Gtk.IMContext::retrieve-surrounding] signal on the input method;
868 * in response to this signal, a widget should provide as much context as
869 * is available, up to an entire paragraph, by calling
870 * [method@Gtk.IMContext.set_surrounding_with_selection].
871 *
872 * Note that there is no obligation for a widget to respond to the
873 * `::retrieve-surrounding` signal, so input methods must be prepared to
874 * function without context.
875 *
876 * Returns: `TRUE` if surrounding text was provided; in this case
877 * you must free the result stored in `text`.
878 *
879 * Since: 4.2
880 */
881gboolean
882gtk_im_context_get_surrounding_with_selection (GtkIMContext *context,
883 char **text,
884 int *cursor_index,
885 int *anchor_index)
886{
887 GtkIMContextClass *klass;
888 char *local_text = NULL;
889 int local_index;
890 gboolean result = FALSE;
891
892 g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), FALSE);
893
894 klass = GTK_IM_CONTEXT_GET_CLASS (context);
895 if (klass->get_surrounding_with_selection)
896 result = klass->get_surrounding_with_selection
897 (context,
898 text ? text : &local_text,
899 cursor_index ? cursor_index : &local_index,
900 anchor_index ? anchor_index : &local_index);
901 else if (klass->get_surrounding)
902 {
903 result = klass->get_surrounding (context,
904 text ? text : &local_text,
905 &local_index);
906 if (cursor_index)
907 *cursor_index = local_index;
908 if (anchor_index)
909 *anchor_index = local_index;
910 }
911
912 if (result)
913 g_free (mem: local_text);
914
915 return result;
916}
917
918/**
919 * gtk_im_context_delete_surrounding:
920 * @context: a `GtkIMContext`
921 * @offset: offset from cursor position in chars;
922 * a negative value means start before the cursor.
923 * @n_chars: number of characters to delete.
924 *
925 * Asks the widget that the input context is attached to delete
926 * characters around the cursor position by emitting the
927 * `::delete_surrounding` signal.
928 *
929 * Note that @offset and @n_chars are in characters not in bytes
930 * which differs from the usage other places in `GtkIMContext`.
931 *
932 * In order to use this function, you should first call
933 * [method@Gtk.IMContext.get_surrounding] to get the current context,
934 * and call this function immediately afterwards to make sure that you
935 * know what you are deleting. You should also account for the fact
936 * that even if the signal was handled, the input context might not
937 * have deleted all the characters that were requested to be deleted.
938 *
939 * This function is used by an input method that wants to make
940 * subsitutions in the existing text in response to new input.
941 * It is not useful for applications.
942 *
943 * Returns: %TRUE if the signal was handled.
944 */
945gboolean
946gtk_im_context_delete_surrounding (GtkIMContext *context,
947 int offset,
948 int n_chars)
949{
950 gboolean result;
951
952 g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), FALSE);
953
954 g_signal_emit (instance: context,
955 signal_id: im_context_signals[DELETE_SURROUNDING], detail: 0,
956 offset, n_chars, &result);
957
958 return result;
959}
960
961static void
962gtk_im_context_get_property (GObject *obj,
963 guint property_id,
964 GValue *value,
965 GParamSpec *pspec)
966{
967 GtkIMContextPrivate *priv = gtk_im_context_get_instance_private (GTK_IM_CONTEXT (obj));
968
969 switch (property_id)
970 {
971 case PROP_INPUT_PURPOSE:
972 g_value_set_enum (value, v_enum: priv->purpose);
973 break;
974 case PROP_INPUT_HINTS:
975 g_value_set_flags (value, v_flags: priv->hints);
976 break;
977 default:
978 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
979 break;
980 }
981}
982
983static void
984gtk_im_context_set_property (GObject *obj,
985 guint property_id,
986 const GValue *value,
987 GParamSpec *pspec)
988{
989 GtkIMContextPrivate *priv = gtk_im_context_get_instance_private (GTK_IM_CONTEXT (obj));
990
991 switch (property_id)
992 {
993 case PROP_INPUT_PURPOSE:
994 if (priv->purpose != g_value_get_enum (value))
995 {
996 priv->purpose = g_value_get_enum (value);
997 g_object_notify_by_pspec (object: obj, pspec);
998 }
999 break;
1000 case PROP_INPUT_HINTS:
1001 if (priv->hints != g_value_get_flags (value))
1002 {
1003 priv->hints = g_value_get_flags (value);
1004 g_object_notify_by_pspec (object: obj, pspec);
1005 }
1006 break;
1007 default:
1008 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
1009 break;
1010 }
1011}
1012

source code of gtk/gtk/gtkimcontext.c