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