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