gtkimcontext.c 27.6 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"
24
#include "gtkintl.h"
25

26 27 28 29 30 31 32 33 34 35
/**
 * SECTION:gtkimcontext
 * @title: GtkIMContext
 * @short_description: Base class for input method contexts
 * @include: gtk/gtk.h,gtk/gtkimmodule.h
 *
 * #GtkIMContext defines the interface for GTK+ input methods. An input method
 * is used by GTK+ text input widgets like #GtkEntry to map from key events to
 * Unicode character strings.
 *
36 37
 * The default input method can be set programmatically via the 
 * #GtkSettings:gtk-im-module GtkSettings property. Alternatively, you may set 
38 39
 * the GTK_IM_MODULE environment variable as documented in
 * [Running GTK+ Applications][gtk-running].
40 41 42 43 44 45 46
 *
 * The #GtkEntry #GtkEntry:im-module and #GtkTextView #GtkTextView:im-module 
 * properties may also be used to set input methods for specific widget 
 * instances. For instance, a certain entry widget might be expected to contain 
 * certain characters which would be easier to input with a certain input 
 * method.
 *
47 48 49 50 51
 * An input method may consume multiple key events in sequence and finally
 * output the composed result. This is called preediting, and an input method
 * may provide feedback about this process by displaying the intermediate
 * composition states as preedit text. For instance, the default GTK+ input
 * method implements the input of arbitrary Unicode code points by holding down
52
 * the Control and Shift keys and then typing “U” followed by the hexadecimal
53 54 55 56 57 58 59 60 61
 * digits of the code point.  When releasing the Control and Shift keys,
 * preediting ends and the character is inserted as text. Ctrl+Shift+u20AC for
 * example results in the € sign.
 *
 * Additional input methods can be made available for use by GTK+ widgets as
 * loadable modules. An input method module is a small shared library which
 * implements a subclass of #GtkIMContext or #GtkIMContextSimple and exports
 * these four functions:
 *
62
 * |[<!-- language="C" -->
63
 * void im_module_init(#GTypeModule *module);
64
 * ]|
65 66 67 68 69
 * This function should register the #GType of the #GtkIMContext subclass which
 * implements the input method by means of g_type_module_register_type(). Note
 * that g_type_register_static() cannot be used as the type needs to be
 * registered dynamically.
 *
70
 * |[<!-- language="C" -->
71
 * void im_module_exit(void);
72
 * ]|
73 74
 * Here goes any cleanup code your input method might require on module unload.
 *
75
 * |[<!-- language="C" -->
76 77 78 79 80
 * void im_module_list(const #GtkIMContextInfo ***contexts, int *n_contexts)
 * {
 *   *contexts = info_list;
 *   *n_contexts = G_N_ELEMENTS (info_list);
 * }
81
 * ]|
82 83 84 85 86
 * This function returns the list of input methods provided by the module. The
 * example implementation above shows a common solution and simply returns a
 * pointer to statically defined array of #GtkIMContextInfo items for each
 * provided input method.
 *
87
 * |[<!-- language="C" -->
88
 * #GtkIMContext * im_module_create(const #gchar *context_id);
89
 * ]|
90 91 92 93 94
 * This function should return a pointer to a newly created instance of the
 * #GtkIMContext subclass identified by @context_id. The context ID is the same
 * as specified in the #GtkIMContextInfo array returned by im_module_list().
 *
 * After a new loadable input method module has been installed on the system,
95
 * the configuration file `gtk.immodules` needs to be
96
 * regenerated by [gtk-query-immodules-3.0][gtk-query-immodules-3.0],
97 98 99
 * in order for the new input method to become available to GTK+ applications.
 */

100 101 102 103 104
enum {
  PREEDIT_START,
  PREEDIT_END,
  PREEDIT_CHANGED,
  COMMIT,
105 106
  RETRIEVE_SURROUNDING,
  DELETE_SURROUNDING,
107 108 109
  LAST_SIGNAL
};

110 111 112 113 114 115 116 117 118 119 120 121 122 123
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;
};
124

125 126 127 128 129 130 131 132 133 134 135 136 137
static void     gtk_im_context_real_get_preedit_string (GtkIMContext   *context,
							gchar         **str,
							PangoAttrList **attrs,
							gint           *cursor_pos);
static gboolean gtk_im_context_real_filter_keypress    (GtkIMContext   *context,
							GdkEventKey    *event);
static gboolean gtk_im_context_real_get_surrounding    (GtkIMContext   *context,
							gchar         **text,
							gint           *cursor_index);
static void     gtk_im_context_real_set_surrounding    (GtkIMContext   *context,
							const char     *text,
							gint            len,
							gint            cursor_index);
138

139 140 141 142 143 144 145 146 147 148
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);


149
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkIMContext, gtk_im_context, G_TYPE_OBJECT)
150

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
/**
 * GtkIMContextClass:
 * @preedit_start: Default handler of the #GtkIMContext::preedit-start signal.
 * @preedit_end: Default handler of the #GtkIMContext::preedit-end signal.
 * @preedit_changed: Default handler of the #GtkIMContext::preedit-changed
 *   signal.
 * @commit: Default handler of the #GtkIMContext::commit signal.
 * @retrieve_surrounding: Default handler of the
 *   #GtkIMContext::retrieve-surrounding signal.
 * @delete_surrounding: Default handler of the
 *   #GtkIMContext::delete-surrounding signal.
 * @set_client_window: Called via gtk_im_context_set_client_window() 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
 *   positioning a status display of your input method.
 * @get_preedit_string: Called via gtk_im_context_get_preedit_string() to
 *   retrieve the text currently being preedited for display at the cursor
 *   position. Any input method which composes complex characters or any
 *   other compositions from multiple sequential key presses should override
 *   this method to provide feedback.
 * @filter_keypress: Called via gtk_im_context_filter_keypress() on every
 *   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
 *   consumed by the input method. In that case, the #GtkIMContext::commit
 *   signal should be emitted upon completion of a key sequence to pass the
 *   resulting text back to the input widget. Alternatively, %FALSE may be
178
 *   returned to indicate that the event wasn’t handled by the input method.
179 180 181 182
 *   If a builtin mapping exists for the key, it is used to produce a
 *   character.
 * @focus_in: Called via gtk_im_context_focus_in() when the input widget
 *   has gained focus. May be overridden to keep track of the current focus.
183
 * @focus_out: Called via gtk_im_context_focus_out() when the input widget
184 185 186 187 188 189 190 191 192 193 194 195 196
 *   has lost focus. May be overridden to keep track of the current focus.
 * @reset: Called via gtk_im_context_reset() to signal a change such as a
 *   change in cursor position. An input method that implements preediting
 *   should override this method to clear the preedit state on reset.
 * @set_cursor_location: Called via gtk_im_context_set_cursor_location()
 *   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.
 * @set_use_preedit: Called via gtk_im_context_set_use_preedit() to control
 *   the use of the preedit string. Override this to display feedback by some
 *   other means if turned off.
 * @set_surrounding: Called via gtk_im_context_set_surrounding() in response
 *   to signal #GtkIMContext::retrieve-surrounding to update the input
197
 *   method’s idea of the context around the cursor. It is not necessary to
198 199 200 201 202 203 204 205 206 207
 *   override this method even with input methods which implement
 *   context-dependent behavior. The base implementation is sufficient for
 *   gtk_im_context_get_surrounding() to work.
 * @get_surrounding: Called via gtk_im_context_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
 *   #GtkIMContext::retrieve-surrounding and records the context received
 *   by the subsequent invocation of @get_surrounding.
 */
208 209 210
static void
gtk_im_context_class_init (GtkIMContextClass *klass)
{
211 212 213 214 215
  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;

216 217
  klass->get_preedit_string = gtk_im_context_real_get_preedit_string;
  klass->filter_keypress = gtk_im_context_real_filter_keypress;
218 219
  klass->get_surrounding = gtk_im_context_real_get_surrounding;
  klass->set_surrounding = gtk_im_context_real_set_surrounding;
220

221 222 223 224 225 226 227
  /**
   * GtkIMContext::preedit-start:
   * @context: the object on which the signal is emitted
   *
   * The ::preedit-start signal is emitted when a new preediting sequence
   * starts.
   */
228
  im_context_signals[PREEDIT_START] =
229
    g_signal_new (I_("preedit-start"),
Manish Singh's avatar
Manish Singh committed
230
		  G_TYPE_FROM_CLASS (klass),
231 232 233 234 235
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkIMContextClass, preedit_start),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
236 237 238 239 240 241 242
  /**
   * 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.
   */
243
  im_context_signals[PREEDIT_END] =
244
    g_signal_new (I_("preedit-end"),
Manish Singh's avatar
Manish Singh committed
245
		  G_TYPE_FROM_CLASS (klass),
246 247 248 249 250
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkIMContextClass, preedit_end),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
251 252 253 254 255 256 257 258 259
  /**
   * GtkIMContext::preedit-changed:
   * @context: the object on which the signal is emitted
   *
   * The ::preedit-changed signal is emitted whenever the preedit sequence
   * currently being entered has changed.  It is also emitted at the end of
   * a preedit sequence, in which case
   * gtk_im_context_get_preedit_string() returns the empty string.
   */
260
  im_context_signals[PREEDIT_CHANGED] =
261
    g_signal_new (I_("preedit-changed"),
Manish Singh's avatar
Manish Singh committed
262
		  G_TYPE_FROM_CLASS (klass),
263 264 265 266 267
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkIMContextClass, preedit_changed),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
268 269 270 271 272 273 274 275 276
  /**
   * 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
   * has been entered by the user. This can be a single character
   * immediately after a key press or the final result of preediting.
   */
277
  im_context_signals[COMMIT] =
278
    g_signal_new (I_("commit"),
Manish Singh's avatar
Manish Singh committed
279
		  G_TYPE_FROM_CLASS (klass),
280 281 282 283 284 285
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkIMContextClass, commit),
		  NULL, NULL,
		  _gtk_marshal_VOID__STRING,
		  G_TYPE_NONE, 1,
		  G_TYPE_STRING);
286 287 288 289 290 291 292 293 294
  /**
   * GtkIMContext::retrieve-surrounding:
   * @context: the object on which the signal is emitted
   *
   * The ::retrieve-surrounding signal is emitted when the input method
   * requires the context surrounding the cursor.  The callback should set
   * the input method surrounding context by calling the
   * gtk_im_context_set_surrounding() method.
   *
295
   * Returns: %TRUE if the signal was handled.
296
   */
297
  im_context_signals[RETRIEVE_SURROUNDING] =
298
    g_signal_new (I_("retrieve-surrounding"),
Manish Singh's avatar
Manish Singh committed
299
                  G_TYPE_FROM_CLASS (klass),
300 301
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkIMContextClass, retrieve_surrounding),
302
                  _gtk_boolean_handled_accumulator, NULL,
303
                  _gtk_marshal_BOOLEAN__VOID,
304
                  G_TYPE_BOOLEAN, 0);
305 306 307 308 309 310 311 312 313 314 315
  /**
   * GtkIMContext::delete-surrounding:
   * @context: the object on which the signal is emitted
   * @offset:  the character offset from the cursor position of the text
   *           to be deleted. A negative value indicates a position before
   *           the cursor.
   * @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.
   *
316
   * Returns: %TRUE if the signal was handled.
317
   */
318
  im_context_signals[DELETE_SURROUNDING] =
319
    g_signal_new (I_("delete-surrounding"),
Manish Singh's avatar
Manish Singh committed
320
                  G_TYPE_FROM_CLASS (klass),
321 322
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkIMContextClass, delete_surrounding),
323
                  _gtk_boolean_handled_accumulator, NULL,
324
                  _gtk_marshal_BOOLEAN__INT_INT,
325 326 327
                  G_TYPE_BOOLEAN, 2,
                  G_TYPE_INT,
		  G_TYPE_INT);
328 329 330 331 332 333 334

  properties[PROP_INPUT_PURPOSE] =
    g_param_spec_enum ("input-purpose",
                         P_("Purpose"),
                         P_("Purpose of the text field"),
                         GTK_TYPE_INPUT_PURPOSE,
                         GTK_INPUT_PURPOSE_FREE_FORM,
335
                         G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
336 337 338 339 340 341 342

  properties[PROP_INPUT_HINTS] =
    g_param_spec_flags ("input-hints",
                         P_("hints"),
                         P_("Hints for the text field behaviour"),
                         GTK_TYPE_INPUT_HINTS,
                         GTK_INPUT_HINT_NONE,
343
                         G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS|G_PARAM_EXPLICIT_NOTIFY);
344 345

  g_object_class_install_properties (object_class, LAST_PROPERTY, properties);
346 347 348 349 350 351 352 353 354 355
}

static void
gtk_im_context_init (GtkIMContext *im_context)
{
}

static void
gtk_im_context_real_get_preedit_string (GtkIMContext       *context,
					gchar             **str,
Owen Taylor's avatar
Owen Taylor committed
356 357
					PangoAttrList     **attrs,
					gint               *cursor_pos)
358 359 360 361 362
{
  if (str)
    *str = g_strdup ("");
  if (attrs)
    *attrs = pango_attr_list_new ();
Owen Taylor's avatar
Owen Taylor committed
363 364
  if (cursor_pos)
    *cursor_pos = 0;
365 366 367 368 369 370 371 372 373
}

static gboolean
gtk_im_context_real_filter_keypress (GtkIMContext       *context,
				     GdkEventKey        *event)
{
  return FALSE;
}

374 375 376 377 378 379 380 381 382 383 384 385
typedef struct
{
  gchar *text;
  gint cursor_index;
} SurroundingInfo;

static void
gtk_im_context_real_set_surrounding (GtkIMContext  *context,
				     const gchar   *text,
				     gint           len,
				     gint           cursor_index)
{
386 387
  SurroundingInfo *info = g_object_get_data (G_OBJECT (context),
                                             "gtk-im-surrounding-info");
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410

  if (info)
    {
      g_free (info->text);
      info->text = g_strndup (text, len);
      info->cursor_index = cursor_index;
    }
}

static gboolean
gtk_im_context_real_get_surrounding (GtkIMContext *context,
				     gchar       **text,
				     gint         *cursor_index)
{
  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;
411
      g_object_set_data (G_OBJECT (context), I_("gtk-im-surrounding-info"), info);
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
      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;
    }
  else
    {
      *text = NULL;
      *cursor_index = 0;
    }

  if (info_is_local)
431 432
    {
      g_free (info->text);
433
      g_object_set_data (G_OBJECT (context), I_("gtk-im-surrounding-info"), NULL);
434
    }
435 436 437 438
  
  return result;
}

439 440 441
/**
 * gtk_im_context_set_client_window:
 * @context: a #GtkIMContext
442
 * @window: (allow-none):  the client window. This may be %NULL to indicate
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
 *           that the previous client window no longer exists.
 * 
 * Set the client window for the input context; this is the
 * #GdkWindow in which the input appears. This window is
 * used in order to correctly position status windows, and may
 * also be used for purposes internal to the input method.
 **/
void
gtk_im_context_set_client_window (GtkIMContext *context,
				  GdkWindow    *window)
{
  GtkIMContextClass *klass;
  
  g_return_if_fail (GTK_IS_IM_CONTEXT (context));

  klass = GTK_IM_CONTEXT_GET_CLASS (context);
  if (klass->set_client_window)
    klass->set_client_window (context, window);
}

/**
 * gtk_im_context_get_preedit_string:
Owen Taylor's avatar
Owen Taylor committed
465
 * @context:    a #GtkIMContext
466 467 468 469 470 471
 * @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
 *              must unreference it with pango_attr_list_unref().
 * @cursor_pos: (out): location to store position of cursor (in characters)
Owen Taylor's avatar
Owen Taylor committed
472
 *              within the preedit string.  
473 474 475 476 477 478 479 480 481
 * 
 * Retrieve the current preedit string for the input context,
 * and a list of attributes to apply to the string.
 * This string should be displayed inserted at the insertion
 * point.
 **/
void
gtk_im_context_get_preedit_string (GtkIMContext   *context,
				   gchar         **str,
Owen Taylor's avatar
Owen Taylor committed
482 483
				   PangoAttrList **attrs,
				   gint           *cursor_pos)
484 485 486 487 488 489
{
  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
490
  klass->get_preedit_string (context, str, attrs, cursor_pos);
491
  g_return_if_fail (str == NULL || g_utf8_validate (*str, -1, NULL));
492 493 494 495 496
}

/**
 * gtk_im_context_filter_keypress:
 * @context: a #GtkIMContext
497
 * @event: the key event
498
 * 
499 500 501
 * Allow an input method to internally handle key press and release 
 * events. If this function returns %TRUE, then no further processing
 * should be done for this key event.
502
 * 
503
 * Returns: %TRUE if the input method handled the key event.
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
 *
 **/
gboolean
gtk_im_context_filter_keypress (GtkIMContext *context,
				GdkEventKey  *key)
{
  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);
}

/**
 * gtk_im_context_focus_in:
 * @context: a #GtkIMContext
 *
 * Notify the input method that the widget to which this
524
 * input context corresponds has gained focus. The input method
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
 * may, for example, change the displayed feedback to reflect
 * this change.
 **/
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
541
 * gtk_im_context_focus_out:
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
 * @context: a #GtkIMContext
 *
 * Notify the input method that the widget to which this
 * 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.
 **/
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
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
/**
 * gtk_im_context_reset:
 * @context: a #GtkIMContext
 *
 * Notify the input method that a change such as a change in cursor
 * position has been made. This will typically cause the input
 * method to clear the preedit state.
 **/
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);
}

581

582
/**
583
 * gtk_im_context_set_cursor_location:
584
 * @context: a #GtkIMContext
Havoc Pennington's avatar
Havoc Pennington committed
585
 * @area: new location
586 587
 *
 * Notify the input method that a change in cursor 
588 589
 * position has been made. The location is relative to the client
 * window.
590 591
 **/
void
592 593
gtk_im_context_set_cursor_location (GtkIMContext       *context,
				    const GdkRectangle *area)
594 595 596 597 598 599
{
  GtkIMContextClass *klass;
  
  g_return_if_fail (GTK_IS_IM_CONTEXT (context));

  klass = GTK_IM_CONTEXT_GET_CLASS (context);
600
  if (klass->set_cursor_location)
601
    klass->set_cursor_location (context, (GdkRectangle *) area);
602 603
}

604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
/**
 * gtk_im_context_set_use_preedit:
 * @context: a #GtkIMContext
 * @use_preedit: whether the IM context should use the preedit string.
 * 
 * Sets whether the IM context should use the preedit string
 * 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.
 **/
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);
}
626 627 628 629 630 631 632 633 634 635 636 637

/**
 * gtk_im_context_set_surrounding:
 * @context: a #GtkIMContext 
 * @text: text surrounding the insertion point, as UTF-8.
 *        the preedit string should not be included within
 *        @text.
 * @len: the length of @text, or -1 if @text is nul-terminated
 * @cursor_index: the byte index of the insertion cursor within @text.
 * 
 * Sets surrounding context around the insertion point and preedit
 * string. This function is expected to be called in response to the
638
 * GtkIMContext::retrieve_surrounding signal, and will likely have no
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666
 * effect if called at other times.
 **/
void
gtk_im_context_set_surrounding (GtkIMContext  *context,
				const gchar   *text,
				gint           len,
				gint           cursor_index)
{
  GtkIMContextClass *klass;
  
  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);
  if (klass->set_surrounding)
    klass->set_surrounding (context, text, len, cursor_index);
}

/**
 * gtk_im_context_get_surrounding:
 * @context: a #GtkIMContext
667 668 669 670
 * @text: (out) (transfer full): location to store a UTF-8 encoded
 *        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
671
 * @cursor_index: (out): location to store byte index of the insertion
672
 *        cursor within @text.
673 674 675 676 677 678 679
 * 
 * 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.
 *
 * This function is implemented by emitting the
680
 * GtkIMContext::retrieve_surrounding signal on the input method; in
681 682
 * response to this signal, a widget should provide as much context as
 * is available, up to an entire paragraph, by calling
683
 * gtk_im_context_set_surrounding(). Note that there is no obligation
684
 * for a widget to respond to the ::retrieve_surrounding signal, so input
685 686
 * methods must be prepared to function without context.
 *
687
 * Returns: %TRUE if surrounding text was provided; in this case
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
 *    you must free the result stored in *text.
 **/
gboolean
gtk_im_context_get_surrounding (GtkIMContext *context,
				gchar       **text,
				gint         *cursor_index)
{
  GtkIMContextClass *klass;
  gchar *local_text = NULL;
  gint local_index;
  gboolean result = FALSE;
  
  g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), FALSE);

  klass = GTK_IM_CONTEXT_GET_CLASS (context);
  if (klass->get_surrounding)
    result = klass->get_surrounding (context,
				     text ? text : &local_text,
				     cursor_index ? cursor_index : &local_index);

  if (result)
    g_free (local_text);

  return result;
}

/**
 * gtk_im_context_delete_surrounding:
 * @context: a #GtkIMContext
 * @offset: offset from cursor position in chars;
 *    a negative value means start before the cursor.
 * @n_chars: number of characters to delete.
 * 
 * Asks the widget that the input context is attached to to delete
 * characters around the cursor position by emitting the
723
 * GtkIMContext::delete_surrounding signal. Note that @offset and @n_chars
724
 * are in characters not in bytes which differs from the usage other
725 726 727 728 729 730 731 732 733 734 735 736 737
 * places in #GtkIMContext.
 *
 * In order to use this function, you should first call
 * gtk_im_context_get_surrounding() to get the current context, and
 * call this function immediately afterwards to make sure that you
 * 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
 * subsitutions in the existing text in response to new input. It is
 * not useful for applications.
 * 
738
 * Returns: %TRUE if the signal was handled.
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754
 **/
gboolean
gtk_im_context_delete_surrounding (GtkIMContext *context,
				   gint          offset,
				   gint          n_chars)
{
  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;
}
755 756 757 758 759 760 761

static void
gtk_im_context_get_property (GObject    *obj,
                             guint       property_id,
                             GValue     *value,
                             GParamSpec *pspec)
{
762
  GtkIMContextPrivate *priv = gtk_im_context_get_instance_private (GTK_IM_CONTEXT (obj));
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783

  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)
{
784
  GtkIMContextPrivate *priv = gtk_im_context_get_instance_private (GTK_IM_CONTEXT (obj));
785 786 787 788

  switch (property_id)
    {
    case PROP_INPUT_PURPOSE:
789 790 791 792 793
      if (priv->purpose != g_value_get_enum (value))
        {
          priv->purpose = g_value_get_enum (value);
          g_object_notify_by_pspec (obj, pspec);
        }
794 795
      break;
    case PROP_INPUT_HINTS:
796 797 798 799 800
      if (priv->hints != g_value_get_flags (value))
        {
          priv->hints = g_value_get_flags (value);
          g_object_notify_by_pspec (obj, pspec);
        }
801 802 803 804 805 806
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
      break;
    }
}