From cf2341863df87e85c38f8cac1bd154d1b7ccf89d Mon Sep 17 00:00:00 2001 From: Alsor Zhou Date: Thu, 22 Oct 2009 17:32:12 +0800 Subject: [PATCH] Add gtk immodule main file --- src/ui/mim-immodule.c | 513 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 513 insertions(+) create mode 100644 src/ui/mim-immodule.c diff --git a/src/ui/mim-immodule.c b/src/ui/mim-immodule.c new file mode 100644 index 0000000..b2ef13e --- /dev/null +++ b/src/ui/mim-immodule.c @@ -0,0 +1,513 @@ +/* + * GTK+ MiM input module + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include "im-extra-intl.h" + + +GType type_mim = 0; + +enum { MIM_MAX_COMPOSE_LEN = 3 }; + +typedef struct +{ + guint keys[MIM_MAX_COMPOSE_LEN + 1]; /* 0-terminated */ + gchar *normal; + gchar *initial; + gchar *final; + gboolean is_terminator; /* true if a preceding possible final form + should be final */ +} +ComposeSequence; + +/* like GtkIMContextSimple */ +static guint compose_buffer[MIM_MAX_COMPOSE_LEN + 1]; +static int n_compose = 0; +static ComposeSequence *preceding_possible_final_form = NULL; + +static ComposeSequence mim_compose_seqs[] = +{ + { { GDK_a, 0, 0, 0, }, "אַ", NULL, NULL, FALSE, }, + { { GDK_A, 0, 0, 0, }, "א", NULL, NULL, FALSE, }, + { { GDK_b, 0, 0, 0, }, "ב", NULL, NULL, FALSE, }, + { { GDK_B, 0, 0, 0, }, "בֿ", NULL, NULL, FALSE, }, + { { GDK_c, 0, 0, 0, }, "צ", NULL, "ץ", FALSE, }, + { { GDK_C, 0, 0, 0, }, "ץ", NULL, NULL, FALSE, }, + { { GDK_g, 0, 0, 0, }, "ג", NULL, NULL, FALSE, }, + { { GDK_d, 0, 0, 0, }, "ד", NULL, NULL, FALSE, }, + { { GDK_h, 0, 0, 0, }, "ה", NULL, NULL, FALSE, }, + { { GDK_i, 0, 0, 0, }, "י", "אי", NULL, FALSE, }, + { { GDK_I, 0, 0, 0, }, "יִ", "איִ", NULL, FALSE, }, + { { GDK_v, 0, 0, 0, }, "װ", NULL, NULL, FALSE, }, + { { GDK_z, 0, 0, 0, }, "ז", NULL, NULL, FALSE, }, + { { GDK_H, 0, 0, 0, }, "ח", NULL, NULL, FALSE, }, + { { GDK_t, 0, 0, 0, }, "ט", NULL, NULL, FALSE, }, + { { GDK_T, 0, 0, 0, }, "תּ", NULL, NULL, FALSE, }, + { { GDK_y, 0, 0, 0, }, "י", NULL, NULL, FALSE, }, + { { GDK_x, 0, 0, 0, }, "כ", NULL, "ך", FALSE, }, + { { GDK_X, 0, 0, 0, }, "ך", NULL, NULL, FALSE, }, + { { GDK_l, 0, 0, 0, }, "ל", NULL, NULL, FALSE, }, + { { GDK_m, 0, 0, 0, }, "מ", NULL, "ם", FALSE, }, + { { GDK_M, 0, 0, 0, }, "ם", NULL, NULL, FALSE, }, + { { GDK_n, 0, 0, 0, }, "נ", NULL, "ן", FALSE, }, + { { GDK_N, 0, 0, 0, }, "ן", NULL, NULL, FALSE, }, + { { GDK_s, 0, 0, 0, }, "ס", NULL, NULL, FALSE, }, + { { GDK_S, 0, 0, 0, }, "ת", NULL, NULL, FALSE, }, + { { GDK_e, 0, 0, 0, }, "ע", NULL, NULL, FALSE, }, + { { GDK_E, 0, 0, 0, }, "ײ", "אײ", NULL, FALSE, }, + { { GDK_o, 0, 0, 0, }, "אָ", NULL, NULL, FALSE, }, + { { GDK_O, 0, 0, 0, }, "ױ", "אױ", NULL, FALSE, }, + { { GDK_u, 0, 0, 0, }, "ו", "או", NULL, FALSE, }, + { { GDK_U, 0, 0, 0, }, "וּ", "אוּ", NULL, FALSE, }, + { { GDK_p, 0, 0, 0, }, "פּ", NULL, NULL, FALSE, }, + { { GDK_P, 0, 0, 0, }, "פ", NULL, NULL, FALSE, }, + { { GDK_w, 0, 0, 0, }, "ש", NULL, NULL, FALSE, }, + { { GDK_W, 0, 0, 0, }, "שׂ", NULL, NULL, FALSE, }, + { { GDK_f, 0, 0, 0, }, "פֿ", NULL, "ף", FALSE, }, + { { GDK_F, 0, 0, 0, }, "ף", NULL, NULL, FALSE, }, + { { GDK_k, 0, 0, 0, }, "ק", NULL, NULL, FALSE, }, + { { GDK_K, 0, 0, 0, }, "כּ", NULL, NULL, FALSE, }, + { { GDK_r, 0, 0, 0, }, "ר", NULL, NULL, FALSE, }, + { { GDK_Y, 0, 0, 0, }, "ײַ", "אײַ", NULL, FALSE, }, + { { GDK_minus, 0, 0, 0, }, "־", NULL, NULL, TRUE, }, + { { GDK_apostrophe, 0, 0, 0, }, "'", NULL, NULL, TRUE, }, + { { GDK_comma, 0, 0, 0, }, ",", NULL, NULL, TRUE, }, + { { GDK_s, GDK_h, 0, 0, }, "ש", NULL, NULL, FALSE, }, + { { GDK_t, GDK_s, 0, 0, }, "צ", NULL, "ץ", FALSE, }, + { { GDK_t, GDK_z, 0, 0, }, "צ", NULL, "ץ", FALSE, }, + { { GDK_z, GDK_h, 0, 0, }, "זש", NULL, NULL, FALSE, }, + { { GDK_a, GDK_y, 0, 0, }, "ײַ", "אײַ", NULL, FALSE, }, + { { GDK_o, GDK_y, 0, 0, }, "ױ", "אױ", NULL, FALSE, }, + { { GDK_o, GDK_i, 0, 0, }, "ױ", "אױ", NULL, FALSE, }, + { { GDK_d, GDK_j, 0, 0, }, "דזש", NULL, NULL, FALSE, }, + { { GDK_e, GDK_y, 0, 0, }, "ײ", "אײ", NULL, FALSE, }, + { { GDK_apostrophe, GDK_apostrophe, 0, 0, }, "“", NULL, NULL, TRUE, }, + { { GDK_comma, GDK_comma, 0, 0, }, "„", NULL, NULL, TRUE, }, + { { GDK_k, GDK_h, 0, 0, }, "כ", NULL, "ך", FALSE, }, + { { GDK_c, GDK_h, 0, 0, }, "כ", NULL, "ך", FALSE, }, + { { GDK_u, GDK_v, 0, 0, }, "וּװ", "אוּװ", NULL, FALSE, }, + { { GDK_u, GDK_u, 0, 0, }, "וּו", "אוּו", NULL, FALSE, }, + { { GDK_u, GDK_i, 0, 0, }, "ויִ", "אויִ", NULL, FALSE, }, + { { GDK_u, GDK_y, 0, 0, }, "וּי", "אוּי", NULL, FALSE, }, + { { GDK_v, GDK_u, 0, 0, }, "װוּ", NULL, NULL, FALSE, }, + { { GDK_y, GDK_i, 0, 0, }, "ייִ", NULL, NULL, FALSE, }, + { { GDK_i, GDK_i, 0, 0, }, "יִיִ", "איִיִ", NULL, FALSE, }, + { { GDK_i, GDK_y, 0, 0, }, "יִי", "איִי", NULL, FALSE, }, + { { GDK_E, GDK_i, 0, 0, }, "ײיִ", "אײיִ", NULL, FALSE, }, + { { GDK_i, GDK_e, 0, 0, }, "יִע", "איִע", NULL, FALSE, }, + { { GDK_i, GDK_a, 0, 0, }, "יִאַ", "איִאַ", NULL, FALSE, }, + { { GDK_i, GDK_o, 0, 0, }, "יִאָ", "איִאָ", NULL, FALSE, }, + { { GDK_t, GDK_s, GDK_h, 0, }, "טש", NULL, NULL, FALSE, }, +}; + + +static const guint16 mim_compose_ignore[] = +{ + GDK_Shift_L, + GDK_Shift_R, + GDK_Control_L, + GDK_Control_R, + GDK_Caps_Lock, + GDK_Shift_Lock, + GDK_Meta_L, + GDK_Meta_R, + GDK_Alt_L, + GDK_Alt_R, + GDK_Super_L, + GDK_Super_R, + GDK_Hyper_L, + GDK_Hyper_R, + GDK_Mode_switch, +}; + + +static void +clear_compose_buffer () +{ + memset (compose_buffer, 0, sizeof (compose_buffer)); + n_compose = 0; +} + + +/* handles null case */ +static void +commit_preceding_possible_final_form (GtkIMContext *context, + gboolean is_final) +{ + if (preceding_possible_final_form != NULL) + { + if (is_final) + g_signal_emit_by_name (context, "commit", + preceding_possible_final_form->final); + else + g_signal_emit_by_name (context, "commit", + preceding_possible_final_form->normal); + + preceding_possible_final_form = NULL; + } +} + +/* returns the composed string iff keys exactly matches the compose + * sequence keys */ +static ComposeSequence * +find_complete_compose_sequence (guint *keys) +{ + gint i, j; + + for (i = 0; i < G_N_ELEMENTS (mim_compose_seqs); i++) + for (j = 0; j <= MIM_MAX_COMPOSE_LEN; j++) + { + if (keys[j] != mim_compose_seqs[i].keys[j]) + break; + else if (keys[j] == 0 && keys[j] == mim_compose_seqs[i].keys[j]) + return mim_compose_seqs + i; + } + + return NULL; +} + + +/* returns the composed string iff keys is a substring thang of the compose + * sequence keys */ +static ComposeSequence * +find_incomplete_compose_sequence (guint *keys) +{ + gint i, j; + + for (i = 0; i < G_N_ELEMENTS (mim_compose_seqs); i++) + for (j = 0; j <= MIM_MAX_COMPOSE_LEN; j++) + { + if (keys[j] == 0 && mim_compose_seqs[i].keys[j] != 0) + return mim_compose_seqs + i; + else if (keys[j] != mim_compose_seqs[i].keys[j]) + break; + } + + return NULL; +} + + +static gchar * +get_appropriate_string (ComposeSequence *comp_seq, gboolean is_initial) +{ + if (comp_seq == NULL) + return NULL; + else if (is_initial && comp_seq->initial != NULL) + return comp_seq->initial; + else + return comp_seq->normal; +} + + +/* is this a character that could appear in a mim word */ +static gboolean +is_mim_word_character (gunichar uc) +{ + return (((uc >= 0x0590 && uc <= 0x5ff) || (uc >= 0xfb1d && uc <= 0xfb4f)) + && g_unichar_isdefined (uc) && ! g_unichar_ispunct (uc)); + +} + + +static gboolean +at_initial_position (GtkIMContext *context) +{ + gchar *text; + gchar *prevp; + gint cursor_index; + gunichar uc; + + if (! gtk_im_context_get_surrounding (context, &text, &cursor_index)) + return FALSE; + + prevp = g_utf8_find_prev_char (text, text + cursor_index); + if (prevp == NULL) + return TRUE; + + uc = g_utf8_get_char_validated (prevp, text + cursor_index - prevp); + g_return_val_if_fail (uc != (gunichar)(-1) && uc != (gunichar)(-2), + FALSE); + + if (is_mim_word_character (uc)) + return FALSE; + else + return TRUE; +} + + +static void +mim_get_preedit_string (GtkIMContext *context, + gchar **str, + PangoAttrList **attrs, + gint *cursor_pos) +{ + ComposeSequence *comp_seq; + gboolean is_initial; + gchar *string; + gint len; + + is_initial = at_initial_position (context) + && preceding_possible_final_form == NULL; + + comp_seq = find_complete_compose_sequence (compose_buffer); + if (comp_seq == NULL) + string = ""; + else + string = get_appropriate_string (comp_seq, is_initial); + + if (preceding_possible_final_form != NULL) + *str = g_strdup_printf ("%s%s", preceding_possible_final_form->normal, + string); + else + *str = g_strdup (string); + + len = strlen (*str); + + if (attrs) + { + *attrs = pango_attr_list_new (); + + if (len != 0) + { + PangoAttribute *attr; + attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); + attr->start_index = 0; + attr->end_index = len; + pango_attr_list_insert (*attrs, attr); + } + } + + if (cursor_pos) + *cursor_pos = len; +} + + +static void +mim_reset (GtkIMContext *context) +{ + clear_compose_buffer (); + preceding_possible_final_form = NULL; + g_signal_emit_by_name (context, "preedit-changed"); +} + + +static gboolean +no_sequence_matches (GtkIMContext *context, GdkEventKey *event) +{ + gunichar uc; + gchar buf[7]; + + uc = gdk_keyval_to_unicode (event->keyval); + if (uc != 0) + { + buf[g_unichar_to_utf8 (uc, buf)] = '\0'; + g_signal_emit_by_name (context, "commit", buf); + g_signal_emit_by_name (context, "preedit-changed"); + + return TRUE; + } + else + return FALSE; +} + + +static gboolean +mim_filter_keypress (GtkIMContext *context, + GdkEventKey *event) +{ + ComposeSequence *comp_seq; + gboolean is_initial; + gint i; + + if (event->type == GDK_KEY_RELEASE) + return FALSE; + + /* don't filter key events with accelerator modifiers held down */ + if (event->state + & (gtk_accelerator_get_default_mod_mask () & ~GDK_SHIFT_MASK)) + return FALSE; + + for (i = 0; i < G_N_ELEMENTS (mim_compose_ignore); i++) + if (event->keyval == mim_compose_ignore[i]) + return FALSE; + + is_initial = at_initial_position (context) + && preceding_possible_final_form == NULL; + + /* '|' commits what we have */ + if (event->keyval == GDK_bar && (n_compose > 0 + || preceding_possible_final_form != NULL)) + { + commit_preceding_possible_final_form (context, FALSE); /* non-final */ + + comp_seq = find_complete_compose_sequence (compose_buffer); + if (comp_seq != NULL) + { + g_signal_emit_by_name (context, "commit", + get_appropriate_string (comp_seq, is_initial)); + clear_compose_buffer (); + } + + g_signal_emit_by_name (context, "preedit-changed"); + return TRUE; + } + + compose_buffer[n_compose] = event->keyval; + n_compose++; + + if (find_incomplete_compose_sequence (compose_buffer) != NULL) + { + g_signal_emit_by_name (context, "preedit-changed"); + return TRUE; + } + + comp_seq = find_complete_compose_sequence (compose_buffer); + if (comp_seq != NULL) + { + if (comp_seq->final != NULL) /* has a final form, don't commit yet */ + { + commit_preceding_possible_final_form (context, + comp_seq->is_terminator); + clear_compose_buffer (); + preceding_possible_final_form = comp_seq; + g_signal_emit_by_name (context, "preedit-changed"); + return TRUE; + } + else + { + commit_preceding_possible_final_form (context, + comp_seq->is_terminator); + g_signal_emit_by_name (context, "commit", + get_appropriate_string (comp_seq, is_initial)); + clear_compose_buffer (); + g_signal_emit_by_name (context, "preedit-changed"); + return TRUE; + } + } + + /* if we reach this point, the sequence *with the key just pressed* + * cannot be a complete or incomplete match, so: commit the old sequence, + * then reprocess this key */ + + n_compose--; + compose_buffer[n_compose] = 0; + + if (n_compose > 0) + { + comp_seq = find_complete_compose_sequence (compose_buffer); + commit_preceding_possible_final_form (context, comp_seq->is_terminator); + if (comp_seq->final != NULL) + { + clear_compose_buffer (); + preceding_possible_final_form = comp_seq; + } + else + { + g_signal_emit_by_name (context, "commit", + get_appropriate_string (comp_seq, is_initial)); + clear_compose_buffer (); + g_signal_emit_by_name (context, "preedit-changed"); + } + + return mim_filter_keypress (context, event); + } + else + commit_preceding_possible_final_form (context, TRUE); + + return no_sequence_matches (context, event); +} + + +static void +mim_class_init (GtkIMContextClass *clazz) +{ + clazz->filter_keypress = mim_filter_keypress; + clazz->get_preedit_string = mim_get_preedit_string; + clazz->reset = mim_reset; +} + +static void +mim_init (GtkIMContext *im_context) +{ +} + + +static void +mim_register_type (GTypeModule *module) +{ + static const GTypeInfo object_info = + { + sizeof (GtkIMContextClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) mim_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkIMContext), + 0, + (GInstanceInitFunc) mim_init, + }; + + type_mim = + g_type_module_register_type (module, + GTK_TYPE_IM_CONTEXT, + "GtkIMContextYiddishNoah", + &object_info, 0); +} + + +static const GtkIMContextInfo mim_info = +{ + "mim", /* ID */ + N_("MiM"), /* Human readable name */ + GETTEXT_PACKAGE, /* Translation domain */ + LOCALEDIR, /* Dir for bindtextdomain */ + "zh_CN", /* Languages for which this module is the default */ +}; + + +static const GtkIMContextInfo *info_list[] = +{ + &mim_info, +}; + + +void +im_module_init (GTypeModule *module) +{ + mim_register_type (module); +} + +void +im_module_exit () +{ +} + +void +im_module_list (const GtkIMContextInfo ***contexts, gint *n_contexts) +{ + *contexts = info_list; + *n_contexts = G_N_ELEMENTS (info_list); +} + + +GtkIMContext * +im_module_create (const gchar *context_id) +{ + if (strcmp (context_id, "mim-n") == 0) + return GTK_IM_CONTEXT (g_object_new (type_mim, NULL)); + else + return NULL; +} + + + -- 1.7.9.5