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