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