gtkimcontextsimple.c 40.7 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"
19

20 21 22 23 24 25 26 27
#include <gdk/gdk.h>

#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#ifdef GDK_WINDOWING_WAYLAND
#include <wayland/gdkwayland.h>
#endif
28 29 30
#ifdef GDK_WINDOWING_WIN32
#include <win32/gdkwin32.h>
#endif
31

32
#include <stdlib.h>
33
#include <string.h>
34

35
#include "gtkprivate.h"
36
#include "gtkaccelgroup.h"
37
#include "gtkimcontextsimple.h"
38 39
#include "gtksettings.h"
#include "gtkwidget.h"
40
#include "gtkdebug.h"
41
#include "gtkintl.h"
42
#include "gtkcomposetable.h"
43

44
#include "gtkimcontextsimpleprivate.h"
45
#include "gtkimcontextsimpleseqs.h"
46

47 48 49 50
/**
 * SECTION:gtkimcontextsimple
 * @Short_description: An input method context supporting table-based input methods
 * @Title: GtkIMContextSimple
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
 *
 * GtkIMContextSimple is a simple input method context supporting table-based
 * input methods. It has a built-in table of compose sequences that is derived
 * from the X11 Compose files.
 *
 * GtkIMContextSimple reads additional compose sequences from the first of the
 * following files that is found: ~/.config/gtk-3.0/Compose, ~/.XCompose,
 * /usr/share/X11/locale/$locale/Compose (for locales that have a nontrivial
 * Compose file). The syntax of these files is described in the Compose(5)
 * manual page.
 *
 * GtkIMContextSimple also supports numeric entry of Unicode characters
 * by typing Ctrl-Shift-u, followed by a hexadecimal Unicode codepoint.
 * For example, Ctrl-Shift-u 1 2 3 Enter yields U+0123 LATIN SMALL LETTER
 * G WITH CEDILLA, i.e. ģ.
66 67
 */

68
struct _GtkIMContextSimplePrivate
69
{
70
  guint16        compose_buffer[GTK_MAX_COMPOSE_LEN + 1];
71 72 73 74 75 76 77
  gunichar       tentative_match;
  gint           tentative_match_len;

  guint          in_hex_sequence : 1;
  guint          modifiers_dropped : 1;
};

78
/* From the values below, the value 30 means the number of different first keysyms
79
 * that exist in the Compose file (from Xorg). When running compose-parse.py without
80 81 82
 * parameters, you get the count that you can put here. Needed when updating the
 * gtkimcontextsimpleseqs.h header file (contains the compose sequences).
 */
83
const GtkComposeTableCompact gtk_compose_table_compact = {
84 85
  gtk_compose_seqs_compact,
  5,
86
  30,
87
  6
88 89
};

90 91
G_LOCK_DEFINE_STATIC (global_tables);
static GSList *global_tables;
92

Matthias Clasen's avatar
Matthias Clasen committed
93
static const guint16 gtk_compose_ignore[] = {
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
  GDK_KEY_Shift_L,
  GDK_KEY_Shift_R,
  GDK_KEY_Control_L,
  GDK_KEY_Control_R,
  GDK_KEY_Caps_Lock,
  GDK_KEY_Shift_Lock,
  GDK_KEY_Meta_L,
  GDK_KEY_Meta_R,
  GDK_KEY_Alt_L,
  GDK_KEY_Alt_R,
  GDK_KEY_Super_L,
  GDK_KEY_Super_R,
  GDK_KEY_Hyper_L,
  GDK_KEY_Hyper_R,
  GDK_KEY_Mode_switch,
  GDK_KEY_ISO_Level3_Shift
110 111
};

Owen Taylor's avatar
Owen Taylor committed
112 113 114 115 116 117 118 119
static void     gtk_im_context_simple_finalize           (GObject                  *obj);
static gboolean gtk_im_context_simple_filter_keypress    (GtkIMContext             *context,
							  GdkEventKey              *key);
static void     gtk_im_context_simple_reset              (GtkIMContext             *context);
static void     gtk_im_context_simple_get_preedit_string (GtkIMContext             *context,
							  gchar                   **str,
							  PangoAttrList           **attrs,
							  gint                     *cursor_pos);
120 121
static void     gtk_im_context_simple_set_client_window  (GtkIMContext             *context,
                                                          GdkWindow                *window);
122

123
G_DEFINE_TYPE_WITH_PRIVATE (GtkIMContextSimple, gtk_im_context_simple, GTK_TYPE_IM_CONTEXT)
124 125 126 127 128

static void
gtk_im_context_simple_class_init (GtkIMContextSimpleClass *class)
{
  GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
Owen Taylor's avatar
Owen Taylor committed
129 130
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);

131
  im_context_class->filter_keypress = gtk_im_context_simple_filter_keypress;
Owen Taylor's avatar
Owen Taylor committed
132 133
  im_context_class->reset = gtk_im_context_simple_reset;
  im_context_class->get_preedit_string = gtk_im_context_simple_get_preedit_string;
134
  im_context_class->set_client_window = gtk_im_context_simple_set_client_window;
Owen Taylor's avatar
Owen Taylor committed
135
  gobject_class->finalize = gtk_im_context_simple_finalize;
136 137
}

138 139 140 141 142 143 144 145
static gchar*
get_x11_compose_file_dir (void)
{
  gchar* compose_file_dir;

#if defined (GDK_WINDOWING_X11) || defined (GDK_WINDOWING_WAYLAND)
  compose_file_dir = g_strdup (X11_DATA_PREFIX "/share/X11/locale");
#else
146
  compose_file_dir = g_build_filename (_gtk_get_datadir (), "X11", "locale", NULL);
147 148 149 150 151
#endif

  return compose_file_dir;
}

152 153 154 155 156 157 158 159 160 161
static void
gtk_im_context_simple_init_compose_table (GtkIMContextSimple *im_context_simple)
{
  gchar *path = NULL;
  const gchar *home;
  const gchar *locale;
  gchar **langs = NULL;
  gchar **lang = NULL;
  gchar * const sys_langs[] = { "el_gr", "fi_fi", "pt_br", NULL };
  gchar * const *sys_lang = NULL;
162
  gchar *x11_compose_file_dir = get_x11_compose_file_dir ();
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208

  path = g_build_filename (g_get_user_config_dir (), "gtk-3.0", "Compose", NULL);
  if (g_file_test (path, G_FILE_TEST_EXISTS))
    {
      gtk_im_context_simple_add_compose_file (im_context_simple, path);
      g_free (path);
      return;
    }
  g_free (path);
  path = NULL;

  home = g_get_home_dir ();
  if (home == NULL)
    return;

  path = g_build_filename (home, ".XCompose", NULL);
  if (g_file_test (path, G_FILE_TEST_EXISTS))
    {
      gtk_im_context_simple_add_compose_file (im_context_simple, path);
      g_free (path);
      return;
    }
  g_free (path);
  path = NULL;

  locale = g_getenv ("LC_CTYPE");
  if (locale == NULL)
    locale = g_getenv ("LANG");
  if (locale == NULL)
    locale = "C";

  /* FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=751826 */
  langs = g_get_locale_variants (locale);

  for (lang = langs; *lang; lang++)
    {
      if (g_str_has_prefix (*lang, "en_US"))
        break;
      if (**lang == 'C')
        break;

      /* Other languages just include en_us compose table. */
      for (sys_lang = sys_langs; *sys_lang; sys_lang++)
        {
          if (g_ascii_strncasecmp (*lang, *sys_lang, strlen (*sys_lang)) == 0)
            {
209
              path = g_build_filename (x11_compose_file_dir, *lang, "Compose", NULL);
210 211 212 213 214 215 216 217 218 219 220 221 222
              break;
            }
        }

      if (path == NULL)
        continue;

      if (g_file_test (path, G_FILE_TEST_EXISTS))
        break;
      g_free (path);
      path = NULL;
    }

223
  g_free (x11_compose_file_dir);
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
  g_strfreev (langs);

  if (path != NULL)
    gtk_im_context_simple_add_compose_file (im_context_simple, path);
  g_free (path);
  path = NULL;
}

static void
init_compose_table_thread_cb (GTask            *task,
                              gpointer          source_object,
                              gpointer          task_data,
                              GCancellable     *cancellable)
{
  if (g_task_return_error_if_cancelled (task))
    return;

  g_return_if_fail (GTK_IS_IM_CONTEXT_SIMPLE (task_data));

243
  gtk_im_context_simple_init_compose_table (GTK_IM_CONTEXT_SIMPLE (task_data));
244 245 246 247 248 249 250 251 252 253
}

void
init_compose_table_async (GtkIMContextSimple   *im_context_simple,
                          GCancellable         *cancellable,
                          GAsyncReadyCallback   callback,
                          gpointer              user_data)
{
  GTask *task = g_task_new (NULL, cancellable, callback, user_data);
  g_task_set_source_tag (task, init_compose_table_async);
254
  g_task_set_task_data (task, g_object_ref (im_context_simple), g_object_unref);
255 256 257 258
  g_task_run_in_thread (task, init_compose_table_thread_cb);
  g_object_unref (task);
}

259 260
static void
gtk_im_context_simple_init (GtkIMContextSimple *im_context_simple)
261
{
262
  im_context_simple->priv = gtk_im_context_simple_get_instance_private (im_context_simple); 
263 264
}

Owen Taylor's avatar
Owen Taylor committed
265 266 267
static void
gtk_im_context_simple_finalize (GObject *obj)
{
Matthias Clasen's avatar
Matthias Clasen committed
268
  G_OBJECT_CLASS (gtk_im_context_simple_parent_class)->finalize (obj);
Owen Taylor's avatar
Owen Taylor committed
269 270
}

271
/**
Matthias Clasen's avatar
Matthias Clasen committed
272 273 274 275 276 277
 * gtk_im_context_simple_new:
 * 
 * Creates a new #GtkIMContextSimple.
 *
 * Returns: a new #GtkIMContextSimple.
 **/
278 279 280
GtkIMContext *
gtk_im_context_simple_new (void)
{
Manish Singh's avatar
Manish Singh committed
281
  return g_object_new (GTK_TYPE_IM_CONTEXT_SIMPLE, NULL);
282 283 284 285
}

static void
gtk_im_context_simple_commit_char (GtkIMContext *context,
286
				   gunichar ch)
287
{
288
  GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
289
  GtkIMContextSimplePrivate *priv = context_simple->priv;
290
  gchar buf[10];
291
  gint len;
Owen Taylor's avatar
Owen Taylor committed
292

293
  g_return_if_fail (g_unichar_validate (ch));
294

295
  len = g_unichar_to_utf8 (ch, buf);
296 297
  buf[len] = '\0';

298
  if (priv->tentative_match || priv->in_hex_sequence)
299
    {
300 301 302
      priv->in_hex_sequence = FALSE;
      priv->tentative_match = 0;
      priv->tentative_match_len = 0;
303 304
      g_signal_emit_by_name (context_simple, "preedit-changed");
      g_signal_emit_by_name (context_simple, "preedit-end");
305
    }
Owen Taylor's avatar
Owen Taylor committed
306

307
  g_signal_emit_by_name (context, "commit", &buf);
308 309
}

310 311 312
static int
compare_seq_index (const void *key, const void *value)
{
313
  const guint16 *keysyms = key;
314 315 316 317 318 319 320 321 322 323
  const guint16 *seq = value;

  if (keysyms[0] < seq[0])
    return -1;
  else if (keysyms[0] > seq[0])
    return 1;

  return 0;
}

324 325 326 327
static int
compare_seq (const void *key, const void *value)
{
  int i = 0;
328
  const guint16 *keysyms = key;
Owen Taylor's avatar
Owen Taylor committed
329
  const guint16 *seq = value;
330 331 332

  while (keysyms[i])
    {
Owen Taylor's avatar
Owen Taylor committed
333
      if (keysyms[i] < seq[i])
334
	return -1;
Owen Taylor's avatar
Owen Taylor committed
335
      else if (keysyms[i] > seq[i])
336
	return 1;
337

338 339 340 341 342 343
      i++;
    }

  return 0;
}

Owen Taylor's avatar
Owen Taylor committed
344 345 346 347 348
static gboolean
check_table (GtkIMContextSimple    *context_simple,
	     const GtkComposeTable *table,
	     gint                   n_compose)
{
349
  GtkIMContextSimplePrivate *priv = context_simple->priv;
Owen Taylor's avatar
Owen Taylor committed
350
  gint row_stride = table->max_seq_len + 2; 
Matthias Clasen's avatar
Matthias Clasen committed
351 352 353 354 355 356 357 358
  guint16 *seq; 
  
  /* Will never match, if the sequence in the compose buffer is longer
   * than the sequences in the table.  Further, compare_seq (key, val)
   * will overrun val if key is longer than val. */
  if (n_compose > table->max_seq_len)
    return FALSE;
  
359
  seq = bsearch (priv->compose_buffer,
Matthias Clasen's avatar
Matthias Clasen committed
360 361 362
		 table->data, table->n_seqs,
		 sizeof (guint16) *  row_stride, 
		 compare_seq);
Owen Taylor's avatar
Owen Taylor committed
363 364 365 366 367 368

  if (seq)
    {
      guint16 *prev_seq;

      /* Back up to the first sequence that matches to make sure
369
       * we find the exact match if there is one.
Owen Taylor's avatar
Owen Taylor committed
370 371 372 373
       */
      while (seq > table->data)
	{
	  prev_seq = seq - row_stride;
374
	  if (compare_seq (priv->compose_buffer, prev_seq) != 0)
Owen Taylor's avatar
Owen Taylor committed
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
	    break;
	  seq = prev_seq;
	}
      
      if (n_compose == table->max_seq_len ||
	  seq[n_compose] == 0) /* complete sequence */
	{
	  guint16 *next_seq;
	  gunichar value = 
	    0x10000 * seq[table->max_seq_len] + seq[table->max_seq_len + 1];

	  /* We found a tentative match. See if there are any longer
	   * sequences containing this subsequence
	   */
	  next_seq = seq + row_stride;
	  if (next_seq < table->data + row_stride * table->n_seqs)
	    {
392
	      if (compare_seq (priv->compose_buffer, next_seq) == 0)
Owen Taylor's avatar
Owen Taylor committed
393
		{
394 395
		  priv->tentative_match = value;
		  priv->tentative_match_len = n_compose;
396

397
		  g_signal_emit_by_name (context_simple, "preedit-changed");
Owen Taylor's avatar
Owen Taylor committed
398 399 400 401 402 403

		  return TRUE;
		}
	    }

	  gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value);
404
	  priv->compose_buffer[0] = 0;
Owen Taylor's avatar
Owen Taylor committed
405 406 407 408 409 410 411 412
	}
      
      return TRUE;
    }

  return FALSE;
}

413
/* Checks if a keysym is a dead key. Dead key keysym values are defined in
414
 * ../gdk/gdkkeysyms.h and the first is GDK_KEY_dead_grave. As X.Org is updated,
415
 * more dead keys are added and we need to update the upper limit.
416 417
 * Currently, the upper limit is GDK_KEY_dead_dasia+1. The +1 has to do with
 * a temporary issue in the X.Org header files.
418 419 420
 * In future versions it will be just the keysym (no +1).
 */
#define IS_DEAD_KEY(k) \
421
    ((k) >= GDK_KEY_dead_grave && (k) <= (GDK_KEY_dead_dasia+1))
422 423 424 425 426 427 428 429 430 431 432 433 434 435

#ifdef GDK_WINDOWING_WIN32

/* On Windows, user expectation is that typing a dead accent followed
 * by space will input the corresponding spacing character. The X
 * compose tables are different for dead acute and diaeresis, which
 * when followed by space produce a plain ASCII apostrophe and double
 * quote respectively. So special-case those.
 */

static gboolean
check_win32_special_cases (GtkIMContextSimple    *context_simple,
			   gint                   n_compose)
{
436
  GtkIMContextSimplePrivate *priv = context_simple->priv;
437
  if (n_compose == 2 &&
438
      priv->compose_buffer[1] == GDK_KEY_space)
439 440 441
    {
      gunichar value = 0;

442
      switch (priv->compose_buffer[0])
443
	{
444
	case GDK_KEY_dead_acute:
445
	  value = 0x00B4; break;
446
	case GDK_KEY_dead_diaeresis:
447 448 449 450 451
	  value = 0x00A8; break;
	}
      if (value > 0)
	{
	  gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value);
452
	  priv->compose_buffer[0] = 0;
453 454 455 456 457 458 459 460 461 462 463 464

	  return TRUE;
	}
    }
  return FALSE;
}

static void
check_win32_special_case_after_compact_match (GtkIMContextSimple    *context_simple,
					      gint                   n_compose,
					      guint                  value)
{
465
  GtkIMContextSimplePrivate *priv = context_simple->priv;
466

467 468 469 470
  /* On Windows user expectation is that typing two dead accents will input
   * two corresponding spacing accents.
   */
  if (n_compose == 2 &&
471 472
      priv->compose_buffer[0] == priv->compose_buffer[1] &&
      IS_DEAD_KEY (priv->compose_buffer[0]))
473 474 475 476 477 478 479
    {
      gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value);
    }
}

#endif

480 481 482 483 484 485 486
#ifdef GDK_WINDOWING_QUARTZ

static gboolean
check_quartz_special_cases (GtkIMContextSimple *context_simple,
                            gint                n_compose)
{
  GtkIMContextSimplePrivate *priv = context_simple->priv;
487
  guint value = 0;
488

489
  if (n_compose == 2)
490 491 492 493
    {
      switch (priv->compose_buffer[0])
        {
        case GDK_KEY_dead_doubleacute:
494 495 496 497
          switch (priv->compose_buffer[1])
            {
            case GDK_KEY_dead_doubleacute:
            case GDK_KEY_space:
498
              value = GDK_KEY_quotedbl; break;
499 500 501 502 503 504 505 506 507 508 509 510

            case 'a': value = GDK_KEY_adiaeresis; break;
            case 'A': value = GDK_KEY_Adiaeresis; break;
            case 'e': value = GDK_KEY_ediaeresis; break;
            case 'E': value = GDK_KEY_Ediaeresis; break;
            case 'i': value = GDK_KEY_idiaeresis; break;
            case 'I': value = GDK_KEY_Idiaeresis; break;
            case 'o': value = GDK_KEY_odiaeresis; break;
            case 'O': value = GDK_KEY_Odiaeresis; break;
            case 'u': value = GDK_KEY_udiaeresis; break;
            case 'U': value = GDK_KEY_Udiaeresis; break;
            case 'y': value = GDK_KEY_ydiaeresis; break;
511
            case 'Y': value = GDK_KEY_Ydiaeresis; break;
512 513 514 515 516 517 518 519 520 521
            }
          break;

        case GDK_KEY_dead_acute:
          switch (priv->compose_buffer[1])
            {
            case 'c': value = GDK_KEY_ccedilla; break;
            case 'C': value = GDK_KEY_Ccedilla; break;
            }
          break;
522
        }
523
    }
524

525 526
  if (value > 0)
    {
527 528
      gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple),
                                         gdk_keyval_to_unicode (value));
529
      priv->compose_buffer[0] = 0;
530

531
      return TRUE;
532 533 534 535 536 537 538
    }

  return FALSE;
}

#endif

539 540
gboolean
gtk_check_compact_table (const GtkComposeTableCompact  *table,
541
                         guint16                       *compose_buffer,
542 543 544 545
                         gint                           n_compose,
                         gboolean                      *compose_finish,
                         gboolean                      *compose_match,
                         gunichar                      *output_char)
546 547 548
{
  gint row_stride;
  guint16 *seq_index;
549
  guint16 *seq;
550
  gint i;
551 552
  gboolean match;
  gunichar value;
553

554 555 556 557 558 559 560
  if (compose_finish)
    *compose_finish = FALSE;
  if (compose_match)
    *compose_match = FALSE;
  if (output_char)
    *output_char = 0;

561 562
  /* Will never match, if the sequence in the compose buffer is longer
   * than the sequences in the table.  Further, compare_seq (key, val)
563 564
   * will overrun val if key is longer than val.
   */
565 566
  if (n_compose > table->max_seq_len)
    return FALSE;
567

568
  seq_index = bsearch (compose_buffer,
569 570 571 572
                       table->data,
                       table->n_index_size,
                       sizeof (guint16) * table->n_index_stride,
                       compare_seq_index);
573 574

  if (!seq_index)
575
    return FALSE;
576 577

  if (seq_index && n_compose == 1)
578
    return TRUE;
579 580

  seq = NULL;
581
  match = FALSE;
582
  value = 0;
583

584
  for (i = n_compose - 1; i < table->max_seq_len; i++)
585 586 587
    {
      row_stride = i + 1;

588
      if (seq_index[i + 1] - seq_index[i] > 0)
589
        {
590
          seq = bsearch (compose_buffer + 1,
591 592 593 594
                         table->data + seq_index[i],
                         (seq_index[i + 1] - seq_index[i]) / row_stride,
                         sizeof (guint16) *  row_stride,
                         compare_seq);
595

596
          if (seq)
597 598
            {
              if (i == n_compose - 1)
599 600 601 602
                {
                  value = seq[row_stride - 1];
                  match = TRUE;
                }
603 604
              else
                {
605 606
                  if (output_char)
                    *output_char = value;
607 608
                  if (match)
                    {
609 610
                      if (compose_match)
                        *compose_match = TRUE;
611 612 613
                    }

                  return TRUE;
614
                }
615
            }
616 617 618
        }
    }

619
  if (match)
620
    {
621 622 623 624 625 626
      if (compose_match)
        *compose_match = TRUE;
      if (compose_finish)
        *compose_finish = TRUE;
      if (output_char)
        *output_char = value;
627 628 629 630 631 632 633

      return TRUE;
    }

  return FALSE;
}

634
/* This function receives a sequence of Unicode characters and tries to
635
 * normalize it (NFC). We check for the case where the resulting string
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
 * has length 1 (single character).
 * NFC normalisation normally rearranges diacritic marks, unless these
 * belong to the same Canonical Combining Class.
 * If they belong to the same canonical combining class, we produce all
 * permutations of the diacritic marks, then attempt to normalize.
 */
static gboolean
check_normalize_nfc (gunichar* combination_buffer, gint n_compose)
{
  gunichar combination_buffer_temp[GTK_MAX_COMPOSE_LEN];
  gchar *combination_utf8_temp = NULL;
  gchar *nfc_temp = NULL;
  gint n_combinations;
  gunichar temp_swap;
  gint i;

  n_combinations = 1;

  for (i = 1; i < n_compose; i++ )
     n_combinations *= i;

  /* Xorg reuses dead_tilde for the perispomeni diacritic mark.
   * We check if base character belongs to Greek Unicode block,
659 660
   * and if so, we replace tilde with perispomeni.
   */
661 662 663 664 665 666 667 668 669 670 671 672 673
  if (combination_buffer[0] >= 0x390 && combination_buffer[0] <= 0x3FF)
    {
      for (i = 1; i < n_compose; i++ )
        if (combination_buffer[i] == 0x303)
          combination_buffer[i] = 0x342;
    }

  memcpy (combination_buffer_temp, combination_buffer, GTK_MAX_COMPOSE_LEN * sizeof (gunichar) );

  for (i = 0; i < n_combinations; i++ )
    {
      g_unicode_canonical_ordering (combination_buffer_temp, n_compose);
      combination_utf8_temp = g_ucs4_to_utf8 (combination_buffer_temp, -1, NULL, NULL, NULL);
674
      nfc_temp = g_utf8_normalize (combination_utf8_temp, -1, G_NORMALIZE_NFC);
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701

      if (g_utf8_strlen (nfc_temp, -1) == 1)
        {
          memcpy (combination_buffer, combination_buffer_temp, GTK_MAX_COMPOSE_LEN * sizeof (gunichar) );

          g_free (combination_utf8_temp);
          g_free (nfc_temp);

          return TRUE;
        }

      g_free (combination_utf8_temp);
      g_free (nfc_temp);

      if (n_compose > 2)
        {
          temp_swap = combination_buffer_temp[i % (n_compose - 1) + 1];
          combination_buffer_temp[i % (n_compose - 1) + 1] = combination_buffer_temp[(i+1) % (n_compose - 1) + 1];
          combination_buffer_temp[(i+1) % (n_compose - 1) + 1] = temp_swap;
        }
      else
        break;
    }

  return FALSE;
}

702
gboolean
703
gtk_check_algorithmically (const guint16       *compose_buffer,
704 705
                           gint                 n_compose,
                           gunichar            *output_char)
706 707

{
708
  gint i;
709 710 711
  gunichar combination_buffer[GTK_MAX_COMPOSE_LEN];
  gchar *combination_utf8, *nfc;

712 713 714
  if (output_char)
    *output_char = 0;

715 716 717
  if (n_compose >= GTK_MAX_COMPOSE_LEN)
    return FALSE;

718
  for (i = 0; i < n_compose && IS_DEAD_KEY (compose_buffer[i]); i++)
719 720 721 722 723 724
    ;
  if (i == n_compose)
    return TRUE;

  if (i > 0 && i == n_compose - 1)
    {
725
      combination_buffer[0] = gdk_keyval_to_unicode (compose_buffer[i]);
726 727 728 729
      combination_buffer[n_compose] = 0;
      i--;
      while (i >= 0)
	{
730
	  switch (compose_buffer[i])
731 732
	    {
#define CASE(keysym, unicode) \
733
	    case GDK_KEY_dead_##keysym: combination_buffer[i+1] = unicode; break
734 735 736 737

	    CASE (grave, 0x0300);
	    CASE (acute, 0x0301);
	    CASE (circumflex, 0x0302);
738
	    CASE (tilde, 0x0303);	/* Also used with perispomeni, 0x342. */
739 740 741 742
	    CASE (macron, 0x0304);
	    CASE (breve, 0x0306);
	    CASE (abovedot, 0x0307);
	    CASE (diaeresis, 0x0308);
743
	    CASE (hook, 0x0309);
744 745 746
	    CASE (abovering, 0x030A);
	    CASE (doubleacute, 0x030B);
	    CASE (caron, 0x030C);
747 748 749 750
	    CASE (abovecomma, 0x0313);         /* Equivalent to psili */
	    CASE (abovereversedcomma, 0x0314); /* Equivalent to dasia */
	    CASE (horn, 0x031B);	/* Legacy use for psili, 0x313 (or 0x343). */
	    CASE (belowdot, 0x0323);
751
	    CASE (cedilla, 0x0327);
752 753
	    CASE (ogonek, 0x0328);	/* Legacy use for dasia, 0x314.*/
	    CASE (iota, 0x0345);
754
	    CASE (voiced_sound, 0x3099);	/* Per Markus Kuhn keysyms.txt file. */
755 756 757 758 759 760 761 762 763 764
	    CASE (semivoiced_sound, 0x309A);	/* Per Markus Kuhn keysyms.txt file. */

	    /* The following cases are to be removed once xkeyboard-config,
 	     * xorg are fully updated.
 	     */
            /* Workaround for typo in 1.4.x xserver-xorg */
	    case 0xfe66: combination_buffer[i+1] = 0x314; break;
	    /* CASE (dasia, 0x314); */
	    /* CASE (perispomeni, 0x342); */
	    /* CASE (psili, 0x343); */
765 766
#undef CASE
	    default:
767
	      combination_buffer[i+1] = gdk_keyval_to_unicode (compose_buffer[i]);
768 769 770 771
	    }
	  i--;
	}
      
772 773
      /* If the buffer normalizes to a single character, then modify the order
       * of combination_buffer accordingly, if necessary, and return TRUE.
774 775 776 777 778
       */
      if (check_normalize_nfc (combination_buffer, n_compose))
        {
      	  combination_utf8 = g_ucs4_to_utf8 (combination_buffer, -1, NULL, NULL, NULL);
          nfc = g_utf8_normalize (combination_utf8, -1, G_NORMALIZE_NFC);
779

780 781
          if (output_char)
            *output_char = g_utf8_get_char (nfc);
782

783 784 785 786 787
          g_free (combination_utf8);
          g_free (nfc);

          return TRUE;
        }
788 789 790 791 792
    }

  return FALSE;
}

Havoc Pennington's avatar
Havoc Pennington committed
793
/* In addition to the table-driven sequences, we allow Unicode hex
794 795
 * codes to be entered. The method chosen here is similar to the
 * one recommended in ISO 14755, but not exactly the same, since we
796
 * don’t want to steal 16 valuable key combinations.
797
 *
798 799 800 801 802 803 804 805 806
 * A hex Unicode sequence must be started with Ctrl-Shift-U, followed
 * by a sequence of hex digits entered with Ctrl-Shift still held.
 * Releasing one of the modifiers or pressing space while the modifiers
 * are still held commits the character. It is possible to erase
 * digits using backspace.
 *
 * As an extension to the above, we also allow to start the sequence
 * with Ctrl-Shift-U, then release the modifiers before typing any
 * digits, and enter the digits without modifiers.
Havoc Pennington's avatar
Havoc Pennington committed
807 808
 */

809
static gboolean
Havoc Pennington's avatar
Havoc Pennington committed
810 811
check_hex (GtkIMContextSimple *context_simple,
           gint                n_compose)
812
{
813
  GtkIMContextSimplePrivate *priv = context_simple->priv;
Havoc Pennington's avatar
Havoc Pennington committed
814 815 816 817 818
  /* See if this is a hex sequence, return TRUE if so */
  gint i;
  GString *str;
  gulong n;
  gchar *nptr = NULL;
819
  gchar buf[7];
820

821 822
  priv->tentative_match = 0;
  priv->tentative_match_len = 0;
823

Havoc Pennington's avatar
Havoc Pennington committed
824 825 826 827 828 829 830
  str = g_string_new (NULL);
  
  i = 0;
  while (i < n_compose)
    {
      gunichar ch;
      
831
      ch = gdk_keyval_to_unicode (priv->compose_buffer[i]);
Havoc Pennington's avatar
Havoc Pennington committed
832 833 834
      
      if (ch == 0)
        return FALSE;
835

Havoc Pennington's avatar
Havoc Pennington committed
836 837
      if (!g_unichar_isxdigit (ch))
        return FALSE;
838

Havoc Pennington's avatar
Havoc Pennington committed
839
      buf[g_unichar_to_utf8 (ch, buf)] = '\0';
840

Havoc Pennington's avatar
Havoc Pennington committed
841 842 843 844
      g_string_append (str, buf);
      
      ++i;
    }
845

Havoc Pennington's avatar
Havoc Pennington committed
846 847
  n = strtoul (str->str, &nptr, 16);

848
  /* If strtoul fails it probably means non-latin digits were used;
Havoc Pennington's avatar
Havoc Pennington committed
849 850
   * we should in principle handle that, but we probably don't.
   */
851
  if (nptr - str->str < str->len)
Owen Taylor's avatar
Owen Taylor committed
852
    {
Havoc Pennington's avatar
Havoc Pennington committed
853 854
      g_string_free (str, TRUE);
      return FALSE;
Owen Taylor's avatar
Owen Taylor committed
855
    }
Havoc Pennington's avatar
Havoc Pennington committed
856 857 858
  else
    g_string_free (str, TRUE);

859 860
  if (g_unichar_validate (n))
    {
861 862
      priv->tentative_match = n;
      priv->tentative_match_len = n_compose;
863
    }
Havoc Pennington's avatar
Havoc Pennington committed
864 865 866 867
  
  return TRUE;
}

868 869 870
static void
beep_window (GdkWindow *window)
{
871
  GdkScreen *screen = gdk_window_get_screen (window);
872
  gboolean   beep;
873

874 875 876
  g_object_get (gtk_settings_get_for_screen (screen),
                "gtk-error-bell", &beep,
                NULL);
877

878 879
  if (beep)
    gdk_window_beep (window);
880 881
}

Havoc Pennington's avatar
Havoc Pennington committed
882 883 884 885 886
static gboolean
no_sequence_matches (GtkIMContextSimple *context_simple,
                     gint                n_compose,
                     GdkEventKey        *event)
{
887
  GtkIMContextSimplePrivate *priv = context_simple->priv;
Havoc Pennington's avatar
Havoc Pennington committed
888 889
  GtkIMContext *context;
  gunichar ch;
Owen Taylor's avatar
Owen Taylor committed
890
  
Havoc Pennington's avatar
Havoc Pennington committed
891
  context = GTK_IM_CONTEXT (context_simple);
Owen Taylor's avatar
Owen Taylor committed
892 893 894 895
  
  /* No compose sequences found, check first if we have a partial
   * match pending.
   */
896
  if (priv->tentative_match)
Owen Taylor's avatar
Owen Taylor committed
897
    {
898
      gint len = priv->tentative_match_len;
Owen Taylor's avatar
Owen Taylor committed
899 900
      int i;
      
901 902
      gtk_im_context_simple_commit_char (context, priv->tentative_match);
      priv->compose_buffer[0] = 0;
Owen Taylor's avatar
Owen Taylor committed
903 904 905
      
      for (i=0; i < n_compose - len - 1; i++)
	{
906
	  GdkEvent *tmp_event = gdk_event_copy ((GdkEvent *)event);
907
	  tmp_event->key.keyval = priv->compose_buffer[len + i];
Owen Taylor's avatar
Owen Taylor committed
908
	  
909
	  gtk_im_context_filter_keypress (context, (GdkEventKey *)tmp_event);
910
	  gdk_event_free (tmp_event);
Owen Taylor's avatar
Owen Taylor committed
911
	}
912

Owen Taylor's avatar
Owen Taylor committed
913 914 915
      return gtk_im_context_filter_keypress (context, event);
    }
  else
916
    {
917
      priv->compose_buffer[0] = 0;
Owen Taylor's avatar
Owen Taylor committed
918
      if (n_compose > 1)		/* Invalid sequence */
919
	{
920
	  beep_window (event->window);
Owen Taylor's avatar
Owen Taylor committed
921
	  return TRUE;
922
	}
Owen Taylor's avatar
Owen Taylor committed
923 924
  
      ch = gdk_keyval_to_unicode (event->keyval);
925
      if (ch != 0 && !g_unichar_iscntrl (ch))
Owen Taylor's avatar
Owen Taylor committed
926 927 928 929 930 931
	{
	  gtk_im_context_simple_commit_char (context, ch);
	  return TRUE;
	}
      else
	return FALSE;
932
    }
Owen Taylor's avatar
Owen Taylor committed
933
}
934

Havoc Pennington's avatar
Havoc Pennington committed
935 936 937 938 939 940 941 942 943 944 945
static gboolean
is_hex_keyval (guint keyval)
{
  gunichar ch = gdk_keyval_to_unicode (keyval);

  return g_unichar_isxdigit (ch);
}

static guint
canonical_hex_keyval (GdkEventKey *event)
{
946
  GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_window_get_display (event->window));
Havoc Pennington's avatar
Havoc Pennington committed
947 948 949 950 951 952 953 954 955 956 957 958
  guint keyval;
  guint *keyvals = NULL;
  gint n_vals = 0;
  gint i;
  
  /* See if the keyval is already a hex digit */
  if (is_hex_keyval (event->keyval))
    return event->keyval;

  /* See if this key would have generated a hex keyval in
   * any other state, and return that hex keyval if so
   */
959 960 961 962
  gdk_keymap_get_entries_for_keycode (keymap,
				      event->hardware_keycode,
				      NULL,
				      &keyvals, &n_vals);
Havoc Pennington's avatar
Havoc Pennington committed
963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981

  keyval = 0;
  i = 0;
  while (i < n_vals)
    {
      if (is_hex_keyval (keyvals[i]))
        {
          keyval = keyvals[i];
          break;
        }

      ++i;
    }

  g_free (keyvals);
  
  if (keyval)
    return keyval;
  else
982
    /* No way to make it a hex digit
Havoc Pennington's avatar
Havoc Pennington committed
983
     */
984
    return 0;
Havoc Pennington's avatar
Havoc Pennington committed
985 986 987 988 989 990 991
}

static gboolean
gtk_im_context_simple_filter_keypress (GtkIMContext *context,
				       GdkEventKey  *event)
{
  GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
992
  GtkIMContextSimplePrivate *priv = context_simple->priv;
993
  GdkDisplay *display = gdk_window_get_display (event->window);
994
  GSList *tmp_list;  
Havoc Pennington's avatar
Havoc Pennington committed
995
  int n_compose = 0;
996
  GdkModifierType hex_mod_mask;
997
  gboolean have_hex_mods;
998
  gboolean is_hex_start;
999
  gboolean is_hex_end;
1000
  gboolean is_backspace;
1001
  gboolean is_escape;
1002
  guint hex_keyval;
Havoc Pennington's avatar
Havoc Pennington committed
1003
  int i;
1004 1005 1006
  gboolean compose_finish;
  gboolean compose_match;
  gunichar output_char;
Havoc Pennington's avatar
Havoc Pennington committed
1007

1008
  while (priv->compose_buffer[n_compose] != 0)
1009 1010
    n_compose++;

1011 1012
  if (event->type == GDK_KEY_RELEASE)
    {
1013 1014
      if (priv->in_hex_sequence &&
	  (event->keyval == GDK_KEY_Control_L || event->keyval == GDK_KEY_Control_R ||
1015
	   event->keyval == GDK_KEY_Shift_L || event->keyval == GDK_KEY_Shift_R))
1016
	{
1017
	  if (priv->tentative_match &&
1018
	      g_unichar_validate (priv->tentative_match))
1019
	    {
1020 1021
	      gtk_im_context_simple_commit_char (context, priv->tentative_match);
	      priv->compose_buffer[0] = 0;
1022 1023

	    }
1024
	  else if (n_compose == 0)
1025
	    {
1026
	      priv->modifiers_dropped = TRUE;
1027
	    }
1028
	  else
1029 1030
	    {
	      /* invalid hex sequence */
1031
	      beep_window (event->window);
1032
	      
1033 1034 1035
	      priv->tentative_match = 0;
	      priv->in_hex_sequence = FALSE;
	      priv->compose_buffer[0] = 0;
1036
	      
1037 1038
	      g_signal_emit_by_name (context_simple, "preedit-changed");
	      g_signal_emit_by_name (context_simple, "preedit-end");
1039 1040
	    }

1041 1042 1043 1044
	  return TRUE;
	}
      else
	return FALSE;
1045
    }
1046

1047
  /* Ignore modifier key presses */
1048
  for (i = 0; i < G_N_ELEMENTS (gtk_compose_ignore); i++)
Havoc Pennington's avatar
Havoc Pennington committed
1049 1050 1051
    if (event->keyval == gtk_compose_ignore[i])
      return FALSE;

1052 1053 1054 1055
  hex_mod_mask = gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
                                               GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR);
  hex_mod_mask |= GDK_SHIFT_MASK;

1056
  if (priv->in_hex_sequence && priv->modifiers_dropped)
1057 1058
    have_hex_mods = TRUE;
  else
1059
    have_hex_mods = (event->state & (hex_mod_mask)) == hex_mod_mask;
1060
  is_hex_start = event->keyval == GDK_KEY_U;
1061 1062 1063 1064 1065
  is_hex_end = (event->keyval == GDK_KEY_space ||
		event->keyval == GDK_KEY_KP_Space ||
		event->keyval == GDK_KEY_Return ||
		event->keyval == GDK_KEY_ISO_Enter ||
		event->keyval == GDK_KEY_KP_Enter);
1066 1067
  is_backspace = event->keyval == GDK_KEY_BackSpace;
  is_escape = event->keyval == GDK_KEY_Escape;
1068 1069
  hex_keyval = canonical_hex_keyval (event);

Havoc Pennington's avatar
Havoc Pennington committed
1070
  /* If we are already in a non-hex sequence, or
1071 1072 1073 1074 1075
   * this keystroke is not hex modifiers + hex digit, don't filter
   * key events with accelerator modifiers held down. We only treat
   * Control and Alt as accel modifiers here, since Super, Hyper and
   * Meta are often co-located with Mode_Switch, Multi_Key or
   * ISO_Level3_Switch.
Havoc Pennington's avatar
Havoc Pennington committed
1076
   */
1077
  if (!have_hex_mods ||
1078 1079
      (n_compose > 0 && !priv->in_hex_sequence) ||
      (n_compose == 0 && !priv->in_hex_sequence && !is_hex_start) ||
1080
      (priv->in_hex_sequence && !hex_keyval &&
1081
       !is_hex_start && !is_hex_end && !is_escape && !is_backspace))
Havoc Pennington's avatar
Havoc Pennington committed
1082
    {
1083 1084 1085 1086 1087 1088 1089
      GdkModifierType no_text_input_mask;

      no_text_input_mask =
        gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
                                      GDK_MODIFIER_INTENT_NO_TEXT_INPUT);

      if (event->state & no_text_input_mask ||
1090
	  (priv->in_hex_sequence && priv->modifiers_dropped &&
1091 1092 1093
	   (event->keyval == GDK_KEY_Return ||
	    event->keyval == GDK_KEY_ISO_Enter ||
	    event->keyval == GDK_KEY_KP_Enter)))
1094 1095 1096
	{
	  return FALSE;
	}
1097 1098 1099
    }
  
  /* Handle backspace */
1100
  if (priv->in_hex_sequence && have_hex_mods && is_backspace)
1101 1102 1103 1104
    {
      if (n_compose > 0)
	{
	  n_compose--;
1105
	  priv->compose_buffer[n_compose] = 0;
1106
          check_hex (context_simple, n_compose);
1107 1108 1109
	}
      else
	{
1110
	  priv->in_hex_sequence = FALSE;
1111 1112
	}

1113
      g_signal_emit_by_name (context_simple, "preedit-changed");
1114

1115
      if (!priv->in_hex_sequence)
1116
        g_signal_emit_by_name (context_simple, "preedit-end");
1117 1118
      
      return TRUE;
Havoc Pennington's avatar
Havoc Pennington committed
1119
    }
1120

1121
  /* Check for hex sequence restart */
1122
  if (priv->in_hex_sequence && have_hex_mods && is_hex_start)
1123
    {
1124 1125
      if (priv->tentative_match &&
	  g_unichar_validate (priv->tentative_match))
1126
	{
1127 1128
	  gtk_im_context_simple_commit_char (context, priv->tentative_match);
	  priv->compose_buffer[0] = 0;
1129
	}
1130
      else 
1131 1132 1133
	{
	  /* invalid hex sequence */
	  if (n_compose > 0)
1134
	    beep_window (event->window);
1135
	  
1136 1137 1138
	  priv->tentative_match = 0;
	  priv->in_hex_sequence = FALSE;
	  priv->compose_buffer[0] = 0;
1139 1140
	}
    }
1141
  
1142
  /* Check for hex sequence start */
1143
  if (!priv->in_hex_sequence && have_hex_mods && is_hex_start)
1144
    {
1145 1146 1147 1148
      priv->compose_buffer[0] = 0;
      priv->in_hex_sequence = TRUE;
      priv->modifiers_dropped = FALSE;
      priv->tentative_match = 0;
1149

1150 1151
      g_signal_emit_by_name (context_simple, "preedit-start");
      g_signal_emit_by_name (context_simple, "preedit-changed");
Havoc Pennington's avatar
Havoc Pennington committed
1152
  
1153 1154
      return TRUE;
    }
1155
  
1156
  /* Then, check for compose sequences */
1157
  if (priv->in_hex_sequence)
1158 1159
    {
      if (hex_keyval)
1160
	priv->compose_buffer[n_compose++] = hex_keyval;
1161 1162 1163
      else if (is_escape)
	{
	  gtk_im_context_simple_reset (context);
1164
	  
1165 1166
	  return TRUE;
	}
1167
      else if (!is_hex_end)
1168 1169
	{
	  /* non-hex character in hex sequence */
1170
	  beep_window (event->window);
1171
	  
1172 1173 1174
	  return TRUE;
	}
    }
Havoc Pennington's avatar
Havoc Pennington committed
1175
  else
1176
    priv->compose_buffer[n_compose++] = event->keyval;
Havoc Pennington's avatar
Havoc Pennington committed
1177

1178
  priv->compose_buffer[n_compose] = 0;
Havoc Pennington's avatar
Havoc Pennington committed
1179

1180
  if (priv->in_hex_sequence)
Havoc Pennington's avatar
Havoc Pennington committed
1181 1182
    {
      /* If the modifiers are still held down, consider the sequence again */
1183
      if (have_hex_mods)
Havoc Pennington's avatar
Havoc Pennington committed
1184
        {
1185
          /* space or return ends the sequence, and we eat the key */
1186
          if (n_compose > 0 && is_hex_end)
Havoc Pennington's avatar
Havoc Pennington committed
1187
            {
1188 1189
	      if (priv->tentative_match &&
		  g_unichar_validate (priv->tentative_match))
1190
		{
1191 1192
		  gtk_im_context_simple_commit_char (context, priv->tentative_match);
		  priv->compose_buffer[0] = 0;
1193 1194 1195 1196
		}
	      else
		{
		  /* invalid hex sequence */
1197
		  beep_window (event->window);
1198

1199 1200 1201
		  priv->tentative_match = 0;
		  priv->in_hex_sequence = FALSE;
		  priv->compose_buffer[0] = 0;
1202
		}
Havoc Pennington's avatar
Havoc Pennington committed
1203
            }
1204
          else if (!check_hex (context_simple, n_compose))
1205
	    beep_window (event->window);
1206
	  
1207
	  g_signal_emit_by_name (context_simple, "preedit-changed");
1208

1209
	  if (!priv->in_hex_sequence)
1210
	    g_signal_emit_by_name (context_simple, "preedit-end");
1211

1212
	  return TRUE;
Havoc Pennington's avatar
Havoc Pennington committed
1213 1214 1215 1216
        }
    }
  else
    {
1217 1218
      gboolean success = FALSE;

1219
#ifdef GDK_WINDOWING_WIN32
1220
      if (GDK_IS_WIN32_DISPLAY (display))
1221
        {
1222 1223 1224 1225 1226 1227 1228
          guint16  output[2];
          gsize    output_size = 2;

          switch (gdk_win32_keymap_check_compose (GDK_WIN32_KEYMAP (gdk_keymap_get_default ()),
                                                  priv->compose_buffer,
                                                  n_compose,
                                                  output, &output_size))
1229
            {
1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243
            case GDK_WIN32_KEYMAP_MATCH_NONE:
              break;
            case GDK_WIN32_KEYMAP_MATCH_EXACT:
            case GDK_WIN32_KEYMAP_MATCH_PARTIAL:
              for (i = 0; i < output_size; i++)
                {
                  output_char = gdk_keyval_to_unicode (output[i]);
                  gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple),
                                                     output_char);
                }
              priv->compose_buffer[0] = 0;
              return TRUE;
            case GDK_WIN32_KEYMAP_MATCH_INCOMPLETE:
              return TRUE;
1244 1245 1246 1247
            }
        }
#endif

1248 1249
      G_LOCK (global_tables);

1250
      tmp_list = global_tables;
Havoc Pennington's avatar
Havoc Pennington committed
1251 1252 1253
      while (tmp_list)
        {
          if (check_table (context_simple, tmp_list->data, n_compose))
1254 1255 1256 1257
            {
              success = TRUE;
              break;
            }
Havoc Pennington's avatar
Havoc Pennington committed
1258 1259
          tmp_list = tmp_list->next;
        }
1260

1261 1262 1263 1264 1265
      G_UNLOCK (global_tables);

      if (success)
        return TRUE;

1266 1267 1268 1269 1270
#ifdef GDK_WINDOWING_WIN32
      if (check_win32_special_cases (context_simple, n_compose))
	return TRUE;
#endif

1271 1272 1273 1274 1275
#ifdef GDK_WINDOWING_QUARTZ
      if (check_quartz_special_cases (context_simple, n_compose))
        return TRUE;
#endif

1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
      if (gtk_check_compact_table (&gtk_compose_table_compact,
                                   priv->compose_buffer,
                                   n_compose, &compose_finish,
                                   &compose_match, &output_char))
        {
          if (compose_finish)
            {
              if (compose_match)
                {
                  gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple),
                                                     output_char);
#ifdef G_OS_WIN32
                  check_win32_special_case_after_compact_match (context_simple,
                                                                n_compose,
                                                                output_char);
#endif
                  priv->compose_buffer[0] = 0;
                }
            }
          else
            {
              if (compose_match)
                {
                  priv->tentative_match = output_char;
                  priv->tentative_match_len = n_compose;
                }
              if (output_char)
                g_signal_emit_by_name (context_simple, "preedit-changed");
            }

          return TRUE;
        }
1308
  
1309 1310 1311 1312 1313 1314 1315 1316 1317 1318
      if (gtk_check_algorithmically (priv->compose_buffer, n_compose, &output_char))
        {
          if (output_char)
            {
              gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple),
                                                 output_char);
              priv->compose_buffer[0] = 0;
            }
	  return TRUE;
        }
Havoc Pennington's avatar
Havoc Pennington committed
1319 1320 1321 1322 1323 1324
    }
  
  /* The current compose_buffer doesn't match anything */
  return no_sequence_matches (context_simple, n_compose, event);
}

Owen Taylor's avatar
Owen Taylor committed
1325 1326 1327 1328
static void
gtk_im_context_simple_reset (GtkIMContext *context)
{
  GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
1329
  GtkIMContextSimplePrivate *priv = context_simple->priv;
1330

1331
  priv->compose_buffer[0] = 0;
Owen Taylor's avatar
Owen Taylor committed
1332

1333
  if (priv->tentative_match || priv->in_hex_sequence)
1334
    {
1335 1336 1337
      priv->in_hex_sequence = FALSE;
      priv->tentative_match = 0;
      priv->tentative_match_len = 0;
1338 1339
      g_signal_emit_by_name (context_simple, "preedit-changed");
      g_signal_emit_by_name (context_simple, "preedit-end");
1340
    }
Owen Taylor's avatar
Owen Taylor committed
1341 1342 1343 1344 1345 1346 1347 1348
}

static void     
gtk_im_context_simple_get_preedit_string (GtkIMContext   *context,
					  gchar         **str,
					  PangoAttrList **attrs,
					  gint           *cursor_pos)
{
1349
  GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
1350
  GtkIMContextSimplePrivate *priv = context_simple->priv;
1351
  char outbuf[37]; /* up to 6 hex digits */
Owen Taylor's avatar