Add text conversion feature using surrounding text
authorChoe Hwanjin <choe.hwanjin@gmail.com>
Thu, 5 Jan 2012 14:49:56 +0000 (23:49 +0900)
committerChoe Hwanjin <choe.hwanjin@gmail.com>
Thu, 5 Jan 2012 14:49:56 +0000 (23:49 +0900)
This patch implements text conversion feature.
With surrounding text, ibus-hangul can convert text that is
already commited or pasted to hanja string.

src/engine.c

index 7203683..61048e3 100644 (file)
@@ -67,6 +67,12 @@ struct _HanjaKeyList {
     GArray *keys;
 };
 
+enum {
+    LOOKUP_METHOD_EXACT,
+    LOOKUP_METHOD_PREFIX,
+    LOOKUP_METHOD_SUFFIX,
+};
+
 /* functions prototype */
 static void     ibus_hangul_engine_class_init
                                             (IBusHangulEngineClass  *klass);
@@ -127,6 +133,9 @@ static void ibus_hangul_engine_update_preedit_text
 
 static void ibus_hangul_engine_update_lookup_table
                                             (IBusHangulEngine       *hangul);
+static gboolean ibus_hangul_engine_has_preedit
+                                            (IBusHangulEngine       *hangul);
+
 static void ibus_config_value_changed       (IBusConfig             *config,
                                              const gchar            *section,
                                              const gchar            *name,
@@ -155,6 +164,8 @@ static gboolean hanja_key_list_match        (HanjaKeyList           *list,
 static gboolean hanja_key_list_has_modifier (HanjaKeyList           *list,
                                              guint                   keyval);
 
+static glong ucschar_strlen (const ucschar* str);
+
 static IBusEngineClass *parent_class = NULL;
 static HanjaTable *hanja_table = NULL;
 static HanjaTable *symbol_table = NULL;
@@ -164,6 +175,15 @@ static HanjaKeyList hanja_keys;
 static int lookup_table_orientation = 0;
 static IBusKeymap *keymap = NULL;
 
+static glong
+ucschar_strlen (const ucschar* str)
+{
+    const ucschar* p = str;
+    while (*p != 0)
+        p++;
+    return p - str;
+}
+
 GType
 ibus_hangul_engine_get_type (void)
 {
@@ -442,23 +462,42 @@ ibus_hangul_engine_commit_current_candidate (IBusHangulEngine *hangul)
     guint cursor_pos;
     const char* key;
     const char* value;
-    int key_len;
-    int preedit_len;
-    int len;
+    const ucschar* hic_preedit;
+    glong key_len;
+    glong hic_preedit_len;
+    glong preedit_len;
 
     IBusText* text;
 
     cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
     key = hanja_list_get_nth_key (hangul->hanja_list, cursor_pos);
     value = hanja_list_get_nth_value (hangul->hanja_list, cursor_pos);
+    hic_preedit = hangul_ic_get_preedit_string (hangul->context);
 
     key_len = g_utf8_strlen(key, -1);
     preedit_len = ustring_length(hangul->preedit);
+    hic_preedit_len = ucschar_strlen (hic_preedit);
 
-    len = MIN(key_len, preedit_len);
-    ustring_erase (hangul->preedit, 0, len);
-    if (key_len > preedit_len)
+    /* remove hic preedit text */
+    if (hic_preedit_len > 0) {
         hangul_ic_reset (hangul->context);
+        key_len -= hic_preedit_len;
+    }
+
+    /* remove ibus preedit text */
+    if (key_len > preedit_len) {
+        ustring_erase (hangul->preedit, 0, preedit_len);
+        key_len -= preedit_len;
+    } else if (key_len > 0) {
+        ustring_erase (hangul->preedit, 0, key_len);
+        key_len = 0;
+    }
+
+    /* remove surrounding_text */
+    if (key_len > 0) {
+        ibus_engine_delete_surrounding_text ((IBusEngine *)hangul,
+                -key_len , key_len);
+    }
 
     ibus_hangul_engine_update_preedit_text (hangul);
 
@@ -466,12 +505,87 @@ ibus_hangul_engine_commit_current_candidate (IBusHangulEngine *hangul)
     ibus_engine_commit_text ((IBusEngine *)hangul, text);
 }
 
+static gchar*
+h_ibus_text_get_substring (IBusText* ibus_text, glong p1, glong p2)
+{
+    const gchar* text;
+    const gchar* begin;
+    const gchar* end;
+    gchar* substring;
+    glong limit;
+    glong pos;
+    glong n;
+
+    text = ibus_text_get_text (ibus_text);
+    limit = ibus_text_get_length (ibus_text) + 1;
+    if (text == NULL || limit == 0)
+        return NULL;
+
+    p1 = MAX(0, p1);
+    p2 = MAX(0, p2);
+
+    pos = MIN(p1, p2);
+    n = ABS(p2 - p1);
+
+    if (pos + n > limit)
+        n = limit - pos;
+
+    begin = g_utf8_offset_to_pointer (text, pos);
+    end = g_utf8_offset_to_pointer (begin, n);
+
+    substring = g_strndup (begin, end - begin);
+    return substring;
+}
+
+static HanjaList*
+ibus_hangul_engine_lookup_hanja_table (const char* key, int method)
+{
+    HanjaList* list;
+
+    if (key == NULL)
+        return NULL;
+
+    switch (method) {
+    case LOOKUP_METHOD_EXACT:
+        if (symbol_table != NULL)
+            list = hanja_table_match_exact (symbol_table, key);
+
+        if (list == NULL)
+            list = hanja_table_match_exact (hanja_table, key);
+
+        break;
+    case LOOKUP_METHOD_PREFIX:
+        if (symbol_table != NULL)
+            list = hanja_table_match_prefix (symbol_table, key);
+
+        if (list == NULL)
+            list = hanja_table_match_prefix (hanja_table, key);
+
+        break;
+    case LOOKUP_METHOD_SUFFIX:
+        if (symbol_table != NULL)
+            list = hanja_table_match_suffix (symbol_table, key);
+
+        if (list == NULL)
+            list = hanja_table_match_suffix (hanja_table, key);
+
+        break;
+    }
+
+    return list;
+}
+
 static void
 ibus_hangul_engine_update_hanja_list (IBusHangulEngine *hangul)
 {
-    char* utf8;
+    gchar* hanja_key;
+    gchar* preedit_utf8;
     const ucschar* hic_preedit;
     UString* preedit;
+    int lookup_method;
+    IBusText* ibus_text = NULL;
+    guint cursor_pos = 0;
+    guint anchor_pos = 0;
 
     if (hangul->hanja_list != NULL) {
         hanja_list_delete (hangul->hanja_list);
@@ -480,22 +594,59 @@ ibus_hangul_engine_update_hanja_list (IBusHangulEngine *hangul)
 
     hic_preedit = hangul_ic_get_preedit_string (hangul->context);
 
+    hanja_key = NULL;
+    lookup_method = LOOKUP_METHOD_PREFIX;
+
     preedit = ustring_dup (hangul->preedit);
     ustring_append_ucs4 (preedit, hic_preedit, -1);
+
     if (ustring_length(preedit) > 0) {
-        utf8 = ustring_to_utf8 (preedit, -1);
-        if (utf8 != NULL) {
-            if (symbol_table != NULL)
-                hangul->hanja_list = hanja_table_match_prefix (symbol_table, utf8);
-            if (hangul->hanja_list == NULL)
-                hangul->hanja_list = hanja_table_match_prefix (hanja_table, utf8);
-            g_free (utf8);
+        preedit_utf8 = ustring_to_utf8 (preedit, -1);
+        if (hangul->hanja_mode) {
+            hanja_key = preedit_utf8;
+            lookup_method = LOOKUP_METHOD_PREFIX;
+        } else {
+            gchar* substr;
+            ibus_engine_get_surrounding_text ((IBusEngine *)hangul, &ibus_text,
+                    &cursor_pos, &anchor_pos);
+
+            substr = h_ibus_text_get_substring (ibus_text,
+                    cursor_pos - 64, cursor_pos);
+
+            if (substr != NULL) {
+                hanja_key = g_strconcat (substr, preedit_utf8, NULL);
+                g_free (preedit_utf8);
+            } else {
+                hanja_key = preedit_utf8;
+            }
+            lookup_method = LOOKUP_METHOD_SUFFIX;
+        }
+    } else {
+        ibus_engine_get_surrounding_text ((IBusEngine *)hangul, &ibus_text,
+                &cursor_pos, &anchor_pos);
+        if (cursor_pos != anchor_pos) {
+            // If we have selection in surrounding text, we use that.
+            hanja_key = h_ibus_text_get_substring (ibus_text,
+                    cursor_pos, anchor_pos);
+            lookup_method = LOOKUP_METHOD_EXACT;
+        } else {
+            hanja_key = h_ibus_text_get_substring (ibus_text,
+                    cursor_pos - 64, cursor_pos);
+            lookup_method = LOOKUP_METHOD_SUFFIX;
         }
     }
 
+    if (hanja_key != NULL) {
+        hangul->hanja_list = ibus_hangul_engine_lookup_hanja_table (hanja_key,
+                lookup_method);
+        g_free (hanja_key);
+    }
+
     ustring_delete (preedit);
-}
 
+    if (ibus_text != NULL)
+        g_object_unref (ibus_text);
+}
 
 static void
 ibus_hangul_engine_apply_hanja_list (IBusHangulEngine *hangul)
@@ -571,7 +722,7 @@ ibus_hangul_engine_process_candidate_key_event (IBusHangulEngine    *hangul,
     } else if (keyval == IBUS_Return) {
         ibus_hangul_engine_commit_current_candidate (hangul);
 
-        if (hangul->hanja_mode) {
+        if (hangul->hanja_mode && ibus_hangul_engine_has_preedit (hangul)) {
             ibus_hangul_engine_update_lookup_table (hangul);
         } else {
             ibus_hangul_engine_hide_lookup_table (hangul);
@@ -591,7 +742,7 @@ ibus_hangul_engine_process_candidate_key_event (IBusHangulEngine    *hangul,
 
         ibus_hangul_engine_commit_current_candidate (hangul);
 
-        if (hangul->hanja_mode) {
+        if (hangul->hanja_mode && ibus_hangul_engine_has_preedit (hangul)) {
             ibus_hangul_engine_update_lookup_table (hangul);
         } else {
             ibus_hangul_engine_hide_lookup_table (hangul);
@@ -902,6 +1053,8 @@ static void
 ibus_hangul_engine_enable (IBusEngine *engine)
 {
     parent_class->enable (engine);
+
+    ibus_engine_get_surrounding_text (engine, NULL, NULL, NULL);
 }
 
 static void
@@ -978,6 +1131,23 @@ ibus_hangul_engine_property_activate (IBusEngine    *engine,
     }
 }
 
+static gboolean
+ibus_hangul_engine_has_preedit (IBusHangulEngine *hangul)
+{
+    guint preedit_len;
+    const ucschar *hic_preedit;
+
+    hic_preedit = hangul_ic_get_preedit_string (hangul->context);
+    if (hic_preedit[0] != 0)
+        return TRUE;
+
+    preedit_len = ustring_length (hangul->preedit);
+    if (preedit_len > 0)
+        return TRUE;
+
+    return FALSE;
+}
+
 static void
 ibus_config_value_changed (IBusConfig   *config,
                            const gchar  *section,