gtkimcontextsimple.c 40.2 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 28
#include <gdk/gdk.h>

#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#ifdef GDK_WINDOWING_WAYLAND
#include <wayland/gdkwayland.h>
#endif

29
#include <stdlib.h>
30
#include <string.h>
31

32
#include "gtkprivate.h"
33
#include "gtkaccelgroup.h"
34
#include "gtkimcontextsimple.h"
35 36
#include "gtksettings.h"
#include "gtkwidget.h"
37
#include "gtkdebug.h"
38
#include "gtkintl.h"
39
#include "gtkcomposetable.h"
40

41
#include "gtkimcontextsimpleprivate.h"
42

43 44 45 46
/**
 * SECTION:gtkimcontextsimple
 * @Short_description: An input method context supporting table-based input methods
 * @Title: GtkIMContextSimple
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
 *
 * 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. ģ.
62 63
 */

64
struct _GtkIMContextSimplePrivate
65
{
66
  guint16        compose_buffer[GTK_MAX_COMPOSE_LEN + 1];
67 68 69 70 71 72 73
  gunichar       tentative_match;
  gint           tentative_match_len;

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

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

86 87
static GSList          *global_tables;

Matthias Clasen's avatar
Matthias Clasen committed
88
static const guint16 gtk_compose_ignore[] = {
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
  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
105 106
};

Owen Taylor's avatar
Owen Taylor committed
107 108 109 110 111 112 113 114
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);
115 116
static void     gtk_im_context_simple_set_client_window  (GtkIMContext             *context,
                                                          GdkWindow                *window);
117

118
G_DEFINE_TYPE_WITH_PRIVATE (GtkIMContextSimple, gtk_im_context_simple, GTK_TYPE_IM_CONTEXT)
119 120 121 122 123

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
124 125
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);

126
  im_context_class->filter_keypress = gtk_im_context_simple_filter_keypress;
Owen Taylor's avatar
Owen Taylor committed
127 128
  im_context_class->reset = gtk_im_context_simple_reset;
  im_context_class->get_preedit_string = gtk_im_context_simple_get_preedit_string;
129
  im_context_class->set_client_window = gtk_im_context_simple_set_client_window;
Owen Taylor's avatar
Owen Taylor committed
130
  gobject_class->finalize = gtk_im_context_simple_finalize;
131 132
}

133 134 135 136 137 138 139 140
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
141
  compose_file_dir = g_build_filename (_gtk_get_datadir (), "X11", "locale", NULL);
142 143 144 145 146
#endif

  return compose_file_dir;
}

147 148 149 150 151 152 153 154 155 156
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;
157
  gchar *x11_compose_file_dir = get_x11_compose_file_dir ();
158 159 160 161 162 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

  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)
            {
204
              path = g_build_filename (x11_compose_file_dir, *lang, "Compose", NULL);
205 206 207 208 209 210 211 212 213 214 215 216 217
              break;
            }
        }

      if (path == NULL)
        continue;

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

218
  g_free (x11_compose_file_dir);
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
  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)
{
  GtkIMContextSimple *im_context_simple;

  if (g_task_return_error_if_cancelled (task))
    return;

  g_return_if_fail (GTK_IS_IM_CONTEXT_SIMPLE (task_data));

  im_context_simple = GTK_IM_CONTEXT_SIMPLE (task_data);

  gtk_im_context_simple_init_compose_table (im_context_simple);
}

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);
  g_task_set_task_data (task, im_context_simple, NULL);
  g_task_run_in_thread (task, init_compose_table_thread_cb);
  g_object_unref (task);
}

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

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

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

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

292
  g_return_if_fail (g_unichar_validate (ch));
293

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

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

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

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

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

  return 0;
}

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

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

337 338 339 340 341 342
      i++;
    }

  return 0;
}

Owen Taylor's avatar
Owen Taylor committed
343 344 345 346 347
static gboolean
check_table (GtkIMContextSimple    *context_simple,
	     const GtkComposeTable *table,
	     gint                   n_compose)
{
348
  GtkIMContextSimplePrivate *priv = context_simple->priv;
Owen Taylor's avatar
Owen Taylor committed
349
  gint row_stride = table->max_seq_len + 2; 
Matthias Clasen's avatar
Matthias Clasen committed
350 351 352 353 354 355 356 357
  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;
  
358
  seq = bsearch (priv->compose_buffer,
Matthias Clasen's avatar
Matthias Clasen committed
359 360 361
		 table->data, table->n_seqs,
		 sizeof (guint16) *  row_stride, 
		 compare_seq);
Owen Taylor's avatar
Owen Taylor committed
362 363 364 365 366 367

  if (seq)
    {
      guint16 *prev_seq;

      /* Back up to the first sequence that matches to make sure
368
       * we find the exact match if there is one.
Owen Taylor's avatar
Owen Taylor committed
369 370 371 372
       */
      while (seq > table->data)
	{
	  prev_seq = seq - row_stride;
373
	  if (compare_seq (priv->compose_buffer, prev_seq) != 0)
Owen Taylor's avatar
Owen Taylor committed
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
	    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)
	    {
391
	      if (compare_seq (priv->compose_buffer, next_seq) == 0)
Owen Taylor's avatar
Owen Taylor committed
392
		{
393 394
		  priv->tentative_match = value;
		  priv->tentative_match_len = n_compose;
395

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

		  return TRUE;
		}
	    }

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

  return FALSE;
}

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

#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)
{
435
  GtkIMContextSimplePrivate *priv = context_simple->priv;
436
  if (n_compose == 2 &&
437
      priv->compose_buffer[1] == GDK_KEY_space)
438 439 440
    {
      gunichar value = 0;

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

	  GTK_NOTE (MISC, g_print ("win32: U+%04X\n", value));
	  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 480
    {
      gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value);
      GTK_NOTE (MISC, g_print ("win32: U+%04X ", value));
    }
}

#endif

481 482 483 484 485 486 487
#ifdef GDK_WINDOWING_QUARTZ

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

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

            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;
512
            case 'Y': value = GDK_KEY_Ydiaeresis; break;
513 514 515 516 517 518 519 520 521 522
            }
          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;
523
        }
524
    }
525

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

532 533
      GTK_NOTE (MISC, g_print ("quartz: U+%04X\n", value));
      return TRUE;
534 535 536 537 538 539 540
    }

  return FALSE;
}

#endif

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

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

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

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

  if (!seq_index)
577 578 579 580
    {
      GTK_NOTE (MISC, g_print ("compact: no\n"));
      return FALSE;
    }
581 582

  if (seq_index && n_compose == 1)
583 584 585 586
    {
      GTK_NOTE (MISC, g_print ("compact: yes\n"));
      return TRUE;
    }
587

588
  GTK_NOTE (MISC, g_print ("compact: %d ", *seq_index));
589
  seq = NULL;
590
  match = FALSE;
591
  value = 0;
592

593
  for (i = n_compose - 1; i < table->max_seq_len; i++)
594 595 596
    {
      row_stride = i + 1;

597
      if (seq_index[i + 1] - seq_index[i] > 0)
598
        {
599
          seq = bsearch (compose_buffer + 1,
600 601 602 603
                         table->data + seq_index[i],
                         (seq_index[i + 1] - seq_index[i]) / row_stride,
                         sizeof (guint16) *  row_stride,
                         compare_seq);
604

605
          if (seq)
606 607
            {
              if (i == n_compose - 1)
608 609 610 611
                {
                  value = seq[row_stride - 1];
                  match = TRUE;
                }
612 613
              else
                {
614 615
                  if (output_char)
                    *output_char = value;
616 617
                  if (match)
                    {
618 619
                      if (compose_match)
                        *compose_match = TRUE;
620 621 622 623 624
                      GTK_NOTE (MISC, g_print ("tentative match U+%04X ", value));
                    }

                  GTK_NOTE (MISC, g_print ("yes\n"));
                  return TRUE;
625 626 627 628 629
                }
             }
        }
    }

630
  if (match)
631
    {
632 633 634 635 636 637
      if (compose_match)
        *compose_match = TRUE;
      if (compose_finish)
        *compose_finish = TRUE;
      if (output_char)
        *output_char = value;
638

639
      GTK_NOTE (MISC, g_print ("U+%04X\n", value));
640 641 642
      return TRUE;
    }

643
  GTK_NOTE (MISC, g_print ("no\n"));
644 645 646
  return FALSE;
}

647
/* This function receives a sequence of Unicode characters and tries to
648
 * normalize it (NFC). We check for the case where the resulting string
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
 * 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,
672 673
   * and if so, we replace tilde with perispomeni.
   */
674 675 676 677 678 679 680 681 682 683 684 685 686
  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);
687
      nfc_temp = g_utf8_normalize (combination_utf8_temp, -1, G_NORMALIZE_NFC);
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

      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;
}

715
gboolean
716
gtk_check_algorithmically (const guint16       *compose_buffer,
717 718
                           gint                 n_compose,
                           gunichar            *output_char)
719 720

{
721
  gint i;
722 723 724
  gunichar combination_buffer[GTK_MAX_COMPOSE_LEN];
  gchar *combination_utf8, *nfc;

725 726 727
  if (output_char)
    *output_char = 0;

728 729 730
  if (n_compose >= GTK_MAX_COMPOSE_LEN)
    return FALSE;

731
  for (i = 0; i < n_compose && IS_DEAD_KEY (compose_buffer[i]); i++)
732 733 734 735 736 737
    ;
  if (i == n_compose)
    return TRUE;

  if (i > 0 && i == n_compose - 1)
    {
738
      combination_buffer[0] = gdk_keyval_to_unicode (compose_buffer[i]);
739 740 741 742
      combination_buffer[n_compose] = 0;
      i--;
      while (i >= 0)
	{
743
	  switch (compose_buffer[i])
744 745
	    {
#define CASE(keysym, unicode) \
746
	    case GDK_KEY_dead_##keysym: combination_buffer[i+1] = unicode; break
747 748 749 750

	    CASE (grave, 0x0300);
	    CASE (acute, 0x0301);
	    CASE (circumflex, 0x0302);
751
	    CASE (tilde, 0x0303);	/* Also used with perispomeni, 0x342. */
752 753 754 755
	    CASE (macron, 0x0304);
	    CASE (breve, 0x0306);
	    CASE (abovedot, 0x0307);
	    CASE (diaeresis, 0x0308);
756
	    CASE (hook, 0x0309);
757 758 759
	    CASE (abovering, 0x030A);
	    CASE (doubleacute, 0x030B);
	    CASE (caron, 0x030C);
760 761 762 763
	    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);
764
	    CASE (cedilla, 0x0327);
765 766
	    CASE (ogonek, 0x0328);	/* Legacy use for dasia, 0x314.*/
	    CASE (iota, 0x0345);
767
	    CASE (voiced_sound, 0x3099);	/* Per Markus Kuhn keysyms.txt file. */
768 769 770 771 772 773 774 775 776 777
	    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); */
778 779
#undef CASE
	    default:
780
	      combination_buffer[i+1] = gdk_keyval_to_unicode (compose_buffer[i]);
781 782 783 784
	    }
	  i--;
	}
      
785 786
      /* If the buffer normalizes to a single character, then modify the order
       * of combination_buffer accordingly, if necessary, and return TRUE.
787 788 789 790 791
       */
      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);
792

793 794
          if (output_char)
            *output_char = g_utf8_get_char (nfc);
795

796 797 798 799 800
          g_free (combination_utf8);
          g_free (nfc);

          return TRUE;
        }
801 802 803 804 805
    }

  return FALSE;
}

Havoc Pennington's avatar
Havoc Pennington committed
806
/* In addition to the table-driven sequences, we allow Unicode hex
807 808
 * codes to be entered. The method chosen here is similar to the
 * one recommended in ISO 14755, but not exactly the same, since we
809
 * don’t want to steal 16 valuable key combinations.
810
 *
811 812 813 814 815 816 817 818 819
 * 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
820 821
 */

822
static gboolean
Havoc Pennington's avatar
Havoc Pennington committed
823 824
check_hex (GtkIMContextSimple *context_simple,
           gint                n_compose)
825
{
826
  GtkIMContextSimplePrivate *priv = context_simple->priv;
Havoc Pennington's avatar
Havoc Pennington committed
827 828 829 830 831
  /* See if this is a hex sequence, return TRUE if so */
  gint i;
  GString *str;
  gulong n;
  gchar *nptr = NULL;
832
  gchar buf[7];
833

834 835
  priv->tentative_match = 0;
  priv->tentative_match_len = 0;
836

Havoc Pennington's avatar
Havoc Pennington committed
837 838 839 840 841 842 843
  str = g_string_new (NULL);
  
  i = 0;
  while (i < n_compose)
    {
      gunichar ch;
      
844
      ch = gdk_keyval_to_unicode (priv->compose_buffer[i]);
Havoc Pennington's avatar
Havoc Pennington committed
845 846 847
      
      if (ch == 0)
        return FALSE;
848

Havoc Pennington's avatar
Havoc Pennington committed
849 850
      if (!g_unichar_isxdigit (ch))
        return FALSE;
851

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

Havoc Pennington's avatar
Havoc Pennington committed
854 855 856 857
      g_string_append (str, buf);
      
      ++i;
    }
858

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

861
  /* If strtoul fails it probably means non-latin digits were used;
Havoc Pennington's avatar
Havoc Pennington committed
862 863
   * we should in principle handle that, but we probably don't.
   */
864
  if (nptr - str->str < str->len)
Owen Taylor's avatar
Owen Taylor committed
865
    {
Havoc Pennington's avatar
Havoc Pennington committed
866 867
      g_string_free (str, TRUE);
      return FALSE;
Owen Taylor's avatar
Owen Taylor committed
868
    }
Havoc Pennington's avatar
Havoc Pennington committed
869 870 871
  else
    g_string_free (str, TRUE);

872 873
  if (g_unichar_validate (n))
    {
874 875
      priv->tentative_match = n;
      priv->tentative_match_len = n_compose;
876
    }
Havoc Pennington's avatar
Havoc Pennington committed
877 878 879 880
  
  return TRUE;
}

881 882 883
static void
beep_window (GdkWindow *window)
{
884
  GdkScreen *screen = gdk_window_get_screen (window);
885
  gboolean   beep;
886

887 888 889
  g_object_get (gtk_settings_get_for_screen (screen),
                "gtk-error-bell", &beep,
                NULL);
890

891 892
  if (beep)
    gdk_window_beep (window);
893 894
}

Havoc Pennington's avatar
Havoc Pennington committed
895 896 897 898 899
static gboolean
no_sequence_matches (GtkIMContextSimple *context_simple,
                     gint                n_compose,
                     GdkEventKey        *event)
{
900
  GtkIMContextSimplePrivate *priv = context_simple->priv;
Havoc Pennington's avatar
Havoc Pennington committed
901 902
  GtkIMContext *context;
  gunichar ch;
Owen Taylor's avatar
Owen Taylor committed
903
  
Havoc Pennington's avatar
Havoc Pennington committed
904
  context = GTK_IM_CONTEXT (context_simple);
Owen Taylor's avatar
Owen Taylor committed
905 906 907 908
  
  /* No compose sequences found, check first if we have a partial
   * match pending.
   */
909
  if (priv->tentative_match)
Owen Taylor's avatar
Owen Taylor committed
910
    {
911
      gint len = priv->tentative_match_len;
Owen Taylor's avatar
Owen Taylor committed
912 913
      int i;
      
914 915
      gtk_im_context_simple_commit_char (context, priv->tentative_match);
      priv->compose_buffer[0] = 0;
Owen Taylor's avatar
Owen Taylor committed
916 917 918
      
      for (i=0; i < n_compose - len - 1; i++)
	{
919
	  GdkEvent *tmp_event = gdk_event_copy ((GdkEvent *)event);
920
	  tmp_event->key.keyval = priv->compose_buffer[len + i];
Owen Taylor's avatar
Owen Taylor committed
921
	  
922
	  gtk_im_context_filter_keypress (context, (GdkEventKey *)tmp_event);
923
	  gdk_event_free (tmp_event);
Owen Taylor's avatar
Owen Taylor committed
924
	}
925

Owen Taylor's avatar
Owen Taylor committed
926 927 928
      return gtk_im_context_filter_keypress (context, event);
    }
  else
929
    {
930
      priv->compose_buffer[0] = 0;
Owen Taylor's avatar
Owen Taylor committed
931
      if (n_compose > 1)		/* Invalid sequence */
932
	{
933
	  beep_window (event->window);
Owen Taylor's avatar
Owen Taylor committed
934
	  return TRUE;
935
	}
Owen Taylor's avatar
Owen Taylor committed
936 937
  
      ch = gdk_keyval_to_unicode (event->keyval);
938
      if (ch != 0 && !g_unichar_iscntrl (ch))
Owen Taylor's avatar
Owen Taylor committed
939 940 941 942 943 944
	{
	  gtk_im_context_simple_commit_char (context, ch);
	  return TRUE;
	}
      else
	return FALSE;
945
    }
Owen Taylor's avatar
Owen Taylor committed
946
}
947

Havoc Pennington's avatar
Havoc Pennington committed
948 949 950 951 952 953 954 955 956 957 958
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)
{
959
  GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_window_get_display (event->window));
Havoc Pennington's avatar
Havoc Pennington committed
960 961 962 963 964 965 966 967 968 969 970 971
  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
   */
972 973 974 975
  gdk_keymap_get_entries_for_keycode (keymap,
				      event->hardware_keycode,
				      NULL,
				      &keyvals, &n_vals);
Havoc Pennington's avatar
Havoc Pennington committed
976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994

  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
995
    /* No way to make it a hex digit
Havoc Pennington's avatar
Havoc Pennington committed
996
     */
997
    return 0;
Havoc Pennington's avatar
Havoc Pennington committed
998 999 1000 1001 1002 1003 1004
}

static gboolean
gtk_im_context_simple_filter_keypress (GtkIMContext *context,
				       GdkEventKey  *event)
{
  GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
1005
  GtkIMContextSimplePrivate *priv = context_simple->priv;
1006
  GdkDisplay *display = gdk_window_get_display (event->window);
Havoc Pennington's avatar
Havoc Pennington committed
1007 1008
  GSList *tmp_list;  
  int n_compose = 0;
1009
  GdkModifierType hex_mod_mask;
1010
  gboolean have_hex_mods;
1011
  gboolean is_hex_start;
1012
  gboolean is_hex_end;
1013
  gboolean is_backspace;
1014
  gboolean is_escape;
1015
  guint hex_keyval;
Havoc Pennington's avatar
Havoc Pennington committed
1016
  int i;
1017 1018 1019
  gboolean compose_finish;
  gboolean compose_match;
  gunichar output_char;
Havoc Pennington's avatar
Havoc Pennington committed
1020

1021
  while (priv->compose_buffer[n_compose] != 0)
1022 1023
    n_compose++;

1024 1025
  if (event->type == GDK_KEY_RELEASE)
    {
1026
      if (priv->in_hex_sequence &&
1027 1028
	  (event->keyval == GDK_KEY_Control_L || event->keyval == GDK_KEY_Control_R ||
	   event->keyval == GDK_KEY_Shift_L || event->keyval == GDK_KEY_Shift_R))
1029
	{
1030 1031
	  if (priv->tentative_match &&
	      g_unichar_validate (priv->tentative_match))
1032
	    {
1033 1034
	      gtk_im_context_simple_commit_char (context, priv->tentative_match);
	      priv->compose_buffer[0] = 0;
1035 1036 1037 1038

	    }
	  else if (n_compose == 0)
	    {
1039
	      priv->modifiers_dropped = TRUE;
1040 1041
	    }
	  else
1042 1043
	    {
	      /* invalid hex sequence */
1044
	      beep_window (event->window);
1045
	      
1046 1047 1048
	      priv->tentative_match = 0;
	      priv->in_hex_sequence = FALSE;
	      priv->compose_buffer[0] = 0;
1049
	      
1050 1051
	      g_signal_emit_by_name (context_simple, "preedit-changed");
	      g_signal_emit_by_name (context_simple, "preedit-end");
1052
	    }
1053 1054 1055 1056 1057 1058

	  return TRUE;
	}
      else
	return FALSE;
    }
1059

1060
  /* Ignore modifier key presses */
1061
  for (i = 0; i < G_N_ELEMENTS (gtk_compose_ignore); i++)
Havoc Pennington's avatar
Havoc Pennington committed
1062 1063 1064
    if (event->keyval == gtk_compose_ignore[i])
      return FALSE;

1065 1066 1067 1068
  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;

1069
  if (priv->in_hex_sequence && priv->modifiers_dropped)
1070 1071
    have_hex_mods = TRUE;
  else
1072
    have_hex_mods = (event->state & (hex_mod_mask)) == hex_mod_mask;
1073 1074 1075 1076 1077 1078 1079 1080
  is_hex_start = event->keyval == GDK_KEY_U;
  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);
  is_backspace = event->keyval == GDK_KEY_BackSpace;
  is_escape = event->keyval == GDK_KEY_Escape;
1081 1082
  hex_keyval = canonical_hex_keyval (event);

Havoc Pennington's avatar
Havoc Pennington committed
1083
  /* If we are already in a non-hex sequence, or
1084 1085 1086 1087 1088
   * 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
1089
   */
1090
  if (!have_hex_mods ||
1091 1092 1093
      (n_compose > 0 && !priv->in_hex_sequence) ||
      (n_compose == 0 && !priv->in_hex_sequence && !is_hex_start) ||
      (priv->in_hex_sequence && !hex_keyval &&
1094
       !is_hex_start && !is_hex_end && !is_escape && !is_backspace))
Havoc Pennington's avatar
Havoc Pennington committed
1095
    {
1096 1097 1098 1099 1100 1101 1102
      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 ||
1103
	  (priv->in_hex_sequence && priv->modifiers_dropped &&
1104 1105 1106
	   (event->keyval == GDK_KEY_Return ||
	    event->keyval == GDK_KEY_ISO_Enter ||
	    event->keyval == GDK_KEY_KP_Enter)))
1107 1108 1109
	{
	  return FALSE;
	}
1110 1111 1112
    }
  
  /* Handle backspace */
1113
  if (priv->in_hex_sequence && have_hex_mods && is_backspace)
1114 1115 1116 1117
    {
      if (n_compose > 0)
	{
	  n_compose--;
1118
	  priv->compose_buffer[n_compose] = 0;
1119 1120 1121 1122
          check_hex (context_simple, n_compose);
	}
      else
	{
1123
	  priv->in_hex_sequence = FALSE;
1124 1125
	}

1126
      g_signal_emit_by_name (context_simple, "preedit-changed");
1127

1128
      if (!priv->in_hex_sequence)
1129
        g_signal_emit_by_name (context_simple, "preedit-end");
1130 1131
      
      return TRUE;
Havoc Pennington's avatar
Havoc Pennington committed
1132
    }
1133

1134
  /* Check for hex sequence restart */
1135
  if (priv->in_hex_sequence && have_hex_mods && is_hex_start)
1136
    {
1137 1138
      if (priv->tentative_match &&
	  g_unichar_validate (priv->tentative_match))
1139
	{
1140 1141
	  gtk_im_context_simple_commit_char (context, priv->tentative_match);
	  priv->compose_buffer[0] = 0;
1142 1143 1144 1145 1146
	}
      else 
	{
	  /* invalid hex sequence */
	  if (n_compose > 0)
1147
	    beep_window (event->window);
1148
	  
1149 1150 1151
	  priv->tentative_match = 0;
	  priv->in_hex_sequence = FALSE;
	  priv->compose_buffer[0] = 0;
1152 1153
	}
    }
1154
  
1155
  /* Check for hex sequence start */
1156
  if (!priv->in_hex_sequence && have_hex_mods && is_hex_start)
1157
    {
1158 1159 1160 1161
      priv->compose_buffer[0] = 0;
      priv->in_hex_sequence = TRUE;
      priv->modifiers_dropped = FALSE;
      priv->tentative_match = 0;
1162

1163 1164
      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
1165
  
1166 1167
      return TRUE;
    }
1168
  
1169
  /* Then, check for compose sequences */
1170
  if (priv->in_hex_sequence)
1171 1172
    {
      if (hex_keyval)
1173
	priv->compose_buffer[n_compose++] = hex_keyval;
1174 1175 1176 1177 1178 1179 1180
      else if (is_escape)
	{
	  gtk_im_context_simple_reset (context);
	  
	  return TRUE;
	}
      else if (!is_hex_end)
1181 1182
	{
	  /* non-hex character in hex sequence */
1183
	  beep_window (event->window);
1184 1185 1186 1187
	  
	  return TRUE;
	}
    }
Havoc Pennington's avatar
Havoc Pennington committed
1188
  else
1189
    priv->compose_buffer[n_compose++] = event->keyval;
Havoc Pennington's avatar
Havoc Pennington committed
1190

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

1193
  if (priv->in_hex_sequence)
Havoc Pennington's avatar
Havoc Pennington committed
1194 1195
    {
      /* If the modifiers are still held down, consider the sequence again */
1196
      if (have_hex_mods)
Havoc Pennington's avatar
Havoc Pennington committed
1197
        {
1198 1199
          /* space or return ends the sequence, and we eat the key */
          if (n_compose > 0 && is_hex_end)
Havoc Pennington's avatar
Havoc Pennington committed
1200
            {
1201 1202
	      if (priv->tentative_match &&
		  g_unichar_validate (priv->tentative_match))
1203
		{
1204 1205
		  gtk_im_context_simple_commit_char (context, priv->tentative_match);
		  priv->compose_buffer[0] = 0;
1206 1207 1208 1209
		}
	      else
		{
		  /* invalid hex sequence */
1210
		  beep_window (event->window);
1211

1212 1213 1214
		  priv->tentative_match = 0;
		  priv->in_hex_sequence = FALSE;
		  priv->compose_buffer[0] = 0;
1215
		}
Havoc Pennington's avatar
Havoc Pennington committed
1216
            }
1217
          else if (!check_hex (context_simple, n_compose))
1218
	    beep_window (event->window);
1219
	  
1220
	  g_signal_emit_by_name (context_simple, "preedit-changed");
1221

1222
	  if (!priv->in_hex_sequence)
1223
	    g_signal_emit_by_name (context_simple, "preedit-end");
1224

1225
	  return TRUE;
Havoc Pennington's avatar
Havoc Pennington committed
1226 1227 1228 1229
        }
    }
  else
    {
1230
      tmp_list = global_tables;
Havoc Pennington's avatar
Havoc Pennington committed
1231 1232 1233 1234 1235 1236
      while (tmp_list)
        {
          if (check_table (context_simple, tmp_list->data, n_compose))
            return TRUE;
          tmp_list = tmp_list->next;
        }
1237

1238 1239 1240 1241
      GTK_NOTE (MISC, {
	  g_print ("[ ");
	  for (i = 0; i < n_compose; i++)
	    {
1242
	      const gchar *keyval_name = gdk_keyval_name (priv->compose_buffer[i]);
1243 1244 1245 1246
	      
	      if (keyval_name != NULL)
		g_print ("%s ", keyval_name);
	      else
1247
		g_print ("%04x ", priv->compose_buffer[i]);
1248 1249 1250 1251 1252 1253 1254 1255 1256
	    }
	  g_print ("] ");
	});

#ifdef GDK_WINDOWING_WIN32
      if (check_win32_special_cases (context_simple, n_compose))
	return TRUE;
#endif

1257 1258 1259 1260 1261
#ifdef GDK_WINDOWING_QUARTZ
      if (check_quartz_special_cases (context_simple, n_compose))
        return TRUE;
#endif

1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293
      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;
        }
1294
  
1295 1296 1297 1298 1299 1300 1301 1302 1303 1304
      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
1305 1306 1307 1308 1309 1310
    }
  
  /* The current compose_buffer doesn't match anything */
  return no_sequence_matches (context_simple, n_compose, event);
}

Owen Taylor's avatar
Owen Taylor committed
1311 1312 1313 1314
static void
gtk_im_context_simple_reset (GtkIMContext *context)
{
  GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
1315
  GtkIMContextSimplePrivate *priv = context_simple->priv;
1316

1317
  priv->compose_buffer[0] = 0;
Owen Taylor's avatar
Owen Taylor committed
1318

1319
  if (priv->tentative_match || priv->in_hex_sequence)
1320
    {
1321 1322 1323
      priv->in_hex_sequence = FALSE;
      priv->tentative_match = 0;
      priv->tentative_match_len = 0;
1324 1325
      g_signal_emit_by_name (context_simple, "preedit-changed");
      g_signal_emit_by_name (context_simple, "preedit-end");
1326
    }
Owen Taylor's avatar
Owen Taylor committed
1327 1328 1329 1330 1331 1332 1333 1334
}

static void     
gtk_im_context_simple_get_preedit_string (GtkIMContext   *context,
					  gchar         **str,
					  PangoAttrList **attrs,
					  gint           *cursor_pos)
{
1335
  GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
1336
  GtkIMContextSimplePrivate *priv = context_simple->priv;
1337
  char outbuf[37]; /* up to 6 hex digits */
Owen Taylor's avatar
Owen Taylor committed
1338 1339
  int len = 0;

1340
  if (priv->in_hex_sequence)
Havoc Pennington's avatar
Havoc Pennington committed
1341
    {
1342 1343 1344 1345 1346
      int hexchars = 0;
         
      outbuf[0] = 'u';
      len = 1;

1347
      while (priv->compose_buffer[hexchars] != 0)
1348
	{
1349
	  len += g_unichar_to_utf8 (gdk_keyval_to_unicode (priv->compose_buffer[hexchars]),
1350 1351 1352 1353
				    outbuf + len);
	  ++hexchars;
	}

1354
      g_assert (len < 25);
Havoc Pennington's avatar
Havoc Pennington committed
1355
    }
1356 1357
  else if (priv->tentative_match)
    len = g_unichar_to_utf8 (priv->tentative_match, outbuf);
1358
      
1359 1360
  outbuf[len] = '\0';      

Owen Taylor's avatar
Owen Taylor committed
1361
  if (str)
Havoc Pennington's avatar
Havoc Pennington committed
1362
    *str = g_strdup (outbuf);
Owen Taylor's avatar
Owen Taylor committed
1363 1364

  if (attrs)
1365
    {
1366
      *attrs = pango_attr_list_new ();
Havoc Pennington's avatar
Havoc Pennington committed
1367
      
Owen Taylor's avatar