gtkimcontextsimple.c 40.5 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

Tor Lillqvist's avatar
Tor Lillqvist committed
29
#include <stdlib.h>
30
#include <string.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
31

Yevgen Muntyan's avatar
Yevgen Muntyan committed
32
#include "gtkprivate.h"
33
#include "gtkaccelgroup.h"
34
#include "gtkimcontextsimple.h"
35 36
#include "gtksettings.h"
#include "gtkwidget.h"
Matthias Clasen's avatar
Matthias Clasen committed
37
#include "gtkdebug.h"
Matthias Clasen's avatar
Matthias Clasen committed
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
Matthias Clasen's avatar
Matthias Clasen committed
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
G_LOCK_DEFINE_STATIC (global_tables);
static GSList *global_tables;
88

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

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

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

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

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

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

  return compose_file_dir;
}

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

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

      if (path == NULL)
        continue;

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

219
  g_free (x11_compose_file_dir);
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 258
  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);
}

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

Owen Taylor's avatar
Owen Taylor committed
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
Matthias Clasen's avatar
Matthias Clasen committed
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 465

	  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)
{
466
  GtkIMContextSimplePrivate *priv = context_simple->priv;
467

468 469 470 471
  /* On Windows user expectation is that typing two dead accents will input
   * two corresponding spacing accents.
   */
  if (n_compose == 2 &&
472 473
      priv->compose_buffer[0] == priv->compose_buffer[1] &&
      IS_DEAD_KEY (priv->compose_buffer[0]))
474 475 476 477 478 479 480 481
    {
      gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value);
      GTK_NOTE (MISC, g_print ("win32: U+%04X ", value));
    }
}

#endif

482 483 484 485 486 487 488
#ifdef GDK_WINDOWING_QUARTZ

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

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

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

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

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

  return FALSE;
}

#endif

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

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

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

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

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

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

589
  GTK_NOTE (MISC, g_print ("compact: %d ", *seq_index));
590
  seq = NULL;
591
  match = FALSE;
Matthias Clasen's avatar
Matthias Clasen committed
592
  value = 0;
593

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

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

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

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

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

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

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

648
/* This function receives a sequence of Unicode characters and tries to
649
 * normalize it (NFC). We check for the case where the resulting string
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
 * 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,
Matthias Clasen's avatar
Matthias Clasen committed
673 674
   * and if so, we replace tilde with perispomeni.
   */
675 676 677 678 679 680 681 682 683 684 685 686 687
  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);
Matthias Clasen's avatar
Matthias Clasen committed
688
      nfc_temp = g_utf8_normalize (combination_utf8_temp, -1, G_NORMALIZE_NFC);
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715

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

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

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

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

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

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

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

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

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

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

          return TRUE;
        }
802 803 804 805 806
    }

  return FALSE;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

static gboolean
gtk_im_context_simple_filter_keypress (GtkIMContext *context,
				       GdkEventKey  *event)
{
  GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
1006
  GtkIMContextSimplePrivate *priv = context_simple->priv;
1007
  GdkDisplay *display = gdk_window_get_display (event->window);
Havoc Pennington's avatar