]> Pileus Git - ~andy/gtk/blob - gtk/gtkimcontextsimple.c
GtkImContextSimple: fix tentative match handling
[~andy/gtk] / gtk / gtkimcontextsimple.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2000 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * 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
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "gtkprivate.h"
26 #include "gtkaccelgroup.h"
27 #include "gtkimcontextsimple.h"
28 #include "gtksettings.h"
29 #include "gtkwidget.h"
30 #include "gtkdebug.h"
31 #include "gtkintl.h"
32
33
34 /**
35  * SECTION:gtkimcontextsimple
36  * @Short_description: An input method context supporting table-based input methods
37  * @Title: GtkIMContextSimple
38  */
39
40
41 typedef struct _GtkComposeTable GtkComposeTable;
42 typedef struct _GtkComposeTableCompact GtkComposeTableCompact;
43
44 struct _GtkIMContextSimplePrivate
45 {
46   GSList        *tables;
47
48   guint          compose_buffer[GTK_MAX_COMPOSE_LEN + 1];
49   gunichar       tentative_match;
50   gint           tentative_match_len;
51
52   guint          in_hex_sequence : 1;
53   guint          modifiers_dropped : 1;
54 };
55
56 struct _GtkComposeTable 
57 {
58   const guint16 *data;
59   gint max_seq_len;
60   gint n_seqs;
61 };
62
63 struct _GtkComposeTableCompact
64 {
65   const guint16 *data;
66   gint max_seq_len;
67   gint n_index_size;
68   gint n_index_stride;
69 };
70
71 /* This file contains the table of the compose sequences,
72  * static const guint16 gtk_compose_seqs_compact[] = {}
73  * It is generated from the compose-parse.py script.
74  */
75 #include "gtkimcontextsimpleseqs.h"
76
77 /* From the values below, the value 24 means the number of different first keysyms
78  * that exist in the Compose file (from Xorg). When running compose-parse.py without
79  * parameters, you get the count that you can put here. Needed when updating the
80  * gtkimcontextsimpleseqs.h header file (contains the compose sequences).
81  */
82 static const GtkComposeTableCompact gtk_compose_table_compact = {
83   gtk_compose_seqs_compact,
84   5,
85   24,
86   6
87 };
88
89 static const guint16 gtk_compose_ignore[] = {
90   GDK_KEY_Shift_L,
91   GDK_KEY_Shift_R,
92   GDK_KEY_Control_L,
93   GDK_KEY_Control_R,
94   GDK_KEY_Caps_Lock,
95   GDK_KEY_Shift_Lock,
96   GDK_KEY_Meta_L,
97   GDK_KEY_Meta_R,
98   GDK_KEY_Alt_L,
99   GDK_KEY_Alt_R,
100   GDK_KEY_Super_L,
101   GDK_KEY_Super_R,
102   GDK_KEY_Hyper_L,
103   GDK_KEY_Hyper_R,
104   GDK_KEY_Mode_switch,
105   GDK_KEY_ISO_Level3_Shift
106 };
107
108 static void     gtk_im_context_simple_finalize           (GObject                  *obj);
109 static gboolean gtk_im_context_simple_filter_keypress    (GtkIMContext             *context,
110                                                           GdkEventKey              *key);
111 static void     gtk_im_context_simple_reset              (GtkIMContext             *context);
112 static void     gtk_im_context_simple_get_preedit_string (GtkIMContext             *context,
113                                                           gchar                   **str,
114                                                           PangoAttrList           **attrs,
115                                                           gint                     *cursor_pos);
116
117 G_DEFINE_TYPE (GtkIMContextSimple, gtk_im_context_simple, GTK_TYPE_IM_CONTEXT)
118
119 static void
120 gtk_im_context_simple_class_init (GtkIMContextSimpleClass *class)
121 {
122   GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
123   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
124
125   im_context_class->filter_keypress = gtk_im_context_simple_filter_keypress;
126   im_context_class->reset = gtk_im_context_simple_reset;
127   im_context_class->get_preedit_string = gtk_im_context_simple_get_preedit_string;
128   gobject_class->finalize = gtk_im_context_simple_finalize;
129
130   g_type_class_add_private (class, sizeof (GtkIMContextSimplePrivate));
131 }
132
133 static void
134 gtk_im_context_simple_init (GtkIMContextSimple *im_context_simple)
135 {
136   im_context_simple->priv = G_TYPE_INSTANCE_GET_PRIVATE (im_context_simple,
137                                                          GTK_TYPE_IM_CONTEXT_SIMPLE,
138                                                          GtkIMContextSimplePrivate);
139 }
140
141 static void
142 gtk_im_context_simple_finalize (GObject *obj)
143 {
144   GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (obj);
145   GtkIMContextSimplePrivate *priv = context_simple->priv;
146
147   if (priv->tables)
148     {
149       g_slist_foreach (priv->tables, (GFunc)g_free, NULL);
150       g_slist_free (priv->tables);
151
152       priv->tables = NULL;
153     }
154
155   G_OBJECT_CLASS (gtk_im_context_simple_parent_class)->finalize (obj);
156 }
157
158 /**
159  * gtk_im_context_simple_new:
160  * 
161  * Creates a new #GtkIMContextSimple.
162  *
163  * Returns: a new #GtkIMContextSimple.
164  **/
165 GtkIMContext *
166 gtk_im_context_simple_new (void)
167 {
168   return g_object_new (GTK_TYPE_IM_CONTEXT_SIMPLE, NULL);
169 }
170
171 static void
172 gtk_im_context_simple_commit_char (GtkIMContext *context,
173                                    gunichar ch)
174 {
175   GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
176   GtkIMContextSimplePrivate *priv = context_simple->priv;
177   gchar buf[10];
178   gint len;
179
180   g_return_if_fail (g_unichar_validate (ch));
181
182   len = g_unichar_to_utf8 (ch, buf);
183   buf[len] = '\0';
184
185   if (priv->tentative_match || priv->in_hex_sequence)
186     {
187       priv->in_hex_sequence = FALSE;
188       priv->tentative_match = 0;
189       priv->tentative_match_len = 0;
190       g_signal_emit_by_name (context_simple, "preedit-changed");
191       g_signal_emit_by_name (context_simple, "preedit-end");
192     }
193
194   g_signal_emit_by_name (context, "commit", &buf);
195 }
196
197 static int
198 compare_seq_index (const void *key, const void *value)
199 {
200   const guint *keysyms = key;
201   const guint16 *seq = value;
202
203   if (keysyms[0] < seq[0])
204     return -1;
205   else if (keysyms[0] > seq[0])
206     return 1;
207
208   return 0;
209 }
210
211 static int
212 compare_seq (const void *key, const void *value)
213 {
214   int i = 0;
215   const guint *keysyms = key;
216   const guint16 *seq = value;
217
218   while (keysyms[i])
219     {
220       if (keysyms[i] < seq[i])
221         return -1;
222       else if (keysyms[i] > seq[i])
223         return 1;
224
225       i++;
226     }
227
228   return 0;
229 }
230
231 static gboolean
232 check_table (GtkIMContextSimple    *context_simple,
233              const GtkComposeTable *table,
234              gint                   n_compose)
235 {
236   GtkIMContextSimplePrivate *priv = context_simple->priv;
237   gint row_stride = table->max_seq_len + 2; 
238   guint16 *seq; 
239   
240   /* Will never match, if the sequence in the compose buffer is longer
241    * than the sequences in the table.  Further, compare_seq (key, val)
242    * will overrun val if key is longer than val. */
243   if (n_compose > table->max_seq_len)
244     return FALSE;
245   
246   seq = bsearch (priv->compose_buffer,
247                  table->data, table->n_seqs,
248                  sizeof (guint16) *  row_stride, 
249                  compare_seq);
250
251   if (seq)
252     {
253       guint16 *prev_seq;
254
255       /* Back up to the first sequence that matches to make sure
256        * we find the exact match if there is one.
257        */
258       while (seq > table->data)
259         {
260           prev_seq = seq - row_stride;
261           if (compare_seq (priv->compose_buffer, prev_seq) != 0)
262             break;
263           seq = prev_seq;
264         }
265       
266       if (n_compose == table->max_seq_len ||
267           seq[n_compose] == 0) /* complete sequence */
268         {
269           guint16 *next_seq;
270           gunichar value = 
271             0x10000 * seq[table->max_seq_len] + seq[table->max_seq_len + 1];
272
273           /* We found a tentative match. See if there are any longer
274            * sequences containing this subsequence
275            */
276           next_seq = seq + row_stride;
277           if (next_seq < table->data + row_stride * table->n_seqs)
278             {
279               if (compare_seq (priv->compose_buffer, next_seq) == 0)
280                 {
281                   priv->tentative_match = value;
282                   priv->tentative_match_len = n_compose;
283
284                   g_signal_emit_by_name (context_simple, "preedit-changed");
285
286                   return TRUE;
287                 }
288             }
289
290           gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value);
291           priv->compose_buffer[0] = 0;
292         }
293       
294       return TRUE;
295     }
296
297   return FALSE;
298 }
299
300 /* Checks if a keysym is a dead key. Dead key keysym values are defined in
301  * ../gdk/gdkkeysyms.h and the first is GDK_KEY_dead_grave. As X.Org is updated,
302  * more dead keys are added and we need to update the upper limit.
303  * Currently, the upper limit is GDK_KEY_dead_dasia+1. The +1 has to do with
304  * a temporary issue in the X.Org header files.
305  * In future versions it will be just the keysym (no +1).
306  */
307 #define IS_DEAD_KEY(k) \
308     ((k) >= GDK_KEY_dead_grave && (k) <= (GDK_KEY_dead_dasia+1))
309
310 #ifdef GDK_WINDOWING_WIN32
311
312 /* On Windows, user expectation is that typing a dead accent followed
313  * by space will input the corresponding spacing character. The X
314  * compose tables are different for dead acute and diaeresis, which
315  * when followed by space produce a plain ASCII apostrophe and double
316  * quote respectively. So special-case those.
317  */
318
319 static gboolean
320 check_win32_special_cases (GtkIMContextSimple    *context_simple,
321                            gint                   n_compose)
322 {
323   GtkIMContextSimplePrivate *priv = context_simple->priv;
324   if (n_compose == 2 &&
325       priv->compose_buffer[1] == GDK_KEY_space)
326     {
327       gunichar value = 0;
328
329       switch (priv->compose_buffer[0])
330         {
331         case GDK_KEY_dead_acute:
332           value = 0x00B4; break;
333         case GDK_KEY_dead_diaeresis:
334           value = 0x00A8; break;
335         }
336       if (value > 0)
337         {
338           gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value);
339           priv->compose_buffer[0] = 0;
340
341           GTK_NOTE (MISC, g_print ("win32: U+%04X\n", value));
342           return TRUE;
343         }
344     }
345   return FALSE;
346 }
347
348 static void
349 check_win32_special_case_after_compact_match (GtkIMContextSimple    *context_simple,
350                                               gint                   n_compose,
351                                               guint                  value)
352 {
353   GtkIMContextSimplePrivate *priv = context_simple->priv;
354
355   /* On Windows user expectation is that typing two dead accents will input
356    * two corresponding spacing accents.
357    */
358   if (n_compose == 2 &&
359       priv->compose_buffer[0] == priv->compose_buffer[1] &&
360       IS_DEAD_KEY (priv->compose_buffer[0]))
361     {
362       gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value);
363       GTK_NOTE (MISC, g_print ("win32: U+%04X ", value));
364     }
365 }
366
367 #endif
368
369 static gboolean
370 check_compact_table (GtkIMContextSimple           *context_simple,
371                      const GtkComposeTableCompact *table,
372                      gint                          n_compose)
373 {
374   GtkIMContextSimplePrivate *priv = context_simple->priv;
375   gint row_stride;
376   guint16 *seq_index;
377   guint16 *seq;
378   gint i;
379   gboolean match;
380   gunichar value;
381
382   /* Will never match, if the sequence in the compose buffer is longer
383    * than the sequences in the table.  Further, compare_seq (key, val)
384    * will overrun val if key is longer than val.
385    */
386   if (n_compose > table->max_seq_len)
387     return FALSE;
388
389   seq_index = bsearch (priv->compose_buffer,
390                        table->data,
391                        table->n_index_size,
392                        sizeof (guint16) * table->n_index_stride,
393                        compare_seq_index);
394
395   if (!seq_index)
396     {
397       GTK_NOTE (MISC, g_print ("compact: no\n"));
398       return FALSE;
399     }
400
401   if (seq_index && n_compose == 1)
402     {
403       GTK_NOTE (MISC, g_print ("compact: yes\n"));
404       return TRUE;
405     }
406
407   GTK_NOTE (MISC, g_print ("compact: %d ", *seq_index));
408   seq = NULL;
409   match = FALSE;
410
411   for (i = n_compose - 1; i < table->max_seq_len; i++)
412     {
413       row_stride = i + 1;
414
415       if (seq_index[i + 1] - seq_index[i] > 0)
416         {
417           seq = bsearch (priv->compose_buffer + 1,
418                          table->data + seq_index[i],
419                          (seq_index[i + 1] - seq_index[i]) / row_stride,
420                          sizeof (guint16) *  row_stride,
421                          compare_seq);
422
423           if (seq)
424             {
425               if (i == n_compose - 1)
426                 {
427                   value = seq[row_stride - 1];
428                   match = TRUE;
429                 }
430               else
431                 {
432                   if (match)
433                     {
434                       GTK_NOTE (MISC, g_print ("tentative match U+%04X ", value));
435                       priv->tentative_match = value;
436                       priv->tentative_match_len = n_compose;
437                     }
438
439                   g_signal_emit_by_name (context_simple, "preedit-changed");
440
441                   GTK_NOTE (MISC, g_print ("yes\n"));
442                   return TRUE;
443                 }
444              }
445         }
446     }
447
448   if (match)
449     {
450       gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value);
451 #ifdef G_OS_WIN32
452       check_win32_special_case_after_compact_match (context_simple, n_compose, value);
453 #endif
454       priv->compose_buffer[0] = 0;
455
456       GTK_NOTE (MISC, g_print ("U+%04X\n", value));
457       return TRUE;
458     }
459
460   GTK_NOTE (MISC, g_print ("no\n"));
461   return FALSE;
462 }
463
464 /* This function receives a sequence of Unicode characters and tries to
465  * normalize it (NFC). We check for the case the the resulting string
466  * has length 1 (single character).
467  * NFC normalisation normally rearranges diacritic marks, unless these
468  * belong to the same Canonical Combining Class.
469  * If they belong to the same canonical combining class, we produce all
470  * permutations of the diacritic marks, then attempt to normalize.
471  */
472 static gboolean
473 check_normalize_nfc (gunichar* combination_buffer, gint n_compose)
474 {
475   gunichar combination_buffer_temp[GTK_MAX_COMPOSE_LEN];
476   gchar *combination_utf8_temp = NULL;
477   gchar *nfc_temp = NULL;
478   gint n_combinations;
479   gunichar temp_swap;
480   gint i;
481
482   n_combinations = 1;
483
484   for (i = 1; i < n_compose; i++ )
485      n_combinations *= i;
486
487   /* Xorg reuses dead_tilde for the perispomeni diacritic mark.
488    * We check if base character belongs to Greek Unicode block,
489    * and if so, we replace tilde with perispomeni.
490    */
491   if (combination_buffer[0] >= 0x390 && combination_buffer[0] <= 0x3FF)
492     {
493       for (i = 1; i < n_compose; i++ )
494         if (combination_buffer[i] == 0x303)
495           combination_buffer[i] = 0x342;
496     }
497
498   memcpy (combination_buffer_temp, combination_buffer, GTK_MAX_COMPOSE_LEN * sizeof (gunichar) );
499
500   for (i = 0; i < n_combinations; i++ )
501     {
502       g_unicode_canonical_ordering (combination_buffer_temp, n_compose);
503       combination_utf8_temp = g_ucs4_to_utf8 (combination_buffer_temp, -1, NULL, NULL, NULL);
504       nfc_temp = g_utf8_normalize (combination_utf8_temp, -1, G_NORMALIZE_NFC);
505
506       if (g_utf8_strlen (nfc_temp, -1) == 1)
507         {
508           memcpy (combination_buffer, combination_buffer_temp, GTK_MAX_COMPOSE_LEN * sizeof (gunichar) );
509
510           g_free (combination_utf8_temp);
511           g_free (nfc_temp);
512
513           return TRUE;
514         }
515
516       g_free (combination_utf8_temp);
517       g_free (nfc_temp);
518
519       if (n_compose > 2)
520         {
521           temp_swap = combination_buffer_temp[i % (n_compose - 1) + 1];
522           combination_buffer_temp[i % (n_compose - 1) + 1] = combination_buffer_temp[(i+1) % (n_compose - 1) + 1];
523           combination_buffer_temp[(i+1) % (n_compose - 1) + 1] = temp_swap;
524         }
525       else
526         break;
527     }
528
529   return FALSE;
530 }
531
532 static gboolean
533 check_algorithmically (GtkIMContextSimple *context_simple,
534                        gint                n_compose)
535
536 {
537   GtkIMContextSimplePrivate *priv = context_simple->priv;
538   gint i;
539   gunichar combination_buffer[GTK_MAX_COMPOSE_LEN];
540   gchar *combination_utf8, *nfc;
541
542   if (n_compose >= GTK_MAX_COMPOSE_LEN)
543     return FALSE;
544
545   for (i = 0; i < n_compose && IS_DEAD_KEY (priv->compose_buffer[i]); i++)
546     ;
547   if (i == n_compose)
548     return TRUE;
549
550   if (i > 0 && i == n_compose - 1)
551     {
552       combination_buffer[0] = gdk_keyval_to_unicode (priv->compose_buffer[i]);
553       combination_buffer[n_compose] = 0;
554       i--;
555       while (i >= 0)
556         {
557           switch (priv->compose_buffer[i])
558             {
559 #define CASE(keysym, unicode) \
560             case GDK_KEY_dead_##keysym: combination_buffer[i+1] = unicode; break
561
562             CASE (grave, 0x0300);
563             CASE (acute, 0x0301);
564             CASE (circumflex, 0x0302);
565             CASE (tilde, 0x0303);       /* Also used with perispomeni, 0x342. */
566             CASE (macron, 0x0304);
567             CASE (breve, 0x0306);
568             CASE (abovedot, 0x0307);
569             CASE (diaeresis, 0x0308);
570             CASE (hook, 0x0309);
571             CASE (abovering, 0x030A);
572             CASE (doubleacute, 0x030B);
573             CASE (caron, 0x030C);
574             CASE (abovecomma, 0x0313);         /* Equivalent to psili */
575             CASE (abovereversedcomma, 0x0314); /* Equivalent to dasia */
576             CASE (horn, 0x031B);        /* Legacy use for psili, 0x313 (or 0x343). */
577             CASE (belowdot, 0x0323);
578             CASE (cedilla, 0x0327);
579             CASE (ogonek, 0x0328);      /* Legacy use for dasia, 0x314.*/
580             CASE (iota, 0x0345);
581             CASE (voiced_sound, 0x3099);        /* Per Markus Kuhn keysyms.txt file. */
582             CASE (semivoiced_sound, 0x309A);    /* Per Markus Kuhn keysyms.txt file. */
583
584             /* The following cases are to be removed once xkeyboard-config,
585              * xorg are fully updated.
586              */
587             /* Workaround for typo in 1.4.x xserver-xorg */
588             case 0xfe66: combination_buffer[i+1] = 0x314; break;
589             /* CASE (dasia, 0x314); */
590             /* CASE (perispomeni, 0x342); */
591             /* CASE (psili, 0x343); */
592 #undef CASE
593             default:
594               combination_buffer[i+1] = gdk_keyval_to_unicode (priv->compose_buffer[i]);
595             }
596           i--;
597         }
598       
599       /* If the buffer normalizes to a single character, then modify the order
600        * of combination_buffer accordingly, if necessary, and return TRUE.
601        */
602       if (check_normalize_nfc (combination_buffer, n_compose))
603         {
604           gunichar value;
605           combination_utf8 = g_ucs4_to_utf8 (combination_buffer, -1, NULL, NULL, NULL);
606           nfc = g_utf8_normalize (combination_utf8, -1, G_NORMALIZE_NFC);
607
608           value = g_utf8_get_char (nfc);
609           gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value);
610           priv->compose_buffer[0] = 0;
611
612           g_free (combination_utf8);
613           g_free (nfc);
614
615           return TRUE;
616         }
617     }
618
619   return FALSE;
620 }
621
622 /* In addition to the table-driven sequences, we allow Unicode hex
623  * codes to be entered. The method chosen here is similar to the
624  * one recommended in ISO 14755, but not exactly the same, since we
625  * don't want to steal 16 valuable key combinations.
626  *
627  * A hex Unicode sequence must be started with Ctrl-Shift-U, followed
628  * by a sequence of hex digits entered with Ctrl-Shift still held.
629  * Releasing one of the modifiers or pressing space while the modifiers
630  * are still held commits the character. It is possible to erase
631  * digits using backspace.
632  *
633  * As an extension to the above, we also allow to start the sequence
634  * with Ctrl-Shift-U, then release the modifiers before typing any
635  * digits, and enter the digits without modifiers.
636  */
637
638 static gboolean
639 check_hex (GtkIMContextSimple *context_simple,
640            gint                n_compose)
641 {
642   GtkIMContextSimplePrivate *priv = context_simple->priv;
643   /* See if this is a hex sequence, return TRUE if so */
644   gint i;
645   GString *str;
646   gulong n;
647   gchar *nptr = NULL;
648   gchar buf[7];
649
650   priv->tentative_match = 0;
651   priv->tentative_match_len = 0;
652
653   str = g_string_new (NULL);
654   
655   i = 0;
656   while (i < n_compose)
657     {
658       gunichar ch;
659       
660       ch = gdk_keyval_to_unicode (priv->compose_buffer[i]);
661       
662       if (ch == 0)
663         return FALSE;
664
665       if (!g_unichar_isxdigit (ch))
666         return FALSE;
667
668       buf[g_unichar_to_utf8 (ch, buf)] = '\0';
669
670       g_string_append (str, buf);
671       
672       ++i;
673     }
674
675   n = strtoul (str->str, &nptr, 16);
676
677   /* If strtoul fails it probably means non-latin digits were used;
678    * we should in principle handle that, but we probably don't.
679    */
680   if (nptr - str->str < str->len)
681     {
682       g_string_free (str, TRUE);
683       return FALSE;
684     }
685   else
686     g_string_free (str, TRUE);
687
688   if (g_unichar_validate (n))
689     {
690       priv->tentative_match = n;
691       priv->tentative_match_len = n_compose;
692     }
693   
694   return TRUE;
695 }
696
697 static void
698 beep_window (GdkWindow *window)
699 {
700   GdkScreen *screen = gdk_window_get_screen (window);
701   gboolean   beep;
702
703   g_object_get (gtk_settings_get_for_screen (screen),
704                 "gtk-error-bell", &beep,
705                 NULL);
706
707   if (beep)
708     gdk_window_beep (window);
709 }
710
711 static gboolean
712 no_sequence_matches (GtkIMContextSimple *context_simple,
713                      gint                n_compose,
714                      GdkEventKey        *event)
715 {
716   GtkIMContextSimplePrivate *priv = context_simple->priv;
717   GtkIMContext *context;
718   gunichar ch;
719   
720   context = GTK_IM_CONTEXT (context_simple);
721   
722   /* No compose sequences found, check first if we have a partial
723    * match pending.
724    */
725   if (priv->tentative_match)
726     {
727       gint len = priv->tentative_match_len;
728       int i;
729       
730       gtk_im_context_simple_commit_char (context, priv->tentative_match);
731       priv->compose_buffer[0] = 0;
732       
733       for (i=0; i < n_compose - len - 1; i++)
734         {
735           GdkEvent *tmp_event = gdk_event_copy ((GdkEvent *)event);
736           tmp_event->key.keyval = priv->compose_buffer[len + i];
737           
738           gtk_im_context_filter_keypress (context, (GdkEventKey *)tmp_event);
739           gdk_event_free (tmp_event);
740         }
741
742       return gtk_im_context_filter_keypress (context, event);
743     }
744   else
745     {
746       priv->compose_buffer[0] = 0;
747       if (n_compose > 1)                /* Invalid sequence */
748         {
749           beep_window (event->window);
750           return TRUE;
751         }
752   
753       ch = gdk_keyval_to_unicode (event->keyval);
754       if (ch != 0 && !g_unichar_iscntrl (ch))
755         {
756           gtk_im_context_simple_commit_char (context, ch);
757           return TRUE;
758         }
759       else
760         return FALSE;
761     }
762 }
763
764 static gboolean
765 is_hex_keyval (guint keyval)
766 {
767   gunichar ch = gdk_keyval_to_unicode (keyval);
768
769   return g_unichar_isxdigit (ch);
770 }
771
772 static guint
773 canonical_hex_keyval (GdkEventKey *event)
774 {
775   GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_window_get_display (event->window));
776   guint keyval;
777   guint *keyvals = NULL;
778   gint n_vals = 0;
779   gint i;
780   
781   /* See if the keyval is already a hex digit */
782   if (is_hex_keyval (event->keyval))
783     return event->keyval;
784
785   /* See if this key would have generated a hex keyval in
786    * any other state, and return that hex keyval if so
787    */
788   gdk_keymap_get_entries_for_keycode (keymap,
789                                       event->hardware_keycode,
790                                       NULL,
791                                       &keyvals, &n_vals);
792
793   keyval = 0;
794   i = 0;
795   while (i < n_vals)
796     {
797       if (is_hex_keyval (keyvals[i]))
798         {
799           keyval = keyvals[i];
800           break;
801         }
802
803       ++i;
804     }
805
806   g_free (keyvals);
807   
808   if (keyval)
809     return keyval;
810   else
811     /* No way to make it a hex digit
812      */
813     return 0;
814 }
815
816 static gboolean
817 gtk_im_context_simple_filter_keypress (GtkIMContext *context,
818                                        GdkEventKey  *event)
819 {
820   GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
821   GtkIMContextSimplePrivate *priv = context_simple->priv;
822   GdkDisplay *display = gdk_window_get_display (event->window);
823   GSList *tmp_list;  
824   int n_compose = 0;
825   GdkModifierType hex_mod_mask;
826   gboolean have_hex_mods;
827   gboolean is_hex_start;
828   gboolean is_hex_end;
829   gboolean is_backspace;
830   gboolean is_escape;
831   guint hex_keyval;
832   int i;
833
834   while (priv->compose_buffer[n_compose] != 0)
835     n_compose++;
836
837   if (event->type == GDK_KEY_RELEASE)
838     {
839       if (priv->in_hex_sequence &&
840           (event->keyval == GDK_KEY_Control_L || event->keyval == GDK_KEY_Control_R ||
841            event->keyval == GDK_KEY_Shift_L || event->keyval == GDK_KEY_Shift_R))
842         {
843           if (priv->tentative_match &&
844               g_unichar_validate (priv->tentative_match))
845             {
846 g_print ("committing tentative match on release\n");
847               gtk_im_context_simple_commit_char (context, priv->tentative_match);
848               priv->compose_buffer[0] = 0;
849
850             }
851           else if (n_compose == 0)
852             {
853               priv->modifiers_dropped = TRUE;
854             }
855           else
856             {
857               /* invalid hex sequence */
858               beep_window (event->window);
859               
860               priv->tentative_match = 0;
861               priv->in_hex_sequence = FALSE;
862               priv->compose_buffer[0] = 0;
863               
864               g_signal_emit_by_name (context_simple, "preedit-changed");
865               g_signal_emit_by_name (context_simple, "preedit-end");
866             }
867
868           return TRUE;
869         }
870       else
871         return FALSE;
872     }
873
874   /* Ignore modifier key presses */
875   for (i = 0; i < G_N_ELEMENTS (gtk_compose_ignore); i++)
876     if (event->keyval == gtk_compose_ignore[i])
877       return FALSE;
878
879   hex_mod_mask = gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
880                                                GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR);
881   hex_mod_mask |= GDK_SHIFT_MASK;
882
883   if (priv->in_hex_sequence && priv->modifiers_dropped)
884     have_hex_mods = TRUE;
885   else
886     have_hex_mods = (event->state & (hex_mod_mask)) == hex_mod_mask;
887   is_hex_start = event->keyval == GDK_KEY_U;
888   is_hex_end = (event->keyval == GDK_KEY_space ||
889                 event->keyval == GDK_KEY_KP_Space ||
890                 event->keyval == GDK_KEY_Return ||
891                 event->keyval == GDK_KEY_ISO_Enter ||
892                 event->keyval == GDK_KEY_KP_Enter);
893   is_backspace = event->keyval == GDK_KEY_BackSpace;
894   is_escape = event->keyval == GDK_KEY_Escape;
895   hex_keyval = canonical_hex_keyval (event);
896
897   /* If we are already in a non-hex sequence, or
898    * this keystroke is not hex modifiers + hex digit, don't filter
899    * key events with accelerator modifiers held down. We only treat
900    * Control and Alt as accel modifiers here, since Super, Hyper and
901    * Meta are often co-located with Mode_Switch, Multi_Key or
902    * ISO_Level3_Switch.
903    */
904   if (!have_hex_mods ||
905       (n_compose > 0 && !priv->in_hex_sequence) ||
906       (n_compose == 0 && !priv->in_hex_sequence && !is_hex_start) ||
907       (priv->in_hex_sequence && !hex_keyval &&
908        !is_hex_start && !is_hex_end && !is_escape && !is_backspace))
909     {
910       GdkDisplay *display;
911       GdkModifierType no_text_input_mask;
912
913       display = gdk_window_get_display (event->window);
914
915       no_text_input_mask =
916         gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
917                                       GDK_MODIFIER_INTENT_NO_TEXT_INPUT);
918
919       if (event->state & no_text_input_mask ||
920           (priv->in_hex_sequence && priv->modifiers_dropped &&
921            (event->keyval == GDK_KEY_Return ||
922             event->keyval == GDK_KEY_ISO_Enter ||
923             event->keyval == GDK_KEY_KP_Enter)))
924         {
925           return FALSE;
926         }
927     }
928   
929   /* Handle backspace */
930   if (priv->in_hex_sequence && have_hex_mods && is_backspace)
931     {
932       if (n_compose > 0)
933         {
934           n_compose--;
935           priv->compose_buffer[n_compose] = 0;
936           check_hex (context_simple, n_compose);
937         }
938       else
939         {
940           priv->in_hex_sequence = FALSE;
941         }
942
943       g_signal_emit_by_name (context_simple, "preedit-changed");
944
945       if (!priv->in_hex_sequence)
946         g_signal_emit_by_name (context_simple, "preedit-end");
947       
948       return TRUE;
949     }
950
951   /* Check for hex sequence restart */
952   if (priv->in_hex_sequence && have_hex_mods && is_hex_start)
953     {
954       if (priv->tentative_match &&
955           g_unichar_validate (priv->tentative_match))
956         {
957           gtk_im_context_simple_commit_char (context, priv->tentative_match);
958           priv->compose_buffer[0] = 0;
959         }
960       else 
961         {
962           /* invalid hex sequence */
963           if (n_compose > 0)
964             beep_window (event->window);
965           
966           priv->tentative_match = 0;
967           priv->in_hex_sequence = FALSE;
968           priv->compose_buffer[0] = 0;
969         }
970     }
971   
972   /* Check for hex sequence start */
973   if (!priv->in_hex_sequence && have_hex_mods && is_hex_start)
974     {
975       priv->compose_buffer[0] = 0;
976       priv->in_hex_sequence = TRUE;
977       priv->modifiers_dropped = FALSE;
978       priv->tentative_match = 0;
979
980       g_signal_emit_by_name (context_simple, "preedit-start");
981       g_signal_emit_by_name (context_simple, "preedit-changed");
982   
983       return TRUE;
984     }
985   
986   /* Then, check for compose sequences */
987   if (priv->in_hex_sequence)
988     {
989       if (hex_keyval)
990         priv->compose_buffer[n_compose++] = hex_keyval;
991       else if (is_escape)
992         {
993           gtk_im_context_simple_reset (context);
994           
995           return TRUE;
996         }
997       else if (!is_hex_end)
998         {
999           /* non-hex character in hex sequence */
1000           beep_window (event->window);
1001           
1002           return TRUE;
1003         }
1004     }
1005   else
1006     priv->compose_buffer[n_compose++] = event->keyval;
1007
1008   priv->compose_buffer[n_compose] = 0;
1009
1010   if (priv->in_hex_sequence)
1011     {
1012       /* If the modifiers are still held down, consider the sequence again */
1013       if (have_hex_mods)
1014         {
1015           /* space or return ends the sequence, and we eat the key */
1016           if (n_compose > 0 && is_hex_end)
1017             {
1018               if (priv->tentative_match &&
1019                   g_unichar_validate (priv->tentative_match))
1020                 {
1021                   gtk_im_context_simple_commit_char (context, priv->tentative_match);
1022                   priv->compose_buffer[0] = 0;
1023                 }
1024               else
1025                 {
1026                   /* invalid hex sequence */
1027                   beep_window (event->window);
1028
1029                   priv->tentative_match = 0;
1030                   priv->in_hex_sequence = FALSE;
1031                   priv->compose_buffer[0] = 0;
1032                 }
1033             }
1034           else if (!check_hex (context_simple, n_compose))
1035             beep_window (event->window);
1036           
1037           g_signal_emit_by_name (context_simple, "preedit-changed");
1038
1039           if (!priv->in_hex_sequence)
1040             g_signal_emit_by_name (context_simple, "preedit-end");
1041
1042           return TRUE;
1043         }
1044     }
1045   else
1046     {
1047       tmp_list = priv->tables;
1048       while (tmp_list)
1049         {
1050           if (check_table (context_simple, tmp_list->data, n_compose))
1051             return TRUE;
1052           tmp_list = tmp_list->next;
1053         }
1054
1055       GTK_NOTE (MISC, {
1056           g_print ("[ ");
1057           for (i = 0; i < n_compose; i++)
1058             {
1059               const gchar *keyval_name = gdk_keyval_name (priv->compose_buffer[i]);
1060               
1061               if (keyval_name != NULL)
1062                 g_print ("%s ", keyval_name);
1063               else
1064                 g_print ("%04x ", priv->compose_buffer[i]);
1065             }
1066           g_print ("] ");
1067         });
1068
1069 #ifdef GDK_WINDOWING_WIN32
1070       if (check_win32_special_cases (context_simple, n_compose))
1071         return TRUE;
1072 #endif
1073
1074       if (check_compact_table (context_simple, &gtk_compose_table_compact, n_compose))
1075         return TRUE;
1076   
1077       if (check_algorithmically (context_simple, n_compose))
1078         return TRUE;
1079     }
1080   
1081   /* The current compose_buffer doesn't match anything */
1082   return no_sequence_matches (context_simple, n_compose, event);
1083 }
1084
1085 static void
1086 gtk_im_context_simple_reset (GtkIMContext *context)
1087 {
1088   GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
1089   GtkIMContextSimplePrivate *priv = context_simple->priv;
1090
1091   priv->compose_buffer[0] = 0;
1092
1093   if (priv->tentative_match || priv->in_hex_sequence)
1094     {
1095       priv->in_hex_sequence = FALSE;
1096       priv->tentative_match = 0;
1097       priv->tentative_match_len = 0;
1098       g_signal_emit_by_name (context_simple, "preedit-changed");
1099       g_signal_emit_by_name (context_simple, "preedit-end");
1100     }
1101 }
1102
1103 static void     
1104 gtk_im_context_simple_get_preedit_string (GtkIMContext   *context,
1105                                           gchar         **str,
1106                                           PangoAttrList **attrs,
1107                                           gint           *cursor_pos)
1108 {
1109   GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
1110   GtkIMContextSimplePrivate *priv = context_simple->priv;
1111   char outbuf[37]; /* up to 6 hex digits */
1112   int len = 0;
1113
1114   if (priv->in_hex_sequence)
1115     {
1116       int hexchars = 0;
1117          
1118       outbuf[0] = 'u';
1119       len = 1;
1120
1121       while (priv->compose_buffer[hexchars] != 0)
1122         {
1123           len += g_unichar_to_utf8 (gdk_keyval_to_unicode (priv->compose_buffer[hexchars]),
1124                                     outbuf + len);
1125           ++hexchars;
1126         }
1127
1128       g_assert (len < 25);
1129     }
1130   else if (priv->tentative_match)
1131     len = g_unichar_to_utf8 (priv->tentative_match, outbuf);
1132       
1133   outbuf[len] = '\0';      
1134
1135   if (str)
1136     *str = g_strdup (outbuf);
1137
1138   if (attrs)
1139     {
1140       *attrs = pango_attr_list_new ();
1141       
1142       if (len)
1143         {
1144           PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
1145           attr->start_index = 0;
1146           attr->end_index = len;
1147           pango_attr_list_insert (*attrs, attr);
1148         }
1149     }
1150
1151   if (cursor_pos)
1152     *cursor_pos = len;
1153 }
1154
1155 /**
1156  * gtk_im_context_simple_add_table: (skip)
1157  * @context_simple: A #GtkIMContextSimple
1158  * @data: the table 
1159  * @max_seq_len: Maximum length of a sequence in the table
1160  *               (cannot be greater than #GTK_MAX_COMPOSE_LEN)
1161  * @n_seqs: number of sequences in the table
1162  * 
1163  * Adds an additional table to search to the input context.
1164  * Each row of the table consists of @max_seq_len key symbols
1165  * followed by two #guint16 interpreted as the high and low
1166  * words of a #gunicode value. Tables are searched starting
1167  * from the last added.
1168  *
1169  * The table must be sorted in dictionary order on the
1170  * numeric value of the key symbol fields. (Values beyond
1171  * the length of the sequence should be zero.)
1172  **/
1173 void
1174 gtk_im_context_simple_add_table (GtkIMContextSimple *context_simple,
1175                                  guint16            *data,
1176                                  gint                max_seq_len,
1177                                  gint                n_seqs)
1178 {
1179   GtkIMContextSimplePrivate *priv = context_simple->priv;
1180   GtkComposeTable *table;
1181
1182   g_return_if_fail (GTK_IS_IM_CONTEXT_SIMPLE (context_simple));
1183   g_return_if_fail (data != NULL);
1184   g_return_if_fail (max_seq_len <= GTK_MAX_COMPOSE_LEN);
1185   
1186   table = g_new (GtkComposeTable, 1);
1187   table->data = data;
1188   table->max_seq_len = max_seq_len;
1189   table->n_seqs = n_seqs;
1190
1191   priv->tables = g_slist_prepend (priv->tables, table);
1192 }