2 * GTK+ MiM input module
9 #include <gtk/gtkimcontext.h>
10 #include <gtk/gtkimmodule.h>
11 #include <gdk/gdkkeysyms.h>
14 #include "im-extra-intl.h"
19 enum { MIM_MAX_COMPOSE_LEN = 3 };
23 guint keys[MIM_MAX_COMPOSE_LEN + 1]; /* 0-terminated */
27 gboolean is_terminator; /* true if a preceding possible final form
32 /* like GtkIMContextSimple */
33 static guint compose_buffer[MIM_MAX_COMPOSE_LEN + 1];
34 static int n_compose = 0;
35 static ComposeSequence *preceding_possible_final_form = NULL;
37 static ComposeSequence mim_compose_seqs[] =
39 { { GDK_a, 0, 0, 0, }, "אַ", NULL, NULL, FALSE, },
40 { { GDK_A, 0, 0, 0, }, "א", NULL, NULL, FALSE, },
41 { { GDK_b, 0, 0, 0, }, "ב", NULL, NULL, FALSE, },
42 { { GDK_B, 0, 0, 0, }, "בֿ", NULL, NULL, FALSE, },
43 { { GDK_c, 0, 0, 0, }, "צ", NULL, "ץ", FALSE, },
44 { { GDK_C, 0, 0, 0, }, "ץ", NULL, NULL, FALSE, },
45 { { GDK_g, 0, 0, 0, }, "ג", NULL, NULL, FALSE, },
46 { { GDK_d, 0, 0, 0, }, "ד", NULL, NULL, FALSE, },
47 { { GDK_h, 0, 0, 0, }, "ה", NULL, NULL, FALSE, },
48 { { GDK_i, 0, 0, 0, }, "י", "אי", NULL, FALSE, },
49 { { GDK_I, 0, 0, 0, }, "יִ", "איִ", NULL, FALSE, },
50 { { GDK_v, 0, 0, 0, }, "װ", NULL, NULL, FALSE, },
51 { { GDK_z, 0, 0, 0, }, "ז", NULL, NULL, FALSE, },
52 { { GDK_H, 0, 0, 0, }, "ח", NULL, NULL, FALSE, },
53 { { GDK_t, 0, 0, 0, }, "ט", NULL, NULL, FALSE, },
54 { { GDK_T, 0, 0, 0, }, "תּ", NULL, NULL, FALSE, },
55 { { GDK_y, 0, 0, 0, }, "י", NULL, NULL, FALSE, },
56 { { GDK_x, 0, 0, 0, }, "כ", NULL, "ך", FALSE, },
57 { { GDK_X, 0, 0, 0, }, "ך", NULL, NULL, FALSE, },
58 { { GDK_l, 0, 0, 0, }, "ל", NULL, NULL, FALSE, },
59 { { GDK_m, 0, 0, 0, }, "מ", NULL, "ם", FALSE, },
60 { { GDK_M, 0, 0, 0, }, "ם", NULL, NULL, FALSE, },
61 { { GDK_n, 0, 0, 0, }, "נ", NULL, "ן", FALSE, },
62 { { GDK_N, 0, 0, 0, }, "ן", NULL, NULL, FALSE, },
63 { { GDK_s, 0, 0, 0, }, "ס", NULL, NULL, FALSE, },
64 { { GDK_S, 0, 0, 0, }, "ת", NULL, NULL, FALSE, },
65 { { GDK_e, 0, 0, 0, }, "ע", NULL, NULL, FALSE, },
66 { { GDK_E, 0, 0, 0, }, "ײ", "אײ", NULL, FALSE, },
67 { { GDK_o, 0, 0, 0, }, "אָ", NULL, NULL, FALSE, },
68 { { GDK_O, 0, 0, 0, }, "ױ", "אױ", NULL, FALSE, },
69 { { GDK_u, 0, 0, 0, }, "ו", "או", NULL, FALSE, },
70 { { GDK_U, 0, 0, 0, }, "וּ", "אוּ", NULL, FALSE, },
71 { { GDK_p, 0, 0, 0, }, "פּ", NULL, NULL, FALSE, },
72 { { GDK_P, 0, 0, 0, }, "פ", NULL, NULL, FALSE, },
73 { { GDK_w, 0, 0, 0, }, "ש", NULL, NULL, FALSE, },
74 { { GDK_W, 0, 0, 0, }, "שׂ", NULL, NULL, FALSE, },
75 { { GDK_f, 0, 0, 0, }, "פֿ", NULL, "ף", FALSE, },
76 { { GDK_F, 0, 0, 0, }, "ף", NULL, NULL, FALSE, },
77 { { GDK_k, 0, 0, 0, }, "ק", NULL, NULL, FALSE, },
78 { { GDK_K, 0, 0, 0, }, "כּ", NULL, NULL, FALSE, },
79 { { GDK_r, 0, 0, 0, }, "ר", NULL, NULL, FALSE, },
80 { { GDK_Y, 0, 0, 0, }, "ײַ", "אײַ", NULL, FALSE, },
81 { { GDK_minus, 0, 0, 0, }, "־", NULL, NULL, TRUE, },
82 { { GDK_apostrophe, 0, 0, 0, }, "'", NULL, NULL, TRUE, },
83 { { GDK_comma, 0, 0, 0, }, ",", NULL, NULL, TRUE, },
84 { { GDK_s, GDK_h, 0, 0, }, "ש", NULL, NULL, FALSE, },
85 { { GDK_t, GDK_s, 0, 0, }, "צ", NULL, "ץ", FALSE, },
86 { { GDK_t, GDK_z, 0, 0, }, "צ", NULL, "ץ", FALSE, },
87 { { GDK_z, GDK_h, 0, 0, }, "זש", NULL, NULL, FALSE, },
88 { { GDK_a, GDK_y, 0, 0, }, "ײַ", "אײַ", NULL, FALSE, },
89 { { GDK_o, GDK_y, 0, 0, }, "ױ", "אױ", NULL, FALSE, },
90 { { GDK_o, GDK_i, 0, 0, }, "ױ", "אױ", NULL, FALSE, },
91 { { GDK_d, GDK_j, 0, 0, }, "דזש", NULL, NULL, FALSE, },
92 { { GDK_e, GDK_y, 0, 0, }, "ײ", "אײ", NULL, FALSE, },
93 { { GDK_apostrophe, GDK_apostrophe, 0, 0, }, "“", NULL, NULL, TRUE, },
94 { { GDK_comma, GDK_comma, 0, 0, }, "„", NULL, NULL, TRUE, },
95 { { GDK_k, GDK_h, 0, 0, }, "כ", NULL, "ך", FALSE, },
96 { { GDK_c, GDK_h, 0, 0, }, "כ", NULL, "ך", FALSE, },
97 { { GDK_u, GDK_v, 0, 0, }, "וּװ", "אוּװ", NULL, FALSE, },
98 { { GDK_u, GDK_u, 0, 0, }, "וּו", "אוּו", NULL, FALSE, },
99 { { GDK_u, GDK_i, 0, 0, }, "ויִ", "אויִ", NULL, FALSE, },
100 { { GDK_u, GDK_y, 0, 0, }, "וּי", "אוּי", NULL, FALSE, },
101 { { GDK_v, GDK_u, 0, 0, }, "װוּ", NULL, NULL, FALSE, },
102 { { GDK_y, GDK_i, 0, 0, }, "ייִ", NULL, NULL, FALSE, },
103 { { GDK_i, GDK_i, 0, 0, }, "יִיִ", "איִיִ", NULL, FALSE, },
104 { { GDK_i, GDK_y, 0, 0, }, "יִי", "איִי", NULL, FALSE, },
105 { { GDK_E, GDK_i, 0, 0, }, "ײיִ", "אײיִ", NULL, FALSE, },
106 { { GDK_i, GDK_e, 0, 0, }, "יִע", "איִע", NULL, FALSE, },
107 { { GDK_i, GDK_a, 0, 0, }, "יִאַ", "איִאַ", NULL, FALSE, },
108 { { GDK_i, GDK_o, 0, 0, }, "יִאָ", "איִאָ", NULL, FALSE, },
109 { { GDK_t, GDK_s, GDK_h, 0, }, "טש", NULL, NULL, FALSE, },
113 static const guint16 mim_compose_ignore[] =
134 clear_compose_buffer ()
136 memset (compose_buffer, 0, sizeof (compose_buffer));
141 /* handles null case */
143 commit_preceding_possible_final_form (GtkIMContext *context,
146 if (preceding_possible_final_form != NULL)
149 g_signal_emit_by_name (context, "commit",
150 preceding_possible_final_form->final);
152 g_signal_emit_by_name (context, "commit",
153 preceding_possible_final_form->normal);
155 preceding_possible_final_form = NULL;
159 /* returns the composed string iff keys exactly matches the compose
161 static ComposeSequence *
162 find_complete_compose_sequence (guint *keys)
166 for (i = 0; i < G_N_ELEMENTS (mim_compose_seqs); i++)
167 for (j = 0; j <= MIM_MAX_COMPOSE_LEN; j++)
169 if (keys[j] != mim_compose_seqs[i].keys[j])
171 else if (keys[j] == 0 && keys[j] == mim_compose_seqs[i].keys[j])
172 return mim_compose_seqs + i;
179 /* returns the composed string iff keys is a substring thang of the compose
181 static ComposeSequence *
182 find_incomplete_compose_sequence (guint *keys)
186 for (i = 0; i < G_N_ELEMENTS (mim_compose_seqs); i++)
187 for (j = 0; j <= MIM_MAX_COMPOSE_LEN; j++)
189 if (keys[j] == 0 && mim_compose_seqs[i].keys[j] != 0)
190 return mim_compose_seqs + i;
191 else if (keys[j] != mim_compose_seqs[i].keys[j])
200 get_appropriate_string (ComposeSequence *comp_seq, gboolean is_initial)
202 if (comp_seq == NULL)
204 else if (is_initial && comp_seq->initial != NULL)
205 return comp_seq->initial;
207 return comp_seq->normal;
211 /* is this a character that could appear in a mim word */
213 is_mim_word_character (gunichar uc)
215 return (((uc >= 0x0590 && uc <= 0x5ff) || (uc >= 0xfb1d && uc <= 0xfb4f))
216 && g_unichar_isdefined (uc) && ! g_unichar_ispunct (uc));
222 at_initial_position (GtkIMContext *context)
229 if (! gtk_im_context_get_surrounding (context, &text, &cursor_index))
232 prevp = g_utf8_find_prev_char (text, text + cursor_index);
236 uc = g_utf8_get_char_validated (prevp, text + cursor_index - prevp);
237 g_return_val_if_fail (uc != (gunichar)(-1) && uc != (gunichar)(-2),
240 if (is_mim_word_character (uc))
248 mim_get_preedit_string (GtkIMContext *context,
250 PangoAttrList **attrs,
253 ComposeSequence *comp_seq;
258 is_initial = at_initial_position (context)
259 && preceding_possible_final_form == NULL;
261 comp_seq = find_complete_compose_sequence (compose_buffer);
262 if (comp_seq == NULL)
265 string = get_appropriate_string (comp_seq, is_initial);
267 if (preceding_possible_final_form != NULL)
268 *str = g_strdup_printf ("%s%s", preceding_possible_final_form->normal,
271 *str = g_strdup (string);
277 *attrs = pango_attr_list_new ();
281 PangoAttribute *attr;
282 attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
283 attr->start_index = 0;
284 attr->end_index = len;
285 pango_attr_list_insert (*attrs, attr);
295 mim_reset (GtkIMContext *context)
297 clear_compose_buffer ();
298 preceding_possible_final_form = NULL;
299 g_signal_emit_by_name (context, "preedit-changed");
304 no_sequence_matches (GtkIMContext *context, GdkEventKey *event)
309 uc = gdk_keyval_to_unicode (event->keyval);
312 buf[g_unichar_to_utf8 (uc, buf)] = '\0';
313 g_signal_emit_by_name (context, "commit", buf);
314 g_signal_emit_by_name (context, "preedit-changed");
324 mim_filter_keypress (GtkIMContext *context,
327 ComposeSequence *comp_seq;
331 if (event->type == GDK_KEY_RELEASE)
334 /* don't filter key events with accelerator modifiers held down */
336 & (gtk_accelerator_get_default_mod_mask () & ~GDK_SHIFT_MASK))
339 for (i = 0; i < G_N_ELEMENTS (mim_compose_ignore); i++)
340 if (event->keyval == mim_compose_ignore[i])
343 is_initial = at_initial_position (context)
344 && preceding_possible_final_form == NULL;
346 /* '|' commits what we have */
347 if (event->keyval == GDK_bar && (n_compose > 0
348 || preceding_possible_final_form != NULL))
350 commit_preceding_possible_final_form (context, FALSE); /* non-final */
352 comp_seq = find_complete_compose_sequence (compose_buffer);
353 if (comp_seq != NULL)
355 g_signal_emit_by_name (context, "commit",
356 get_appropriate_string (comp_seq, is_initial));
357 clear_compose_buffer ();
360 g_signal_emit_by_name (context, "preedit-changed");
364 compose_buffer[n_compose] = event->keyval;
367 if (find_incomplete_compose_sequence (compose_buffer) != NULL)
369 g_signal_emit_by_name (context, "preedit-changed");
373 comp_seq = find_complete_compose_sequence (compose_buffer);
374 if (comp_seq != NULL)
376 if (comp_seq->final != NULL) /* has a final form, don't commit yet */
378 commit_preceding_possible_final_form (context,
379 comp_seq->is_terminator);
380 clear_compose_buffer ();
381 preceding_possible_final_form = comp_seq;
382 g_signal_emit_by_name (context, "preedit-changed");
387 commit_preceding_possible_final_form (context,
388 comp_seq->is_terminator);
389 g_signal_emit_by_name (context, "commit",
390 get_appropriate_string (comp_seq, is_initial));
391 clear_compose_buffer ();
392 g_signal_emit_by_name (context, "preedit-changed");
397 /* if we reach this point, the sequence *with the key just pressed*
398 * cannot be a complete or incomplete match, so: commit the old sequence,
399 * then reprocess this key */
402 compose_buffer[n_compose] = 0;
406 comp_seq = find_complete_compose_sequence (compose_buffer);
407 commit_preceding_possible_final_form (context, comp_seq->is_terminator);
408 if (comp_seq->final != NULL)
410 clear_compose_buffer ();
411 preceding_possible_final_form = comp_seq;
415 g_signal_emit_by_name (context, "commit",
416 get_appropriate_string (comp_seq, is_initial));
417 clear_compose_buffer ();
418 g_signal_emit_by_name (context, "preedit-changed");
421 return mim_filter_keypress (context, event);
424 commit_preceding_possible_final_form (context, TRUE);
426 return no_sequence_matches (context, event);
431 mim_class_init (GtkIMContextClass *clazz)
433 clazz->filter_keypress = mim_filter_keypress;
434 clazz->get_preedit_string = mim_get_preedit_string;
435 clazz->reset = mim_reset;
439 mim_init (GtkIMContext *im_context)
445 mim_register_type (GTypeModule *module)
447 static const GTypeInfo object_info =
449 sizeof (GtkIMContextClass),
450 (GBaseInitFunc) NULL,
451 (GBaseFinalizeFunc) NULL,
452 (GClassInitFunc) mim_class_init,
453 NULL, /* class_finalize */
454 NULL, /* class_data */
455 sizeof (GtkIMContext),
457 (GInstanceInitFunc) mim_init,
461 g_type_module_register_type (module,
463 "GtkIMContextYiddishNoah",
468 static const GtkIMContextInfo mim_info =
471 N_("MiM"), /* Human readable name */
472 GETTEXT_PACKAGE, /* Translation domain */
473 LOCALEDIR, /* Dir for bindtextdomain */
474 "zh_CN", /* Languages for which this module is the default */
478 static const GtkIMContextInfo *info_list[] =
485 im_module_init (GTypeModule *module)
487 mim_register_type (module);
496 im_module_list (const GtkIMContextInfo ***contexts, gint *n_contexts)
498 *contexts = info_list;
499 *n_contexts = G_N_ELEMENTS (info_list);
504 im_module_create (const gchar *context_id)
506 if (strcmp (context_id, "mim-n") == 0)
507 return GTK_IM_CONTEXT (g_object_new (type_mim, NULL));