diff --git a/src/engine.c b/src/engine.c index 2fffad0..60e479e 100644 --- a/src/engine.c +++ b/src/engine.c @@ -16,6 +16,8 @@ typedef struct _IBusHangulEngine IBusHangulEngine; typedef struct _IBusHangulEngineClass IBusHangulEngineClass; +typedef struct _HanjaKeyList HanjaKeyList; + struct _IBusHangulEngine { IBusEngine parent; @@ -41,6 +43,11 @@ struct KeyEvent { guint modifiers; }; +struct _HanjaKeyList { + guint all_modifiers; + GArray *keys; +}; + /* functions prototype */ static void ibus_hangul_engine_class_init (IBusHangulEngineClass *klass); @@ -89,6 +96,12 @@ static void ibus_hangul_engine_property_hide const gchar *prop_name); #endif +static void ibus_hangul_engine_candidate_clicked + (IBusEngine *engine, + guint index, + guint button, + guint state); + static void ibus_hangul_engine_flush (IBusHangulEngine *hangul); static void ibus_hangul_engine_update_preedit_text (IBusHangulEngine *hangul); @@ -106,18 +119,29 @@ static void lookup_table_set_visible (IBusLookupTable *table, static gboolean lookup_table_is_visible (IBusLookupTable *table); -static void key_event_list_set (GArray *list, - const gchar *str); static gboolean key_event_list_match (GArray *list, guint keyval, guint modifiers); +static void hanja_key_list_init (HanjaKeyList *list); +static void hanja_key_list_fini (HanjaKeyList *list); +static void hanja_key_list_set_from_string(HanjaKeyList *list, + const char *str); +static void hanja_key_list_append (HanjaKeyList *list, + guint keyval, + guint modifiers); +static gboolean hanja_key_list_match (HanjaKeyList *list, + guint keyval, + guint modifiers); +static gboolean hanja_key_list_has_modifier (HanjaKeyList *list, + guint keyval); + static IBusEngineClass *parent_class = NULL; static HanjaTable *hanja_table = NULL; static HanjaTable *symbol_table = NULL; static IBusConfig *config = NULL; static GString *hangul_keyboard = NULL; -static GArray *hanja_keys = NULL; +static HanjaKeyList hanja_keys; static int lookup_table_orientation = 0; GType @@ -170,29 +194,25 @@ ibus_hangul_init (IBusBus *bus) g_value_unset(&value); } - hanja_keys = g_array_sized_new(FALSE, TRUE, sizeof(struct KeyEvent), 4); + hanja_key_list_init(&hanja_keys); + res = ibus_config_get_value (config, "engine/Hangul", "HanjaKeys", &value); if (res) { const gchar* str = g_value_get_string (&value); - key_event_list_set(hanja_keys, str); + hanja_key_list_set_from_string(&hanja_keys, str); g_value_unset(&value); } else { - struct KeyEvent ev; - - ev.keyval = IBUS_Hangul_Hanja; - ev.modifiers = 0; - g_array_append_val(hanja_keys, ev); - - ev.keyval = IBUS_F9; - ev.modifiers = 0; - g_array_append_val(hanja_keys, ev); + hanja_key_list_append(&hanja_keys, IBUS_Hangul_Hanja, 0); + hanja_key_list_append(&hanja_keys, IBUS_F9, 0); } } void ibus_hangul_exit (void) { + hanja_key_list_fini(&hanja_keys); + hanja_table_delete (hanja_table); hanja_table = NULL; @@ -234,6 +254,8 @@ ibus_hangul_engine_class_init (IBusHangulEngineClass *klass) engine_class->cursor_down = ibus_hangul_engine_cursor_down; engine_class->property_activate = ibus_hangul_engine_property_activate; + + engine_class->candidate_clicked = ibus_hangul_engine_candidate_clicked; } static void @@ -342,6 +364,11 @@ ibus_hangul_engine_update_preedit_text (IBusHangulEngine *hangul) ustring_append_ucs4 (preedit, hic_preedit, -1); if (ustring_length(preedit) > 0) { + IBusPreeditFocusMode preedit_option = IBUS_ENGINE_PREEDIT_COMMIT; + + if (hangul->hanja_list != NULL) + preedit_option = IBUS_ENGINE_PREEDIT_CLEAR; + text = ibus_text_new_from_ucs4 ((gunichar*)preedit->data); // ibus-hangul's internal preedit string ibus_text_append_attribute (text, IBUS_ATTR_TYPE_UNDERLINE, @@ -356,7 +383,7 @@ ibus_hangul_engine_update_preedit_text (IBusHangulEngine *hangul) text, ibus_text_get_length (text), TRUE, - IBUS_ENGINE_PREEDIT_COMMIT); + preedit_option); } else { text = ibus_text_new_from_static_string (""); ibus_engine_update_preedit_text ((IBusEngine *)hangul, text, 0, FALSE); @@ -492,6 +519,9 @@ ibus_hangul_engine_update_lookup_table (IBusHangulEngine *hangul) ibus_hangul_engine_update_hanja_list (hangul); if (hangul->hanja_list != NULL) { + // We should redraw preedit text with IBUS_ENGINE_PREEDIT_CLEAR option + // here to prevent committing it on focus out event incidentally. + ibus_hangul_engine_update_preedit_text (hangul); ibus_hangul_engine_apply_hanja_list (hangul); } else { ibus_hangul_engine_hide_lookup_table (hangul); @@ -505,6 +535,12 @@ ibus_hangul_engine_process_candidate_key_event (IBusHangulEngine *hangul, { if (keyval == IBUS_Escape) { ibus_hangul_engine_hide_lookup_table (hangul); + // When the lookup table is poped up, preedit string is + // updated with IBUS_ENGINE_PREEDIT_CLEAR option. + // So, when focus is out, the preedit text will not be committed. + // To prevent this problem, we have to update preedit text here + // with IBUS_ENGINE_PREEDIT_COMMIT option. + ibus_hangul_engine_update_preedit_text (hangul); return TRUE; } else if (keyval == IBUS_Return) { ibus_hangul_engine_commit_current_candidate (hangul); @@ -638,6 +674,7 @@ ibus_hangul_engine_process_key_event (IBusEngine *engine, { IBusHangulEngine *hangul = (IBusHangulEngine *) engine; + guint mask; gboolean retval; const ucschar *str; @@ -652,7 +689,17 @@ ibus_hangul_engine_process_key_event (IBusEngine *engine, if (keyval == IBUS_Shift_L || keyval == IBUS_Shift_R) return FALSE; - if (key_event_list_match(hanja_keys, keyval, modifiers)) { + // If hanja key has any modifiers, we ignore that modifier keyval, + // or we cannot make the hanja key work. + // Because when we get the modifier key alone, we commit the + // current preedit string. So after that, even if we get the + // right hanja key event, we don't have preedit string to be changed + // to hanja word. + // See this bug: http://code.google.com/p/ibus/issues/detail?id=1036 + if (hanja_key_list_has_modifier(&hanja_keys, keyval)) + return FALSE; + + if (hanja_key_list_match(&hanja_keys, keyval, modifiers)) { if (hangul->hanja_list == NULL) { ibus_hangul_engine_update_lookup_table (hangul); } else { @@ -661,9 +708,6 @@ ibus_hangul_engine_process_key_event (IBusEngine *engine, return TRUE; } - if (modifiers & (IBUS_CONTROL_MASK | IBUS_MOD1_MASK)) - return FALSE; - if (hangul->hanja_list != NULL) { retval = ibus_hangul_engine_process_candidate_key_event (hangul, keyval, modifiers); @@ -675,6 +719,18 @@ ibus_hangul_engine_process_key_event (IBusEngine *engine, } } + // if we've got a key event with modifiers, commit current + // preedit string and ignore this key event. + // So, if you want to add some key event handler, put it + // before this code. + // Omit shift, capslock, numlock and xkb modifiers. + mask = IBUS_MODIFIER_MASK & + ~(IBUS_SHIFT_MASK | IBUS_LOCK_MASK | IBUS_MOD2_MASK); + if (modifiers & mask) { + ibus_hangul_engine_flush (hangul); + return FALSE; + } + if (keyval == IBUS_BackSpace) { retval = hangul_ic_backspace (hangul->context); } else { @@ -740,17 +796,16 @@ ibus_hangul_engine_flush (IBusHangulEngine *hangul) ustring_append_ucs4 (hangul->preedit, str, -1); - if (ustring_length (hangul->preedit) == 0) - return; + if (ustring_length (hangul->preedit) != 0) { + str = ustring_begin (hangul->preedit); + text = ibus_text_new_from_ucs4 (str); - str = ustring_begin (hangul->preedit); - text = ibus_text_new_from_ucs4 (str); + ibus_engine_commit_text ((IBusEngine *) hangul, text); - ibus_engine_hide_preedit_text ((IBusEngine *) hangul); - // Use ibus_engine_update_preedit_text_with_mode instead. - //ibus_engine_commit_text ((IBusEngine *) hangul, text); + ustring_clear(hangul->preedit); + } - ustring_clear(hangul->preedit); + ibus_hangul_engine_update_preedit_text (hangul); } static void @@ -766,6 +821,8 @@ ibus_hangul_engine_focus_in (IBusEngine *engine) ibus_engine_register_properties (engine, hangul->prop_list); + ibus_hangul_engine_update_preedit_text (hangul); + if (hangul->hanja_list != NULL) { ibus_hangul_engine_update_lookup_table_ui (hangul); } @@ -779,7 +836,11 @@ ibus_hangul_engine_focus_out (IBusEngine *engine) IBusHangulEngine *hangul = (IBusHangulEngine *) engine; if (hangul->hanja_list == NULL) { - ibus_hangul_engine_flush (hangul); + // ibus-hangul uses + // ibus_engine_update_preedit_text_with_mode() function which makes + // the preedit string committed automatically when the focus is out. + // So we don't need to commit the preedit here. + hangul_ic_reset (hangul->context); } else { ibus_engine_hide_lookup_table (engine); ibus_engine_hide_auxiliary_text (engine); @@ -794,9 +855,6 @@ ibus_hangul_engine_reset (IBusEngine *engine) IBusHangulEngine *hangul = (IBusHangulEngine *) engine; ibus_hangul_engine_flush (hangul); - if (hangul->hanja_list != NULL) { - ibus_hangul_engine_hide_lookup_table (hangul); - } parent_class->reset (engine); } @@ -903,7 +961,7 @@ ibus_config_value_changed (IBusConfig *config, hangul_ic_select_keyboard (hangul->context, hangul_keyboard->str); } else if (strcmp(name, "HanjaKeys") == 0) { const gchar* str = g_value_get_string (value); - key_event_list_set(hanja_keys, str); + hanja_key_list_set_from_string(&hanja_keys, str); } } else if (strcmp(section, "panel") == 0) { if (strcmp(name, "lookup_table_orientation") == 0) { @@ -926,26 +984,10 @@ lookup_table_is_visible (IBusLookupTable *table) } static void -key_event_list_set (GArray* list, const char* str) +key_event_list_append(GArray* list, guint keyval, guint modifiers) { - gchar** items = g_strsplit(str, ",", 0); - - g_array_set_size(list, 0); - - if (items != NULL) { - int i; - for (i = 0; items[i] != NULL; ++i) { - guint keyval = 0; - guint modifiers = 0; - gboolean res; - res = ibus_key_event_from_string(items[i], &keyval, &modifiers); - if (res) { - struct KeyEvent ev = { keyval, modifiers }; - g_array_append_val(list, ev); - } - } - g_strfreev(items); - } + struct KeyEvent ev = { keyval, modifiers}; + g_array_append_val(list, ev); } static gboolean @@ -972,3 +1014,113 @@ key_event_list_match(GArray* list, guint keyval, guint modifiers) return FALSE; } + +static void +ibus_hangul_engine_candidate_clicked (IBusEngine *engine, + guint index, + guint button, + guint state) +{ + IBusHangulEngine *hangul = (IBusHangulEngine *) engine; + if (hangul == NULL) + return; + + if (hangul->table == NULL) + return; + + ibus_lookup_table_set_cursor_pos (hangul->table, index); + ibus_hangul_engine_commit_current_candidate (hangul); + + if (hangul->hanja_mode) { + ibus_hangul_engine_update_lookup_table (hangul); + } else { + ibus_hangul_engine_hide_lookup_table (hangul); + } +} + +static void +hanja_key_list_init(HanjaKeyList* list) +{ + list->all_modifiers = 0; + list->keys = g_array_sized_new(FALSE, TRUE, sizeof(struct KeyEvent), 4); +} + +static void +hanja_key_list_fini(HanjaKeyList* list) +{ + g_array_free(list->keys, TRUE); +} + +static void +hanja_key_list_append_from_string(HanjaKeyList *list, const char* str) +{ + guint keyval = 0; + guint modifiers = 0; + gboolean res; + + res = ibus_key_event_from_string(str, &keyval, &modifiers); + if (res) { + hanja_key_list_append(list, keyval, modifiers); + } +} + +static void +hanja_key_list_append(HanjaKeyList *list, guint keyval, guint modifiers) +{ + list->all_modifiers |= modifiers; + key_event_list_append(list->keys, keyval, modifiers); +} + +static void +hanja_key_list_set_from_string(HanjaKeyList *list, const char* str) +{ + gchar** items = g_strsplit(str, ",", 0); + + list->all_modifiers = 0; + g_array_set_size(list->keys, 0); + + if (items != NULL) { + int i; + for (i = 0; items[i] != NULL; ++i) { + hanja_key_list_append_from_string(list, items[i]); + } + g_strfreev(items); + } +} + +static gboolean +hanja_key_list_match(HanjaKeyList* list, guint keyval, guint modifiers) +{ + return key_event_list_match(list->keys, keyval, modifiers); +} + +static gboolean +hanja_key_list_has_modifier(HanjaKeyList* list, guint keyval) +{ + if (list->all_modifiers & IBUS_CONTROL_MASK) { + if (keyval == IBUS_Control_L || keyval == IBUS_Control_R) + return TRUE; + } + + if (list->all_modifiers & IBUS_MOD1_MASK) { + if (keyval == IBUS_Alt_L || keyval == IBUS_Alt_R) + return TRUE; + } + + if (list->all_modifiers & IBUS_SUPER_MASK) { + if (keyval == IBUS_Super_L || keyval == IBUS_Super_R) + return TRUE; + } + + if (list->all_modifiers & IBUS_HYPER_MASK) { + if (keyval == IBUS_Hyper_L || keyval == IBUS_Hyper_R) + return TRUE; + } + + if (list->all_modifiers & IBUS_META_MASK) { + if (keyval == IBUS_Meta_L || keyval == IBUS_Meta_R) + return TRUE; + } + + return FALSE; +}