gtkimcontext.c 35.3 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
2
3
4
 * Copyright (C) 2000 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
7
8
9
10
11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * 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
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16
17
 */

18
#include "config.h"
Manish Singh's avatar
Manish Singh committed
19
#include <string.h>
20
#include "gtkimcontext.h"
21
#include "gtkprivate.h"
22
#include "gtktypebuiltins.h"
23
#include "gtkmarshalers.h"
Matthias Clasen's avatar
Matthias Clasen committed
24
#include "gtkintl.h"
25

26
27
#include "gdk/gdkeventsprivate.h"

28
/**
Matthias Clasen's avatar
Matthias Clasen committed
29
30
31
32
33
34
35
36
37
38
 * GtkIMContext:
 *
 * `GtkIMContext` defines the interface for GTK input methods.
 *
 * `GtkIMContext` is used by GTK text input widgets like `GtkText`
 * to map from key events to Unicode character strings.
 *
 * An input method may consume multiple key events in sequence before finally
 * outputting the composed result. This is called *preediting*, and an input
 * method may provide feedback about this process by displaying the intermediate
Matthias Clasen's avatar
Matthias Clasen committed
39
40
41
 * composition states as preedit text. To do so, the `GtkIMContext` will emit
 * [signal@Gtk.IMContext::preedit-start], [signal@Gtk.IMContext::preedit-changed]
 * and [signal@Gtk.IMContext::preedit-end] signals.
Matthias Clasen's avatar
Matthias Clasen committed
42
 *
Matthias Clasen's avatar
Matthias Clasen committed
43
44
45
 * For instance, the built-in GTK input method [class@Gtk.IMContextSimple]
 * implements the input of arbitrary Unicode code points by holding down the
 * <kbd>Control</kbd> and <kbd>Shift</kbd> keys and then typing <kbd>u</kbd>
Matthias Clasen's avatar
Matthias Clasen committed
46
47
48
49
50
51
52
 * followed by the hexadecimal digits of the code point. When releasing the
 * <kbd>Control</kbd> and <kbd>Shift</kbd> keys, preediting ends and the
 * character is inserted as text. For example,
 *
 *     Ctrl+Shift+u 2 0 A C
 *
 * results in the € sign.
53
 *
Matthias Clasen's avatar
Matthias Clasen committed
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
Matthias Clasen's avatar
Matthias Clasen committed
56
 * provides a `GIOExtension` for the extension point named "gtk-im-module".
Matthias Clasen's avatar
Matthias Clasen committed
57
58
59
 *
 * To connect a widget to the users preferred input method, you should use
 * [class@Gtk.IMMulticontext].
60
61
 */

62
63
64
65
66
enum {
  PREEDIT_START,
  PREEDIT_END,
  PREEDIT_CHANGED,
  COMMIT,
67
68
  RETRIEVE_SURROUNDING,
  DELETE_SURROUNDING,
69
70
71
  LAST_SIGNAL
};

72
73
74
75
76
77
78
79
80
81
82
83
84
85
enum {
  PROP_INPUT_PURPOSE = 1,
  PROP_INPUT_HINTS,
  LAST_PROPERTY
};

static guint im_context_signals[LAST_SIGNAL] = { 0, };
static GParamSpec *properties[LAST_PROPERTY] = { NULL, };

typedef struct _GtkIMContextPrivate GtkIMContextPrivate;
struct _GtkIMContextPrivate {
  GtkInputPurpose purpose;
  GtkInputHints hints;
};
86

87
static void     gtk_im_context_real_get_preedit_string (GtkIMContext   *context,
Benjamin Otte's avatar
Benjamin Otte committed
88
							char          **str,
89
							PangoAttrList **attrs,
Benjamin Otte's avatar
Benjamin Otte committed
90
							int            *cursor_pos);
91
static gboolean gtk_im_context_real_filter_keypress    (GtkIMContext   *context,
Matthias Clasen's avatar
Matthias Clasen committed
92
							GdkEvent       *event);
93
94
95
96
97
98
99
100
101
102
103
104

static gboolean gtk_im_context_real_get_surrounding_with_selection
                                                       (GtkIMContext   *context,
                                                        char          **text,
                                                        int            *cursor_index,
                                                        int            *selection_bound);
static void     gtk_im_context_real_set_surrounding_with_selection
                                                       (GtkIMContext   *context,
                                                        const char     *text,
                                                        int             len,
                                                        int             cursor_index,
                                                        int             selection_bound);
105

106
107
108
109
110
111
112
113
114
115
static void     gtk_im_context_get_property            (GObject        *obj,
                                                        guint           property_id,
                                                        GValue         *value,
                                                        GParamSpec     *pspec);
static void     gtk_im_context_set_property            (GObject        *obj,
                                                        guint           property_id,
                                                        const GValue   *value,
                                                        GParamSpec     *pspec);


116
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkIMContext, gtk_im_context, G_TYPE_OBJECT)
117

118
119
/**
 * GtkIMContextClass:
Matthias Clasen's avatar
Matthias Clasen committed
120
121
122
 * @preedit_start: Default handler of the [signal@Gtk.IMContext::preedit-start] signal.
 * @preedit_end: Default handler of the [signal@Gtk.IMContext::preedit-end] signal.
 * @preedit_changed: Default handler of the [signal@Gtk.IMContext::preedit-changed]
123
 *   signal.
Matthias Clasen's avatar
Matthias Clasen committed
124
 * @commit: Default handler of the [signal@Gtk.IMContext::commit] signal.
125
 * @retrieve_surrounding: Default handler of the
Matthias Clasen's avatar
Matthias Clasen committed
126
 *   [signal@Gtk.IMContext::retrieve-surrounding] signal.
127
 * @delete_surrounding: Default handler of the
Matthias Clasen's avatar
Matthias Clasen committed
128
129
130
131
 *   [signal@Gtk.IMContext::delete-surrounding] signal.
 * @set_client_widget: Called via [method@Gtk.IMContext.set_client_widget] when
 *   the input window where the entered text will appear changes. Override this
 *   to keep track of the current input window, for instance for the purpose of
132
 *   positioning a status display of your input method.
Matthias Clasen's avatar
Matthias Clasen committed
133
134
 * @get_preedit_string: Called via [method@Gtk.IMContext.get_preedit_string]
 *   to retrieve the text currently being preedited for display at the cursor
135
136
137
 *   position. Any input method which composes complex characters or any
 *   other compositions from multiple sequential key presses should override
 *   this method to provide feedback.
Matthias Clasen's avatar
Matthias Clasen committed
138
 * @filter_keypress: Called via [method@Gtk.IMContext.filter_keypress] on every
139
140
141
 *   key press or release event. Every non-trivial input method needs to
 *   override this in order to implement the mapping from key events to text.
 *   A return value of %TRUE indicates to the caller that the event was
Matthias Clasen's avatar
Matthias Clasen committed
142
 *   consumed by the input method. In that case, the [signal@Gtk.IMContext::commit]
143
144
 *   signal should be emitted upon completion of a key sequence to pass the
 *   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
147
 *   If a builtin mapping exists for the key, it is used to produce a
 *   character.
Matthias Clasen's avatar
Matthias Clasen committed
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.
Matthias Clasen's avatar
Matthias Clasen committed
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.
Matthias Clasen's avatar
Matthias Clasen committed
152
 * @reset: Called via [method@Gtk.IMContext.reset] to signal a change such as a
153
154
 *   change in cursor position. An input method that implements preediting
 *   should override this method to clear the preedit state on reset.
Matthias Clasen's avatar
Matthias Clasen committed
155
 * @set_cursor_location: Called via [method@Gtk.IMContext.set_cursor_location]
156
157
158
 *   to inform the input method of the current cursor location relative to
 *   the client window. May be overridden to implement the display of popup
 *   windows at the cursor position.
Matthias Clasen's avatar
Matthias Clasen committed
159
 * @set_use_preedit: Called via [method@Gtk.IMContext.set_use_preedit] to control
160
161
 *   the use of the preedit string. Override this to display feedback by some
 *   other means if turned off.
Matthias Clasen's avatar
Matthias Clasen committed
162
163
164
165
 * @set_surrounding: Called via [method@Gtk.IMContext.set_surrounding] in
 *   response to [signal@Gtk.IMContext::retrieve-surrounding] signal to update
 *   the input method’s idea of the context around the cursor. It is not necessary
 *   to override this method even with input methods which implement
166
 *   context-dependent behavior. The base implementation is sufficient for
Matthias Clasen's avatar
Matthias Clasen committed
167
168
169
170
171
172
173
174
175
176
 *   [method@Gtk.IMContext.get_surrounding] to work.
 * @get_surrounding: Called via [method@Gtk.IMContext.get_surrounding] to update
 *   the context around the cursor location. It is not necessary to override this
 *   method even with input methods which implement context-dependent behavior.
 *   The base implementation emits [signal@Gtk.IMContext::retrieve-surrounding]
 *   and records the context received by the subsequent invocation of
 *   [vfunc@Gtk.IMContext.get_surrounding].
 * @set_surrounding_with_selection: Called via
 *   [method@Gtk.IMContext.set_surrounding_with_selection] in response to the
 *   [signal@Gtk.IMContext::retrieve-surrounding] signal to update the input
177
178
179
 *   method’s idea of the context around the cursor. It is not necessary to
 *   override this method even with input methods which implement
 *   context-dependent behavior. The base implementation is sufficient for
Matthias Clasen's avatar
Matthias Clasen committed
180
181
182
183
 *   [method@Gtk.IMContext.get_surrounding] to work.
 * @get_surrounding_with_selection: Called via
 *   [method@Gtk.IMContext.get_surrounding_with_selection] to update the
 *   context around the cursor location. It is not necessary to override
184
185
 *   this method even with input methods which implement context-dependent
 *   behavior. The base implementation emits
Matthias Clasen's avatar
Matthias Clasen committed
186
187
 *   [signal@Gtk.IMContext::retrieve-surrounding] and records the context
 *   received by the subsequent invocation of [vfunc@Gtk.IMContext.get_surrounding].
188
 */
189
190
191
static void
gtk_im_context_class_init (GtkIMContextClass *klass)
{
192
193
194
195
196
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->get_property = gtk_im_context_get_property;
  object_class->set_property = gtk_im_context_set_property;

197
198
  klass->get_preedit_string = gtk_im_context_real_get_preedit_string;
  klass->filter_keypress = gtk_im_context_real_filter_keypress;
199
200
  klass->get_surrounding_with_selection = gtk_im_context_real_get_surrounding_with_selection;
  klass->set_surrounding_with_selection = gtk_im_context_real_set_surrounding_with_selection;
201

202
203
204
205
206
207
208
  /**
   * GtkIMContext::preedit-start:
   * @context: the object on which the signal is emitted
   *
   * The ::preedit-start signal is emitted when a new preediting sequence
   * starts.
   */
209
  im_context_signals[PREEDIT_START] =
210
    g_signal_new (I_("preedit-start"),
Manish Singh's avatar
Manish Singh committed
211
		  G_TYPE_FROM_CLASS (klass),
212
213
214
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkIMContextClass, preedit_start),
		  NULL, NULL,
215
		  NULL,
216
		  G_TYPE_NONE, 0);
Matthias Clasen's avatar
Matthias Clasen committed
217

218
219
220
221
222
223
224
  /**
   * GtkIMContext::preedit-end:
   * @context: the object on which the signal is emitted
   *
   * The ::preedit-end signal is emitted when a preediting sequence
   * has been completed or canceled.
   */
225
  im_context_signals[PREEDIT_END] =
226
    g_signal_new (I_("preedit-end"),
Manish Singh's avatar
Manish Singh committed
227
		  G_TYPE_FROM_CLASS (klass),
228
229
230
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkIMContextClass, preedit_end),
		  NULL, NULL,
231
		  NULL,
232
		  G_TYPE_NONE, 0);
Matthias Clasen's avatar
Matthias Clasen committed
233

234
235
236
237
238
  /**
   * GtkIMContext::preedit-changed:
   * @context: the object on which the signal is emitted
   *
   * The ::preedit-changed signal is emitted whenever the preedit sequence
Matthias Clasen's avatar
Matthias Clasen committed
239
240
241
242
   * currently being entered has changed.
   *
   * It is also emitted at the end of a preedit sequence, in which case
   * [method@Gtk.IMContext.get_preedit_string] returns the empty string.
243
   */
244
  im_context_signals[PREEDIT_CHANGED] =
245
    g_signal_new (I_("preedit-changed"),
Manish Singh's avatar
Manish Singh committed
246
		  G_TYPE_FROM_CLASS (klass),
247
248
249
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkIMContextClass, preedit_changed),
		  NULL, NULL,
250
		  NULL,
251
		  G_TYPE_NONE, 0);
Matthias Clasen's avatar
Matthias Clasen committed
252

253
254
255
256
257
258
  /**
   * GtkIMContext::commit:
   * @context: the object on which the signal is emitted
   * @str: the completed character(s) entered by the user
   *
   * The ::commit signal is emitted when a complete input sequence
Matthias Clasen's avatar
Matthias Clasen committed
259
260
261
262
263
264
265
   * has been entered by the user.
   *
   * If the commit comes after a preediting sequence, the
   * ::commit signal is emitted after ::preedit-end.
   *
   * This can be a single character immediately after a key press or
   * the final result of preediting.
266
   */
267
  im_context_signals[COMMIT] =
Matthias Clasen's avatar
Matthias Clasen committed
268
    g_signal_new (I_("commit"),
Manish Singh's avatar
Manish Singh committed
269
		  G_TYPE_FROM_CLASS (klass),
270
271
272
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkIMContextClass, commit),
		  NULL, NULL,
273
		  NULL,
274
275
		  G_TYPE_NONE, 1,
		  G_TYPE_STRING);
Matthias Clasen's avatar
Matthias Clasen committed
276

277
278
279
280
281
  /**
   * GtkIMContext::retrieve-surrounding:
   * @context: the object on which the signal is emitted
   *
   * The ::retrieve-surrounding signal is emitted when the input method
Matthias Clasen's avatar
Matthias Clasen committed
282
283
284
285
   * requires the context surrounding the cursor.
   *
   * The callback should set the input method surrounding context by
   * 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"),
Manish Singh's avatar
Manish Singh committed
291
                  G_TYPE_FROM_CLASS (klass),
292
293
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkIMContextClass, retrieve_surrounding),
294
                  _gtk_boolean_handled_accumulator, NULL,
295
                  _gtk_marshal_BOOLEAN__VOID,
296
                  G_TYPE_BOOLEAN, 0);
297
298
299
  g_signal_set_va_marshaller (im_context_signals[RETRIEVE_SURROUNDING],
                              G_TYPE_FROM_CLASS (klass),
                              _gtk_marshal_BOOLEAN__VOIDv);
Matthias Clasen's avatar
Matthias Clasen committed
300

301
302
303
  /**
   * GtkIMContext::delete-surrounding:
   * @context: the object on which the signal is emitted
Matthias Clasen's avatar
Matthias Clasen committed
304
305
306
   * @offset: the character offset from the cursor position of the text
   *   to be deleted. A negative value indicates a position before
   *   the cursor.
307
308
309
310
311
   * @n_chars: the number of characters to be deleted
   *
   * The ::delete-surrounding signal is emitted when the input method
   * needs to delete all or part of the context surrounding the cursor.
   *
312
   * Returns: %TRUE if the signal was handled.
313
   */
314
  im_context_signals[DELETE_SURROUNDING] =
315
    g_signal_new (I_("delete-surrounding"),
Manish Singh's avatar
Manish Singh committed
316
                  G_TYPE_FROM_CLASS (klass),
317
318
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkIMContextClass, delete_surrounding),
319
                  _gtk_boolean_handled_accumulator, NULL,
320
                  _gtk_marshal_BOOLEAN__INT_INT,
321
322
                  G_TYPE_BOOLEAN, 2,
                  G_TYPE_INT,
323
324
325
326
                  G_TYPE_INT);
  g_signal_set_va_marshaller (im_context_signals[DELETE_SURROUNDING],
                              G_TYPE_FROM_CLASS (klass),
                              _gtk_marshal_BOOLEAN__INT_INTv);
327

Matthias Clasen's avatar
Matthias Clasen committed
328
329
330
331
332
333
334
335
  /**
   * GtkIMContext:input-purpose:
   *
   * The purpose of the text field that the `GtkIMContext is connected to.
   *
   * This property can be used by on-screen keyboards and other input
   * methods to adjust their behaviour.
   */
336
  properties[PROP_INPUT_PURPOSE] =
337
    g_param_spec_enum ("input-purpose", NULL, NULL,
338
339
                         GTK_TYPE_INPUT_PURPOSE,
                         GTK_INPUT_PURPOSE_FREE_FORM,
340
                         G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
341

Matthias Clasen's avatar
Matthias Clasen committed
342
343
344
345
346
347
  /**
   * GtkIMContext:input-hints:
   *
   * Additional hints that allow input methods to fine-tune
   * their behaviour.
   */
348
  properties[PROP_INPUT_HINTS] =
349
    g_param_spec_flags ("input-hints", NULL, NULL,
350
351
                         GTK_TYPE_INPUT_HINTS,
                         GTK_INPUT_HINT_NONE,
352
                         G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
353
354

  g_object_class_install_properties (object_class, LAST_PROPERTY, properties);
355
356
357
358
359
360
361
362
363
}

static void
gtk_im_context_init (GtkIMContext *im_context)
{
}

static void
gtk_im_context_real_get_preedit_string (GtkIMContext       *context,
Benjamin Otte's avatar
Benjamin Otte committed
364
					char              **str,
Owen Taylor's avatar
Owen Taylor committed
365
					PangoAttrList     **attrs,
Benjamin Otte's avatar
Benjamin Otte committed
366
					int                *cursor_pos)
367
368
369
370
371
{
  if (str)
    *str = g_strdup ("");
  if (attrs)
    *attrs = pango_attr_list_new ();
Owen Taylor's avatar
Owen Taylor committed
372
373
  if (cursor_pos)
    *cursor_pos = 0;
374
375
376
}

static gboolean
Matthias Clasen's avatar
Matthias Clasen committed
377
378
gtk_im_context_real_filter_keypress (GtkIMContext *context,
				     GdkEvent     *event)
379
380
381
382
{
  return FALSE;
}

383
384
typedef struct
{
Benjamin Otte's avatar
Benjamin Otte committed
385
  char *text;
Benjamin Otte's avatar
Benjamin Otte committed
386
  int cursor_index;
387
  int selection_bound;
388
389
390
} SurroundingInfo;

static void
391
392
393
394
395
gtk_im_context_real_set_surrounding_with_selection (GtkIMContext  *context,
                                                    const char    *text,
                                                    int            len,
                                                    int            cursor_index,
                                                    int            selection_bound)
396
{
397
398
  SurroundingInfo *info = g_object_get_data (G_OBJECT (context),
                                             "gtk-im-surrounding-info");
399
400
401
402
403
404

  if (info)
    {
      g_free (info->text);
      info->text = g_strndup (text, len);
      info->cursor_index = cursor_index;
405
      info->selection_bound = selection_bound;
406
407
408
409
    }
}

static gboolean
410
411
412
413
gtk_im_context_real_get_surrounding_with_selection (GtkIMContext *context,
                                                    char        **text,
                                                    int          *cursor_index,
                                                    int          *selection_bound)
414
415
416
417
418
419
420
421
422
423
{
  gboolean result;
  gboolean info_is_local = FALSE;
  SurroundingInfo local_info = { NULL, 0 };
  SurroundingInfo *info;
  
  info = g_object_get_data (G_OBJECT (context), "gtk-im-surrounding-info");
  if (!info)
    {
      info = &local_info;
Matthias Clasen's avatar
Matthias Clasen committed
424
      g_object_set_data (G_OBJECT (context), I_("gtk-im-surrounding-info"), info);
425
426
427
428
429
430
431
432
433
434
435
      info_is_local = TRUE;
    }
  
  g_signal_emit (context,
		 im_context_signals[RETRIEVE_SURROUNDING], 0,
		 &result);

  if (result)
    {
      *text = g_strdup (info->text ? info->text : "");
      *cursor_index = info->cursor_index;
436
      *selection_bound = info->selection_bound;
437
438
439
440
441
    }
  else
    {
      *text = NULL;
      *cursor_index = 0;
442
      *selection_bound = 0;
443
444
445
    }

  if (info_is_local)
446
447
    {
      g_free (info->text);
Matthias Clasen's avatar
Matthias Clasen committed
448
      g_object_set_data (G_OBJECT (context), I_("gtk-im-surrounding-info"), NULL);
449
    }
450
451
452
453
  
  return result;
}

454
/**
455
 * gtk_im_context_set_client_widget:
Matthias Clasen's avatar
Matthias Clasen committed
456
 * @context: a `GtkIMContext`
457
 * @widget: (nullable): the client widget. This may be %NULL to indicate
Matthias Clasen's avatar
Matthias Clasen committed
458
459
 *   that the previous client widget no longer exists.
 *
Matthias Clasen's avatar
Matthias Clasen committed
460
461
462
 * Set the client widget for the input context.
 *
 * This is the `GtkWidget` holding the input focus. This widget is
463
464
 * used in order to correctly position status windows, and may
 * also be used for purposes internal to the input method.
Matthias Clasen's avatar
Matthias Clasen committed
465
 */
466
void
467
468
gtk_im_context_set_client_widget (GtkIMContext *context,
                                  GtkWidget    *widget)
469
470
471
472
473
474
{
  GtkIMContextClass *klass;
  
  g_return_if_fail (GTK_IS_IM_CONTEXT (context));

  klass = GTK_IM_CONTEXT_GET_CLASS (context);
475
476
  if (klass->set_client_widget)
    klass->set_client_widget (context, widget);
477
478
479
480
}

/**
 * gtk_im_context_get_preedit_string:
Matthias Clasen's avatar
Matthias Clasen committed
481
482
483
484
485
 * @context: a `GtkIMContext`
 * @str: (out) (transfer full): location to store the retrieved
 *   string. The string retrieved must be freed with g_free().
 * @attrs: (out) (transfer full): location to store the retrieved
 *   attribute list. When you are done with this list, you
Matthias Clasen's avatar
Matthias Clasen committed
486
487
488
 *   must unreference it with [method@Pango.AttrList.unref].
 * @cursor_pos: (out): location to store position of cursor
 *   (in characters) within the preedit string.
Matthias Clasen's avatar
Matthias Clasen committed
489
 *
490
491
 * Retrieve the current preedit string for the input context,
 * and a list of attributes to apply to the string.
Matthias Clasen's avatar
Matthias Clasen committed
492
493
494
 *
 * This string should be displayed inserted at the insertion point.
 */
495
496
void
gtk_im_context_get_preedit_string (GtkIMContext   *context,
Benjamin Otte's avatar
Benjamin Otte committed
497
				   char          **str,
Owen Taylor's avatar
Owen Taylor committed
498
				   PangoAttrList **attrs,
Benjamin Otte's avatar
Benjamin Otte committed
499
				   int            *cursor_pos)
500
501
502
503
504
505
{
  GtkIMContextClass *klass;
  
  g_return_if_fail (GTK_IS_IM_CONTEXT (context));
  
  klass = GTK_IM_CONTEXT_GET_CLASS (context);
Owen Taylor's avatar
Owen Taylor committed
506
  klass->get_preedit_string (context, str, attrs, cursor_pos);
507
  g_return_if_fail (str == NULL || g_utf8_validate (*str, -1, NULL));
508
509
510
511
}

/**
 * gtk_im_context_filter_keypress:
Matthias Clasen's avatar
Matthias Clasen committed
512
 * @context: a `GtkIMContext`
513
 * @event: the key event
Matthias Clasen's avatar
Matthias Clasen committed
514
515
516
517
518
 *
 * Allow an input method to internally handle key press and release
 * events.
 *
 * If this function returns %TRUE, then no further processing
Matthias Clasen's avatar
Matthias Clasen committed
519
 * should be done for this key event.
520
 *
Matthias Clasen's avatar
Matthias Clasen committed
521
522
 * Returns: %TRUE if the input method handled the key event.
 */
523
524
gboolean
gtk_im_context_filter_keypress (GtkIMContext *context,
Matthias Clasen's avatar
Matthias Clasen committed
525
				GdkEvent     *key)
526
527
528
529
530
531
532
533
534
535
{
  GtkIMContextClass *klass;
  
  g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), FALSE);
  g_return_val_if_fail (key != NULL, FALSE);

  klass = GTK_IM_CONTEXT_GET_CLASS (context);
  return klass->filter_keypress (context, key);
}

536
537
/**
 * gtk_im_context_filter_key:
Matthias Clasen's avatar
Matthias Clasen committed
538
 * @context: a `GtkIMContext`
539
540
541
542
543
544
545
546
547
 * @press: whether to forward a key press or release event
 * @surface: the surface the event is for
 * @device: the device that the event is for
 * @time: the timestamp for the event
 * @keycode: the keycode for the event
 * @state: modifier state for the event
 * @group: the active keyboard group for the event
 *
 * Allow an input method to forward key press and release events
Matthias Clasen's avatar
Matthias Clasen committed
548
 * to another input method without necessarily having a `GdkEvent`
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
 * available.
 *
 * Returns: %TRUE if the input method handled the key event.
 */
gboolean
gtk_im_context_filter_key (GtkIMContext    *context,
                           gboolean         press,
                           GdkSurface      *surface,
                           GdkDevice       *device,
                           guint32          time,
                           guint            keycode,
                           GdkModifierType  state,
                           int              group)
{
  GdkTranslatedKey translated, no_lock;
  GdkEvent *key;
  gboolean ret;
  guint keyval;
  int layout;
  int level;
  GdkModifierType consumed;

  g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), FALSE);

  if (!gdk_display_translate_key (gdk_surface_get_display (surface),
                                  keycode,
                                  state,
                                  group,
                                  &keyval,
                                  &layout,
                                  &level,
                                  &consumed))
    return FALSE;

  translated.keyval = keyval;
  translated.layout = layout;
  translated.level = level;
  translated.consumed = consumed;

  if (!gdk_display_translate_key (gdk_surface_get_display (surface),
                                  keycode,
                                  state & ~GDK_LOCK_MASK,
                                  group,
                                  &keyval,
                                  &layout,
                                  &level,
                                  &consumed))
    return FALSE;

  no_lock.keyval = keyval;
  no_lock.layout = layout;
  no_lock.level = level;
  no_lock.consumed = consumed;

  key = gdk_key_event_new (press ? GDK_KEY_PRESS : GDK_KEY_RELEASE,
                           surface,
                           device,
                           time,
                           keycode,
                           state,
                           FALSE, /* FIXME */
                           &translated,
                           &no_lock);

  ret = GTK_IM_CONTEXT_GET_CLASS (context)->filter_keypress (context, key);

  gdk_event_unref (key);

  return ret;
}

620
621
/**
 * gtk_im_context_focus_in:
Matthias Clasen's avatar
Matthias Clasen committed
622
 * @context: a `GtkIMContext`
623
624
 *
 * Notify the input method that the widget to which this
Matthias Clasen's avatar
Matthias Clasen committed
625
626
627
628
629
 * input context corresponds has gained focus.
 *
 * The input method may, for example, change the displayed
 * feedback to reflect this change.
 */
630
631
632
633
634
635
636
637
638
639
640
641
642
void
gtk_im_context_focus_in (GtkIMContext   *context)
{
  GtkIMContextClass *klass;
  
  g_return_if_fail (GTK_IS_IM_CONTEXT (context));
  
  klass = GTK_IM_CONTEXT_GET_CLASS (context);
  if (klass->focus_in)
    klass->focus_in (context);
}

/**
Owen Taylor's avatar
Owen Taylor committed
643
 * gtk_im_context_focus_out:
Matthias Clasen's avatar
Matthias Clasen committed
644
 * @context: a `GtkIMContext`
645
646
 *
 * Notify the input method that the widget to which this
Matthias Clasen's avatar
Matthias Clasen committed
647
648
649
650
651
 * input context corresponds has lost focus.
 *
 * The input method may, for example, change the displayed
 * feedback or reset the contexts state to reflect this change.
 */
652
653
654
655
656
657
658
659
660
661
662
663
void
gtk_im_context_focus_out (GtkIMContext   *context)
{
  GtkIMContextClass *klass;
  
  g_return_if_fail (GTK_IS_IM_CONTEXT (context));

  klass = GTK_IM_CONTEXT_GET_CLASS (context);
  if (klass->focus_out)
    klass->focus_out (context);
}

Owen Taylor's avatar
Owen Taylor committed
664
665
/**
 * gtk_im_context_reset:
Matthias Clasen's avatar
Matthias Clasen committed
666
 * @context: a `GtkIMContext`
Owen Taylor's avatar
Owen Taylor committed
667
668
 *
 * Notify the input method that a change such as a change in cursor
Matthias Clasen's avatar
Matthias Clasen committed
669
670
671
672
 * position has been made.
 *
 * This will typically cause the input method to clear the preedit state.
 */
Owen Taylor's avatar
Owen Taylor committed
673
674
675
676
677
678
679
680
681
682
683
684
void
gtk_im_context_reset (GtkIMContext   *context)
{
  GtkIMContextClass *klass;
  
  g_return_if_fail (GTK_IS_IM_CONTEXT (context));

  klass = GTK_IM_CONTEXT_GET_CLASS (context);
  if (klass->reset)
    klass->reset (context);
}

685

686
/**
687
 * gtk_im_context_set_cursor_location:
Matthias Clasen's avatar
Matthias Clasen committed
688
 * @context: a `GtkIMContext`
Havoc Pennington's avatar
Havoc Pennington committed
689
 * @area: new location
690
 *
Matthias Clasen's avatar
Matthias Clasen committed
691
692
693
 * Notify the input method that a change in cursor
 * position has been made.
 *
Matthias Clasen's avatar
Matthias Clasen committed
694
 * The location is relative to the client widget.
Matthias Clasen's avatar
Matthias Clasen committed
695
 */
696
void
697
698
gtk_im_context_set_cursor_location (GtkIMContext       *context,
				    const GdkRectangle *area)
699
700
701
702
703
704
{
  GtkIMContextClass *klass;
  
  g_return_if_fail (GTK_IS_IM_CONTEXT (context));

  klass = GTK_IM_CONTEXT_GET_CLASS (context);
705
  if (klass->set_cursor_location)
706
    klass->set_cursor_location (context, (GdkRectangle *) area);
707
708
}

709
710
/**
 * gtk_im_context_set_use_preedit:
Matthias Clasen's avatar
Matthias Clasen committed
711
 * @context: a `GtkIMContext`
712
 * @use_preedit: whether the IM context should use the preedit string.
Matthias Clasen's avatar
Matthias Clasen committed
713
 *
714
 * Sets whether the IM context should use the preedit string
Matthias Clasen's avatar
Matthias Clasen committed
715
716
717
718
719
720
 * to display feedback.
 *
 * If @use_preedit is %FALSE (default is %TRUE), then the IM context
 * may use some other method to display feedback, such as displaying
 * it in a child of the root window.
 */
721
722
723
724
725
726
727
728
729
730
731
732
void
gtk_im_context_set_use_preedit (GtkIMContext *context,
				gboolean      use_preedit)
{
  GtkIMContextClass *klass;
  
  g_return_if_fail (GTK_IS_IM_CONTEXT (context));

  klass = GTK_IM_CONTEXT_GET_CLASS (context);
  if (klass->set_use_preedit)
    klass->set_use_preedit (context, use_preedit);
}
733
734
735

/**
 * gtk_im_context_set_surrounding:
Matthias Clasen's avatar
Matthias Clasen committed
736
 * @context: a `GtkIMContext`
737
 * @text: text surrounding the insertion point, as UTF-8.
Matthias Clasen's avatar
Matthias Clasen committed
738
 *   the preedit string should not be included within @text
739
740
 * @len: the length of @text, or -1 if @text is nul-terminated
 * @cursor_index: the byte index of the insertion cursor within @text.
741
 *
742
 * Sets surrounding context around the insertion point and preedit
Matthias Clasen's avatar
Matthias Clasen committed
743
744
745
746
747
 * string.
 *
 * This function is expected to be called in response to the
 * [signal@Gtk.IMContext::retrieve-surrounding] signal, and will
 * likely have no effect if called at other times.
748
 *
Matthias Clasen's avatar
Matthias Clasen committed
749
 * Deprecated: 4.2: Use [method@Gtk.IMContext.set_surrounding_with_selection] instead
750
 */
751
752
void
gtk_im_context_set_surrounding (GtkIMContext  *context,
753
754
755
756
757
758
759
760
761
                                const char    *text,
                                int            len,
                                int            cursor_index)
{
  gtk_im_context_set_surrounding_with_selection (context, text, len, cursor_index, cursor_index);
}

/**
 * gtk_im_context_set_surrounding_with_selection:
Matthias Clasen's avatar
Matthias Clasen committed
762
 * @context: a `GtkIMContext`
763
 * @text: text surrounding the insertion point, as UTF-8.
Matthias Clasen's avatar
Matthias Clasen committed
764
 *   the preedit string should not be included within @text
765
 * @len: the length of @text, or -1 if @text is nul-terminated
Matthias Clasen's avatar
Matthias Clasen committed
766
 * @cursor_index: the byte index of the insertion cursor within @text
767
768
769
770
 * @anchor_index: the byte index of the selection bound within @text
 *
 * Sets surrounding context around the insertion point and preedit
 * string. This function is expected to be called in response to the
Matthias Clasen's avatar
Matthias Clasen committed
771
772
 * [signal@Gtk.IMContext::retrieve_surrounding] signal, and will likely
 * have no effect if called at other times.
773
774
 *
 * Since: 4.2
775
776
777
778
779
780
781
 */
void
gtk_im_context_set_surrounding_with_selection (GtkIMContext  *context,
                                               const char    *text,
                                               int            len,
                                               int            cursor_index,
                                               int            anchor_index)
782
783
{
  GtkIMContextClass *klass;
784

785
786
787
788
789
790
791
792
793
794
795
  g_return_if_fail (GTK_IS_IM_CONTEXT (context));
  g_return_if_fail (text != NULL || len == 0);

  if (text == NULL && len == 0)
    text = "";
  if (len < 0)
    len = strlen (text);

  g_return_if_fail (cursor_index >= 0 && cursor_index <= len);

  klass = GTK_IM_CONTEXT_GET_CLASS (context);
796
797
798
  if (klass->set_surrounding_with_selection)
    klass->set_surrounding_with_selection (context, text, len, cursor_index, anchor_index);
  else if (klass->set_surrounding)
799
800
801
802
803
    klass->set_surrounding (context, text, len, cursor_index);
}

/**
 * gtk_im_context_get_surrounding:
Matthias Clasen's avatar
Matthias Clasen committed
804
 * @context: a `GtkIMContext`
805
 * @text: (out) (transfer full): location to store a UTF-8 encoded
Matthias Clasen's avatar
Matthias Clasen committed
806
807
808
 *   string of text holding context around the insertion point.
 *   If the function returns %TRUE, then you must free the result
 *   stored in this location with g_free().
Matthias Clasen's avatar
Matthias Clasen committed
809
 * @cursor_index: (out): location to store byte index of the insertion
Matthias Clasen's avatar
Matthias Clasen committed
810
811
812
 *   cursor within @text.
 *
 * Retrieves context around the insertion point.
813
 *
Matthias Clasen's avatar
Matthias Clasen committed
814
815
816
 * Input methods typically want context in order to constrain input text
 * based on existing text; this is important for languages such as Thai
 * where only some sequences of characters are allowed.
817
818
 *
 * This function is implemented by emitting the
Matthias Clasen's avatar
Matthias Clasen committed
819
820
 * [signal@Gtk.IMContext::retrieve-surrounding] signal on the input method;
 * in response to this signal, a widget should provide as much context as
821
 * is available, up to an entire paragraph, by calling
Matthias Clasen's avatar
Matthias Clasen committed
822
823
824
825
826
 * [method@Gtk.IMContext.set_surrounding].
 *
 * Note that there is no obligation for a widget to respond to the
 * `::retrieve-surrounding` signal, so input methods must be prepared to
 * function without context.
827
 *
Matthias Clasen's avatar
Matthias Clasen committed
828
829
 * Returns: `TRUE` if surrounding text was provided; in this case
 *    you must free the result stored in `text`.
830
 *
Matthias Clasen's avatar
Matthias Clasen committed
831
 * Deprecated: 4.2: Use [method@Gtk.IMContext.get_surrounding_with_selection] instead.
832
833
834
835
836
837
838
839
840
841
842
843
844
 */
gboolean
gtk_im_context_get_surrounding (GtkIMContext  *context,
                                char         **text,
                                int           *cursor_index)
{
  return gtk_im_context_get_surrounding_with_selection (context,
                                                        text,
                                                        cursor_index,
                                                        NULL);
}

/**
Matthias Clasen's avatar
Matthias Clasen committed
845
 * gtk_im_context_get_surrounding_with_selection:
Matthias Clasen's avatar
Matthias Clasen committed
846
 * @context: a `GtkIMContext`
847
 * @text: (out) (transfer full): location to store a UTF-8 encoded
Matthias Clasen's avatar
Matthias Clasen committed
848
849
850
 *   string of text holding context around the insertion point.
 *   If the function returns %TRUE, then you must free the result
 *   stored in this location with g_free().
851
 * @cursor_index: (out): location to store byte index of the insertion
Matthias Clasen's avatar
Matthias Clasen committed
852
 *   cursor within @text.
853
 * @anchor_index: (out): location to store byte index of the selection
Matthias Clasen's avatar
Matthias Clasen committed
854
 *   bound within @text
855
 *
Matthias Clasen's avatar
Matthias Clasen committed
856
857
858
859
860
 * Retrieves context around the insertion point.
 *
 * Input methods typically want context in order to constrain input
 * text based on existing text; this is important for languages such
 * as Thai where only some sequences of characters are allowed.
861
862
 *
 * This function is implemented by emitting the
Matthias Clasen's avatar
Matthias Clasen committed
863
864
 * [signal@Gtk.IMContext::retrieve-surrounding] signal on the input method;
 * in response to this signal, a widget should provide as much context as
865
 * is available, up to an entire paragraph, by calling
Matthias Clasen's avatar
Matthias Clasen committed
866
867
868
869
870
 * [method@Gtk.IMContext.set_surrounding_with_selection].
 *
 * Note that there is no obligation for a widget to respond to the
 * `::retrieve-surrounding` signal, so input methods must be prepared to
 * function without context.
871
 *
Matthias Clasen's avatar
Matthias Clasen committed
872
873
 * Returns: `TRUE` if surrounding text was provided; in this case
 *   you must free the result stored in `text`.
874
875
 *
 * Since: 4.2
876
 */
877
gboolean
878
879
880
881
gtk_im_context_get_surrounding_with_selection (GtkIMContext  *context,
                                               char         **text,
                                               int           *cursor_index,
                                               int           *anchor_index)
882
883
{
  GtkIMContextClass *klass;
Benjamin Otte's avatar
Benjamin Otte committed
884
  char *local_text = NULL;
Benjamin Otte's avatar
Benjamin Otte committed
885
  int local_index;
886
  gboolean result = FALSE;
887

888
889
890
  g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), FALSE);

  klass = GTK_IM_CONTEXT_GET_CLASS (context);
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
  if (klass->get_surrounding_with_selection)
    result = klass->get_surrounding_with_selection
                                    (context,
                                     text ? text : &local_text,
                                     cursor_index ? cursor_index : &local_index,
                                     anchor_index ? anchor_index : &local_index);
  else if (klass->get_surrounding)
    {
      result = klass->get_surrounding (context,
                                       text ? text : &local_text,
                                       &local_index);
      if (cursor_index)
        *cursor_index = local_index;
      if (anchor_index)
        *anchor_index = local_index;
    }
907
908
909
910
911
912
913
914
915

  if (result)
    g_free (local_text);

  return result;
}

/**
 * gtk_im_context_delete_surrounding:
Matthias Clasen's avatar
Matthias Clasen committed
916
 * @context: a `GtkIMContext`
917
918
919
 * @offset: offset from cursor position in chars;
 *    a negative value means start before the cursor.
 * @n_chars: number of characters to delete.
Matthias Clasen's avatar
Matthias Clasen committed
920
 *
Yuri Chornoivan's avatar
Yuri Chornoivan committed
921
 * Asks the widget that the input context is attached to delete
922
 * characters around the cursor position by emitting the
Matthias Clasen's avatar
Matthias Clasen committed
923
 * `::delete_surrounding` signal.
Matthias Clasen's avatar
Matthias Clasen committed
924
925
 *
 * Note that @offset and @n_chars are in characters not in bytes
Matthias Clasen's avatar
Matthias Clasen committed
926
 * which differs from the usage other places in `GtkIMContext`.
927
928
 *
 * In order to use this function, you should first call
Matthias Clasen's avatar
Matthias Clasen committed
929
930
 * [method@Gtk.IMContext.get_surrounding] to get the current context,
 * and call this function immediately afterwards to make sure that you
931
932
933
934
935
 * know what you are deleting. You should also account for the fact
 * that even if the signal was handled, the input context might not
 * have deleted all the characters that were requested to be deleted.
 *
 * This function is used by an input method that wants to make
Matthias Clasen's avatar
Matthias Clasen committed
936
937
938
 * subsitutions in the existing text in response to new input.
 * It is not useful for applications.
 *
939
 * Returns: %TRUE if the signal was handled.
Matthias Clasen's avatar
Matthias Clasen committed
940
 */
941
942
gboolean
gtk_im_context_delete_surrounding (GtkIMContext *context,
Benjamin Otte's avatar
Benjamin Otte committed
943
944
				   int           offset,
				   int           n_chars)
945
946
947
948
949
950
951
952
953
954
955
{
  gboolean result;
  
  g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), FALSE);

  g_signal_emit (context,
		 im_context_signals[DELETE_SURROUNDING], 0,
		 offset, n_chars, &result);

  return result;
}
956
957
958
959
960
961
962

static void
gtk_im_context_get_property (GObject    *obj,
                             guint       property_id,
                             GValue     *value,
                             GParamSpec *pspec)
{
963
  GtkIMContextPrivate *priv = gtk_im_context_get_instance_private (GTK_IM_CONTEXT (obj));
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984

  switch (property_id)
    {
    case PROP_INPUT_PURPOSE:
      g_value_set_enum (value, priv->purpose);
      break;
    case PROP_INPUT_HINTS:
      g_value_set_flags (value, priv->hints);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
      break;
    }
}

static void
gtk_im_context_set_property (GObject      *obj,
                             guint         property_id,
                             const GValue *value,
                             GParamSpec   *pspec)
{
985
  GtkIMContextPrivate *priv = gtk_im_context_get_instance_private (GTK_IM_CONTEXT (obj));
986
987
988
989

  switch (property_id)
    {
    case PROP_INPUT_PURPOSE:
990
991
992
993
994
      if (priv->purpose != g_value_get_enum (value))
        {
          priv->purpose = g_value_get_enum (value);
          g_object_notify_by_pspec (obj, pspec);
        }
995
996
      break;
    case PROP_INPUT_HINTS:
997
998
999
1000
      if (priv->hints != g_value_get_flags (value))
        {
          priv->hints = g_value_get_flags (value);
          g_object_notify_by_pspec (obj, pspec);