]> Pileus Git - ~andy/gtk/blob - gtk/gtkimcontextsimple.c
stylecontext: Do invalidation on first resize container
[~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 #ifdef GDK_WINDOWING_QUARTZ
368
369 static gboolean
370 check_quartz_special_cases (GtkIMContextSimple *context_simple,
371                             gint                n_compose)
372 {
373   GtkIMContextSimplePrivate *priv = context_simple->priv;
374   guint value = 0;
375
376   if (n_compose == 2)
377     {
378       switch (priv->compose_buffer[0])
379         {
380         case GDK_KEY_dead_doubleacute:
381           switch (priv->compose_buffer[1])
382             {
383             case GDK_KEY_dead_doubleacute:
384             case GDK_KEY_space:
385               value = GDK_KEY_quotedbl; break;
386
387             case 'a': value = GDK_KEY_adiaeresis; break;
388             case 'A': value = GDK_KEY_Adiaeresis; break;
389             case 'e': value = GDK_KEY_ediaeresis; break;
390             case 'E': value = GDK_KEY_Ediaeresis; break;
391             case 'i': value = GDK_KEY_idiaeresis; break;
392             case 'I': value = GDK_KEY_Idiaeresis; break;
393             case 'o': value = GDK_KEY_odiaeresis; break;
394             case 'O': value = GDK_KEY_Odiaeresis; break;
395             case 'u': value = GDK_KEY_udiaeresis; break;
396             case 'U': value = GDK_KEY_Udiaeresis; break;
397             case 'y': value = GDK_KEY_ydiaeresis; break;
398             case 'Y': value = GDK_KEY_Ydiaeresis; break;
399             }
400           break;
401
402         case GDK_KEY_dead_acute:
403           switch (priv->compose_buffer[1])
404             {
405             case 'c': value = GDK_KEY_ccedilla; break;
406             case 'C': value = GDK_KEY_Ccedilla; break;
407             }
408           break;
409         }
410     }
411
412   if (value > 0)
413     {
414       gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple),
415                                          gdk_keyval_to_unicode (value));
416       priv->compose_buffer[0] = 0;
417
418       GTK_NOTE (MISC, g_print ("quartz: U+%04X\n", value));
419       return TRUE;
420     }
421
422   return FALSE;
423 }
424
425 #endif
426
427 static gboolean
428 check_compact_table (GtkIMContextSimple           *context_simple,
429                      const GtkComposeTableCompact *table,
430                      gint                          n_compose)
431 {
432   GtkIMContextSimplePrivate *priv = context_simple->priv;
433   gint row_stride;
434   guint16 *seq_index;
435   guint16 *seq;
436   gint i;
437   gboolean match;
438   gunichar value;
439
440   /* Will never match, if the sequence in the compose buffer is longer
441    * than the sequences in the table.  Further, compare_seq (key, val)
442    * will overrun val if key is longer than val.
443    */
444   if (n_compose > table->max_seq_len)
445     return FALSE;
446
447   seq_index = bsearch (priv->compose_buffer,
448                        table->data,
449                        table->n_index_size,
450                        sizeof (guint16) * table->n_index_stride,
451                        compare_seq_index);
452
453   if (!seq_index)
454     {
455       GTK_NOTE (MISC, g_print ("compact: no\n"));
456       return FALSE;
457     }
458
459   if (seq_index && n_compose == 1)
460     {
461       GTK_NOTE (MISC, g_print ("compact: yes\n"));
462       return TRUE;
463     }
464
465   GTK_NOTE (MISC, g_print ("compact: %d ", *seq_index));
466   seq = NULL;
467   match = FALSE;
468
469   for (i = n_compose - 1; i < table->max_seq_len; i++)
470     {
471       row_stride = i + 1;
472
473       if (seq_index[i + 1] - seq_index[i] > 0)
474         {
475           seq = bsearch (priv->compose_buffer + 1,
476                          table->data + seq_index[i],
477                          (seq_index[i + 1] - seq_index[i]) / row_stride,
478                          sizeof (guint16) *  row_stride,
479                          compare_seq);
480
481           if (seq)
482             {
483               if (i == n_compose - 1)
484                 {
485                   value = seq[row_stride - 1];
486                   match = TRUE;
487                 }
488               else
489                 {
490                   if (match)
491                     {
492                       GTK_NOTE (MISC, g_print ("tentative match U+%04X ", value));
493                       priv->tentative_match = value;
494                       priv->tentative_match_len = n_compose;
495                     }
496
497                   g_signal_emit_by_name (context_simple, "preedit-changed");
498
499                   GTK_NOTE (MISC, g_print ("yes\n"));
500                   return TRUE;
501                 }
502              }
503         }
504     }
505
506   if (match)
507     {
508       gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value);
509 #ifdef G_OS_WIN32
510       check_win32_special_case_after_compact_match (context_simple, n_compose, value);
511 #endif
512       priv->compose_buffer[0] = 0;
513
514       GTK_NOTE (MISC, g_print ("U+%04X\n", value));
515       return TRUE;
516     }
517
518   GTK_NOTE (MISC, g_print ("no\n"));
519   return FALSE;
520 }
521
522 /* This function receives a sequence of Unicode characters and tries to
523  * normalize it (NFC). We check for the case where the resulting string
524  * has length 1 (single character).
525  * NFC normalisation normally rearranges diacritic marks, unless these
526  * belong to the same Canonical Combining Class.
527  * If they belong to the same canonical combining class, we produce all
528  * permutations of the diacritic marks, then attempt to normalize.
529  */
530 static gboolean
531 check_normalize_nfc (gunichar* combination_buffer, gint n_compose)
532 {
533   gunichar combination_buffer_temp[GTK_MAX_COMPOSE_LEN];
534   gchar *combination_utf8_temp = NULL;
535   gchar *nfc_temp = NULL;
536   gint n_combinations;
537   gunichar temp_swap;
538   gint i;
539
540   n_combinations = 1;
541
542   for (i = 1; i < n_compose; i++ )
543      n_combinations *= i;
544
545   /* Xorg reuses dead_tilde for the perispomeni diacritic mark.
546    * We check if base character belongs to Greek Unicode block,
547    * and if so, we replace tilde with perispomeni.
548    */
549   if (combination_buffer[0] >= 0x390 && combination_buffer[0] <= 0x3FF)
550     {
551       for (i = 1; i < n_compose; i++ )
552         if (combination_buffer[i] == 0x303)
553           combination_buffer[i] = 0x342;
554     }
555
556   memcpy (combination_buffer_temp, combination_buffer, GTK_MAX_COMPOSE_LEN * sizeof (gunichar) );
557
558   for (i = 0; i < n_combinations; i++ )
559     {
560       g_unicode_canonical_ordering (combination_buffer_temp, n_compose);
561       combination_utf8_temp = g_ucs4_to_utf8 (combination_buffer_temp, -1, NULL, NULL, NULL);
562       nfc_temp = g_utf8_normalize (combination_utf8_temp, -1, G_NORMALIZE_NFC);
563
564       if (g_utf8_strlen (nfc_temp, -1) == 1)
565         {
566           memcpy (combination_buffer, combination_buffer_temp, GTK_MAX_COMPOSE_LEN * sizeof (gunichar) );
567
568           g_free (combination_utf8_temp);
569           g_free (nfc_temp);
570
571           return TRUE;
572         }
573
574       g_free (combination_utf8_temp);
575       g_free (nfc_temp);
576
577       if (n_compose > 2)
578         {
579           temp_swap = combination_buffer_temp[i % (n_compose - 1) + 1];
580           combination_buffer_temp[i % (n_compose - 1) + 1] = combination_buffer_temp[(i+1) % (n_compose - 1) + 1];
581           combination_buffer_temp[(i+1) % (n_compose - 1) + 1] = temp_swap;
582         }
583       else
584         break;
585     }
586
587   return FALSE;
588 }
589
590 static gboolean
591 check_algorithmically (GtkIMContextSimple *context_simple,
592                        gint                n_compose)
593
594 {
595   GtkIMContextSimplePrivate *priv = context_simple->priv;
596   gint i;
597   gunichar combination_buffer[GTK_MAX_COMPOSE_LEN];
598   gchar *combination_utf8, *nfc;
599
600   if (n_compose >= GTK_MAX_COMPOSE_LEN)
601     return FALSE;
602
603   for (i = 0; i < n_compose && IS_DEAD_KEY (priv->compose_buffer[i]); i++)
604     ;
605   if (i == n_compose)
606     return TRUE;
607
608   if (i > 0 && i == n_compose - 1)
609     {
610       combination_buffer[0] = gdk_keyval_to_unicode (priv->compose_buffer[i]);
611       combination_buffer[n_compose] = 0;
612       i--;
613       while (i >= 0)
614         {
615           switch (priv->compose_buffer[i])
616             {
617 #define CASE(keysym, unicode) \
618             case GDK_KEY_dead_##keysym: combination_buffer[i+1] = unicode; break
619
620             CASE (grave, 0x0300);
621             CASE (acute, 0x0301);
622             CASE (circumflex, 0x0302);
623             CASE (tilde, 0x0303);       /* Also used with perispomeni, 0x342. */
624             CASE (macron, 0x0304);
625             CASE (breve, 0x0306);
626             CASE (abovedot, 0x0307);
627             CASE (diaeresis, 0x0308);
628             CASE (hook, 0x0309);
629             CASE (abovering, 0x030A);
630             CASE (doubleacute, 0x030B);
631             CASE (caron, 0x030C);
632             CASE (abovecomma, 0x0313);         /* Equivalent to psili */
633             CASE (abovereversedcomma, 0x0314); /* Equivalent to dasia */
634             CASE (horn, 0x031B);        /* Legacy use for psili, 0x313 (or 0x343). */
635             CASE (belowdot, 0x0323);
636             CASE (cedilla, 0x0327);
637             CASE (ogonek, 0x0328);      /* Legacy use for dasia, 0x314.*/
638             CASE (iota, 0x0345);
639             CASE (voiced_sound, 0x3099);        /* Per Markus Kuhn keysyms.txt file. */
640             CASE (semivoiced_sound, 0x309A);    /* Per Markus Kuhn keysyms.txt file. */
641
642             /* The following cases are to be removed once xkeyboard-config,
643              * xorg are fully updated.
644              */
645             /* Workaround for typo in 1.4.x xserver-xorg */
646             case 0xfe66: combination_buffer[i+1] = 0x314; break;
647             /* CASE (dasia, 0x314); */
648             /* CASE (perispomeni, 0x342); */
649             /* CASE (psili, 0x343); */
650 #undef CASE
651             default:
652               combination_buffer[i+1] = gdk_keyval_to_unicode (priv->compose_buffer[i]);
653             }
654           i--;
655         }
656       
657       /* If the buffer normalizes to a single character, then modify the order
658        * of combination_buffer accordingly, if necessary, and return TRUE.
659        */
660       if (check_normalize_nfc (combination_buffer, n_compose))
661         {
662           gunichar value;
663           combination_utf8 = g_ucs4_to_utf8 (combination_buffer, -1, NULL, NULL, NULL);
664           nfc = g_utf8_normalize (combination_utf8, -1, G_NORMALIZE_NFC);
665
666           value = g_utf8_get_char (nfc);
667           gtk_im_context_simple_commit_char (GTK_IM_CONTEXT (context_simple), value);
668           priv->compose_buffer[0] = 0;
669
670           g_free (combination_utf8);
671           g_free (nfc);
672
673           return TRUE;
674         }
675     }
676
677   return FALSE;
678 }
679
680 /* In addition to the table-driven sequences, we allow Unicode hex
681  * codes to be entered. The method chosen here is similar to the
682  * one recommended in ISO 14755, but not exactly the same, since we
683  * don't want to steal 16 valuable key combinations.
684  *
685  * A hex Unicode sequence must be started with Ctrl-Shift-U, followed
686  * by a sequence of hex digits entered with Ctrl-Shift still held.
687  * Releasing one of the modifiers or pressing space while the modifiers
688  * are still held commits the character. It is possible to erase
689  * digits using backspace.
690  *
691  * As an extension to the above, we also allow to start the sequence
692  * with Ctrl-Shift-U, then release the modifiers before typing any
693  * digits, and enter the digits without modifiers.
694  */
695
696 static gboolean
697 check_hex (GtkIMContextSimple *context_simple,
698            gint                n_compose)
699 {
700   GtkIMContextSimplePrivate *priv = context_simple->priv;
701   /* See if this is a hex sequence, return TRUE if so */
702   gint i;
703   GString *str;
704   gulong n;
705   gchar *nptr = NULL;
706   gchar buf[7];
707
708   priv->tentative_match = 0;
709   priv->tentative_match_len = 0;
710
711   str = g_string_new (NULL);
712   
713   i = 0;
714   while (i < n_compose)
715     {
716       gunichar ch;
717       
718       ch = gdk_keyval_to_unicode (priv->compose_buffer[i]);
719       
720       if (ch == 0)
721         return FALSE;
722
723       if (!g_unichar_isxdigit (ch))
724         return FALSE;
725
726       buf[g_unichar_to_utf8 (ch, buf)] = '\0';
727
728       g_string_append (str, buf);
729       
730       ++i;
731     }
732
733   n = strtoul (str->str, &nptr, 16);
734
735   /* If strtoul fails it probably means non-latin digits were used;
736    * we should in principle handle that, but we probably don't.
737    */
738   if (nptr - str->str < str->len)
739     {
740       g_string_free (str, TRUE);
741       return FALSE;
742     }
743   else
744     g_string_free (str, TRUE);
745
746   if (g_unichar_validate (n))
747     {
748       priv->tentative_match = n;
749       priv->tentative_match_len = n_compose;
750     }
751   
752   return TRUE;
753 }
754
755 static void
756 beep_window (GdkWindow *window)
757 {
758   GdkScreen *screen = gdk_window_get_screen (window);
759   gboolean   beep;
760
761   g_object_get (gtk_settings_get_for_screen (screen),
762                 "gtk-error-bell", &beep,
763                 NULL);
764
765   if (beep)
766     gdk_window_beep (window);
767 }
768
769 static gboolean
770 no_sequence_matches (GtkIMContextSimple *context_simple,
771                      gint                n_compose,
772                      GdkEventKey        *event)
773 {
774   GtkIMContextSimplePrivate *priv = context_simple->priv;
775   GtkIMContext *context;
776   gunichar ch;
777   
778   context = GTK_IM_CONTEXT (context_simple);
779   
780   /* No compose sequences found, check first if we have a partial
781    * match pending.
782    */
783   if (priv->tentative_match)
784     {
785       gint len = priv->tentative_match_len;
786       int i;
787       
788       gtk_im_context_simple_commit_char (context, priv->tentative_match);
789       priv->compose_buffer[0] = 0;
790       
791       for (i=0; i < n_compose - len - 1; i++)
792         {
793           GdkEvent *tmp_event = gdk_event_copy ((GdkEvent *)event);
794           tmp_event->key.keyval = priv->compose_buffer[len + i];
795           
796           gtk_im_context_filter_keypress (context, (GdkEventKey *)tmp_event);
797           gdk_event_free (tmp_event);
798         }
799
800       return gtk_im_context_filter_keypress (context, event);
801     }
802   else
803     {
804       priv->compose_buffer[0] = 0;
805       if (n_compose > 1)                /* Invalid sequence */
806         {
807           beep_window (event->window);
808           return TRUE;
809         }
810   
811       ch = gdk_keyval_to_unicode (event->keyval);
812       if (ch != 0 && !g_unichar_iscntrl (ch))
813         {
814           gtk_im_context_simple_commit_char (context, ch);
815           return TRUE;
816         }
817       else
818         return FALSE;
819     }
820 }
821
822 static gboolean
823 is_hex_keyval (guint keyval)
824 {
825   gunichar ch = gdk_keyval_to_unicode (keyval);
826
827   return g_unichar_isxdigit (ch);
828 }
829
830 static guint
831 canonical_hex_keyval (GdkEventKey *event)
832 {
833   GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_window_get_display (event->window));
834   guint keyval;
835   guint *keyvals = NULL;
836   gint n_vals = 0;
837   gint i;
838   
839   /* See if the keyval is already a hex digit */
840   if (is_hex_keyval (event->keyval))
841     return event->keyval;
842
843   /* See if this key would have generated a hex keyval in
844    * any other state, and return that hex keyval if so
845    */
846   gdk_keymap_get_entries_for_keycode (keymap,
847                                       event->hardware_keycode,
848                                       NULL,
849                                       &keyvals, &n_vals);
850
851   keyval = 0;
852   i = 0;
853   while (i < n_vals)
854     {
855       if (is_hex_keyval (keyvals[i]))
856         {
857           keyval = keyvals[i];
858           break;
859         }
860
861       ++i;
862     }
863
864   g_free (keyvals);
865   
866   if (keyval)
867     return keyval;
868   else
869     /* No way to make it a hex digit
870      */
871     return 0;
872 }
873
874 static gboolean
875 gtk_im_context_simple_filter_keypress (GtkIMContext *context,
876                                        GdkEventKey  *event)
877 {
878   GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
879   GtkIMContextSimplePrivate *priv = context_simple->priv;
880   GdkDisplay *display = gdk_window_get_display (event->window);
881   GSList *tmp_list;  
882   int n_compose = 0;
883   GdkModifierType hex_mod_mask;
884   gboolean have_hex_mods;
885   gboolean is_hex_start;
886   gboolean is_hex_end;
887   gboolean is_backspace;
888   gboolean is_escape;
889   guint hex_keyval;
890   int i;
891
892   while (priv->compose_buffer[n_compose] != 0)
893     n_compose++;
894
895   if (event->type == GDK_KEY_RELEASE)
896     {
897       if (priv->in_hex_sequence &&
898           (event->keyval == GDK_KEY_Control_L || event->keyval == GDK_KEY_Control_R ||
899            event->keyval == GDK_KEY_Shift_L || event->keyval == GDK_KEY_Shift_R))
900         {
901           if (priv->tentative_match &&
902               g_unichar_validate (priv->tentative_match))
903             {
904               gtk_im_context_simple_commit_char (context, priv->tentative_match);
905               priv->compose_buffer[0] = 0;
906
907             }
908           else if (n_compose == 0)
909             {
910               priv->modifiers_dropped = TRUE;
911             }
912           else
913             {
914               /* invalid hex sequence */
915               beep_window (event->window);
916               
917               priv->tentative_match = 0;
918               priv->in_hex_sequence = FALSE;
919               priv->compose_buffer[0] = 0;
920               
921               g_signal_emit_by_name (context_simple, "preedit-changed");
922               g_signal_emit_by_name (context_simple, "preedit-end");
923             }
924
925           return TRUE;
926         }
927       else
928         return FALSE;
929     }
930
931   /* Ignore modifier key presses */
932   for (i = 0; i < G_N_ELEMENTS (gtk_compose_ignore); i++)
933     if (event->keyval == gtk_compose_ignore[i])
934       return FALSE;
935
936   hex_mod_mask = gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
937                                                GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR);
938   hex_mod_mask |= GDK_SHIFT_MASK;
939
940   if (priv->in_hex_sequence && priv->modifiers_dropped)
941     have_hex_mods = TRUE;
942   else
943     have_hex_mods = (event->state & (hex_mod_mask)) == hex_mod_mask;
944   is_hex_start = event->keyval == GDK_KEY_U;
945   is_hex_end = (event->keyval == GDK_KEY_space ||
946                 event->keyval == GDK_KEY_KP_Space ||
947                 event->keyval == GDK_KEY_Return ||
948                 event->keyval == GDK_KEY_ISO_Enter ||
949                 event->keyval == GDK_KEY_KP_Enter);
950   is_backspace = event->keyval == GDK_KEY_BackSpace;
951   is_escape = event->keyval == GDK_KEY_Escape;
952   hex_keyval = canonical_hex_keyval (event);
953
954   /* If we are already in a non-hex sequence, or
955    * this keystroke is not hex modifiers + hex digit, don't filter
956    * key events with accelerator modifiers held down. We only treat
957    * Control and Alt as accel modifiers here, since Super, Hyper and
958    * Meta are often co-located with Mode_Switch, Multi_Key or
959    * ISO_Level3_Switch.
960    */
961   if (!have_hex_mods ||
962       (n_compose > 0 && !priv->in_hex_sequence) ||
963       (n_compose == 0 && !priv->in_hex_sequence && !is_hex_start) ||
964       (priv->in_hex_sequence && !hex_keyval &&
965        !is_hex_start && !is_hex_end && !is_escape && !is_backspace))
966     {
967       GdkDisplay *display;
968       GdkModifierType no_text_input_mask;
969
970       display = gdk_window_get_display (event->window);
971
972       no_text_input_mask =
973         gdk_keymap_get_modifier_mask (gdk_keymap_get_for_display (display),
974                                       GDK_MODIFIER_INTENT_NO_TEXT_INPUT);
975
976       if (event->state & no_text_input_mask ||
977           (priv->in_hex_sequence && priv->modifiers_dropped &&
978            (event->keyval == GDK_KEY_Return ||
979             event->keyval == GDK_KEY_ISO_Enter ||
980             event->keyval == GDK_KEY_KP_Enter)))
981         {
982           return FALSE;
983         }
984     }
985   
986   /* Handle backspace */
987   if (priv->in_hex_sequence && have_hex_mods && is_backspace)
988     {
989       if (n_compose > 0)
990         {
991           n_compose--;
992           priv->compose_buffer[n_compose] = 0;
993           check_hex (context_simple, n_compose);
994         }
995       else
996         {
997           priv->in_hex_sequence = FALSE;
998         }
999
1000       g_signal_emit_by_name (context_simple, "preedit-changed");
1001
1002       if (!priv->in_hex_sequence)
1003         g_signal_emit_by_name (context_simple, "preedit-end");
1004       
1005       return TRUE;
1006     }
1007
1008   /* Check for hex sequence restart */
1009   if (priv->in_hex_sequence && have_hex_mods && is_hex_start)
1010     {
1011       if (priv->tentative_match &&
1012           g_unichar_validate (priv->tentative_match))
1013         {
1014           gtk_im_context_simple_commit_char (context, priv->tentative_match);
1015           priv->compose_buffer[0] = 0;
1016         }
1017       else 
1018         {
1019           /* invalid hex sequence */
1020           if (n_compose > 0)
1021             beep_window (event->window);
1022           
1023           priv->tentative_match = 0;
1024           priv->in_hex_sequence = FALSE;
1025           priv->compose_buffer[0] = 0;
1026         }
1027     }
1028   
1029   /* Check for hex sequence start */
1030   if (!priv->in_hex_sequence && have_hex_mods && is_hex_start)
1031     {
1032       priv->compose_buffer[0] = 0;
1033       priv->in_hex_sequence = TRUE;
1034       priv->modifiers_dropped = FALSE;
1035       priv->tentative_match = 0;
1036
1037       g_signal_emit_by_name (context_simple, "preedit-start");
1038       g_signal_emit_by_name (context_simple, "preedit-changed");
1039   
1040       return TRUE;
1041     }
1042   
1043   /* Then, check for compose sequences */
1044   if (priv->in_hex_sequence)
1045     {
1046       if (hex_keyval)
1047         priv->compose_buffer[n_compose++] = hex_keyval;
1048       else if (is_escape)
1049         {
1050           gtk_im_context_simple_reset (context);
1051           
1052           return TRUE;
1053         }
1054       else if (!is_hex_end)
1055         {
1056           /* non-hex character in hex sequence */
1057           beep_window (event->window);
1058           
1059           return TRUE;
1060         }
1061     }
1062   else
1063     priv->compose_buffer[n_compose++] = event->keyval;
1064
1065   priv->compose_buffer[n_compose] = 0;
1066
1067   if (priv->in_hex_sequence)
1068     {
1069       /* If the modifiers are still held down, consider the sequence again */
1070       if (have_hex_mods)
1071         {
1072           /* space or return ends the sequence, and we eat the key */
1073           if (n_compose > 0 && is_hex_end)
1074             {
1075               if (priv->tentative_match &&
1076                   g_unichar_validate (priv->tentative_match))
1077                 {
1078                   gtk_im_context_simple_commit_char (context, priv->tentative_match);
1079                   priv->compose_buffer[0] = 0;
1080                 }
1081               else
1082                 {
1083                   /* invalid hex sequence */
1084                   beep_window (event->window);
1085
1086                   priv->tentative_match = 0;
1087                   priv->in_hex_sequence = FALSE;
1088                   priv->compose_buffer[0] = 0;
1089                 }
1090             }
1091           else if (!check_hex (context_simple, n_compose))
1092             beep_window (event->window);
1093           
1094           g_signal_emit_by_name (context_simple, "preedit-changed");
1095
1096           if (!priv->in_hex_sequence)
1097             g_signal_emit_by_name (context_simple, "preedit-end");
1098
1099           return TRUE;
1100         }
1101     }
1102   else
1103     {
1104       tmp_list = priv->tables;
1105       while (tmp_list)
1106         {
1107           if (check_table (context_simple, tmp_list->data, n_compose))
1108             return TRUE;
1109           tmp_list = tmp_list->next;
1110         }
1111
1112       GTK_NOTE (MISC, {
1113           g_print ("[ ");
1114           for (i = 0; i < n_compose; i++)
1115             {
1116               const gchar *keyval_name = gdk_keyval_name (priv->compose_buffer[i]);
1117               
1118               if (keyval_name != NULL)
1119                 g_print ("%s ", keyval_name);
1120               else
1121                 g_print ("%04x ", priv->compose_buffer[i]);
1122             }
1123           g_print ("] ");
1124         });
1125
1126 #ifdef GDK_WINDOWING_WIN32
1127       if (check_win32_special_cases (context_simple, n_compose))
1128         return TRUE;
1129 #endif
1130
1131 #ifdef GDK_WINDOWING_QUARTZ
1132       if (check_quartz_special_cases (context_simple, n_compose))
1133         return TRUE;
1134 #endif
1135
1136       if (check_compact_table (context_simple, &gtk_compose_table_compact, n_compose))
1137         return TRUE;
1138   
1139       if (check_algorithmically (context_simple, n_compose))
1140         return TRUE;
1141     }
1142   
1143   /* The current compose_buffer doesn't match anything */
1144   return no_sequence_matches (context_simple, n_compose, event);
1145 }
1146
1147 static void
1148 gtk_im_context_simple_reset (GtkIMContext *context)
1149 {
1150   GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
1151   GtkIMContextSimplePrivate *priv = context_simple->priv;
1152
1153   priv->compose_buffer[0] = 0;
1154
1155   if (priv->tentative_match || priv->in_hex_sequence)
1156     {
1157       priv->in_hex_sequence = FALSE;
1158       priv->tentative_match = 0;
1159       priv->tentative_match_len = 0;
1160       g_signal_emit_by_name (context_simple, "preedit-changed");
1161       g_signal_emit_by_name (context_simple, "preedit-end");
1162     }
1163 }
1164
1165 static void     
1166 gtk_im_context_simple_get_preedit_string (GtkIMContext   *context,
1167                                           gchar         **str,
1168                                           PangoAttrList **attrs,
1169                                           gint           *cursor_pos)
1170 {
1171   GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
1172   GtkIMContextSimplePrivate *priv = context_simple->priv;
1173   char outbuf[37]; /* up to 6 hex digits */
1174   int len = 0;
1175
1176   if (priv->in_hex_sequence)
1177     {
1178       int hexchars = 0;
1179          
1180       outbuf[0] = 'u';
1181       len = 1;
1182
1183       while (priv->compose_buffer[hexchars] != 0)
1184         {
1185           len += g_unichar_to_utf8 (gdk_keyval_to_unicode (priv->compose_buffer[hexchars]),
1186                                     outbuf + len);
1187           ++hexchars;
1188         }
1189
1190       g_assert (len < 25);
1191     }
1192   else if (priv->tentative_match)
1193     len = g_unichar_to_utf8 (priv->tentative_match, outbuf);
1194       
1195   outbuf[len] = '\0';      
1196
1197   if (str)
1198     *str = g_strdup (outbuf);
1199
1200   if (attrs)
1201     {
1202       *attrs = pango_attr_list_new ();
1203       
1204       if (len)
1205         {
1206           PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
1207           attr->start_index = 0;
1208           attr->end_index = len;
1209           pango_attr_list_insert (*attrs, attr);
1210         }
1211     }
1212
1213   if (cursor_pos)
1214     *cursor_pos = len;
1215 }
1216
1217 /**
1218  * gtk_im_context_simple_add_table: (skip)
1219  * @context_simple: A #GtkIMContextSimple
1220  * @data: the table 
1221  * @max_seq_len: Maximum length of a sequence in the table
1222  *               (cannot be greater than #GTK_MAX_COMPOSE_LEN)
1223  * @n_seqs: number of sequences in the table
1224  * 
1225  * Adds an additional table to search to the input context.
1226  * Each row of the table consists of @max_seq_len key symbols
1227  * followed by two #guint16 interpreted as the high and low
1228  * words of a #gunicode value. Tables are searched starting
1229  * from the last added.
1230  *
1231  * The table must be sorted in dictionary order on the
1232  * numeric value of the key symbol fields. (Values beyond
1233  * the length of the sequence should be zero.)
1234  **/
1235 void
1236 gtk_im_context_simple_add_table (GtkIMContextSimple *context_simple,
1237                                  guint16            *data,
1238                                  gint                max_seq_len,
1239                                  gint                n_seqs)
1240 {
1241   GtkIMContextSimplePrivate *priv = context_simple->priv;
1242   GtkComposeTable *table;
1243
1244   g_return_if_fail (GTK_IS_IM_CONTEXT_SIMPLE (context_simple));
1245   g_return_if_fail (data != NULL);
1246   g_return_if_fail (max_seq_len <= GTK_MAX_COMPOSE_LEN);
1247   
1248   table = g_new (GtkComposeTable, 1);
1249   table->data = data;
1250   table->max_seq_len = max_seq_len;
1251   table->n_seqs = n_seqs;
1252
1253   priv->tables = g_slist_prepend (priv->tables, table);
1254 }