Implementation of Hanja mode
authorChoe Hwanjin <choe.hwanjin@gmail.com>
Mon, 31 Aug 2009 05:45:02 +0000 (14:45 +0900)
committerChoe Hwanjin <choe.hwanjin@gmail.com>
Mon, 31 Aug 2009 05:45:02 +0000 (14:45 +0900)
 * Add hanja mode property
 * Add ibus level preedit string

src/Makefile.am
src/engine.c
src/ustring.c [new file with mode: 0644]
src/ustring.h [new file with mode: 0644]

index d016424..ba82dbe 100644 (file)
@@ -41,6 +41,8 @@ ibus_engine_hangul_SOURCES = \
        main.c \
        engine.c \
        engine.h \
+       ustring.c \
+       ustring.h \
        $(NULL)
 ibus_engine_hangul_CFLAGS = \
        @IBUS_CFLAGS@ \
index a31ee1e..de1fea9 100644 (file)
@@ -3,7 +3,9 @@
 #include <ibus.h>
 #include <hangul.h>
 #include <string.h>
+
 #include "engine.h"
+#include "ustring.h"
 
 typedef struct _IBusHangulEngine IBusHangulEngine;
 typedef struct _IBusHangulEngineClass IBusHangulEngineClass;
@@ -13,11 +15,14 @@ struct _IBusHangulEngine {
 
     /* members */
     HangulInputContext *context;
+    UString* preedit;
     gboolean hangul_mode;
+    gboolean hanja_mode;
     HanjaList* hanja_list;
 
     IBusLookupTable *table;
     IBusProperty    *hangul_mode_prop;
+    IBusProperty    *prop_hanja_mode;
     IBusPropList    *prop_list;
 };
 
@@ -182,21 +187,37 @@ ibus_hangul_engine_init (IBusHangulEngine *hangul)
     IBusText* tooltip;
 
     hangul->context = hangul_ic_new (hangul_keyboard->str);
+    hangul->preedit = ustring_new();
     hangul->hanja_list = NULL;
     hangul->hangul_mode = TRUE;
-    label = ibus_text_new_from_string("Setup");
-    tooltip = ibus_text_new_from_string("Configure hangul engine");
+    hangul->hanja_mode = FALSE;
+
+    hangul->prop_list = ibus_prop_list_new ();
+
+    label = ibus_text_new_from_static_string ("韓");
+    tooltip = ibus_text_new_from_static_string ("Enable/Disable Hanja mode");
+    prop = ibus_property_new ("hanja_mode",
+                              PROP_TYPE_NORMAL,
+                              label,
+                             NULL,
+                              tooltip,
+                              TRUE, TRUE, PROP_STATE_UNCHECKED, NULL);
+    g_object_unref (label);
+    g_object_unref (tooltip);
+    ibus_prop_list_append (hangul->prop_list, prop);
+    hangul->prop_hanja_mode = prop;
+
+    label = ibus_text_new_from_static_string ("Setup");
+    tooltip = ibus_text_new_from_static_string ("Configure hangul engine");
     prop = ibus_property_new ("setup",
                               PROP_TYPE_NORMAL,
                               label,
                              "gtk-preferences",
                               tooltip,
-                              TRUE, TRUE, 0, NULL);
+                              TRUE, TRUE, PROP_STATE_UNCHECKED, NULL);
     g_object_unref (label);
     g_object_unref (tooltip);
-
-    hangul->prop_list = ibus_prop_list_new ();
-    ibus_prop_list_append (hangul->prop_list,  prop);
+    ibus_prop_list_append (hangul->prop_list, prop);
 
     hangul->table = ibus_lookup_table_new (9, 0, TRUE, FALSE);
 
@@ -248,26 +269,45 @@ ibus_hangul_engine_destroy (IBusHangulEngine *hangul)
 static void
 ibus_hangul_engine_update_preedit_text (IBusHangulEngine *hangul)
 {
-    const gunichar *str;
+    const ucschar *hic_preedit;
     IBusText *text;
-
-    str = hangul_ic_get_preedit_string (hangul->context);
-
-    if (str != NULL && str[0] != 0) {
-        text = ibus_text_new_from_ucs4 (str);
-        ibus_text_append_attribute (text, IBUS_ATTR_TYPE_FOREGROUND, 0x00ffffff, 0, -1);
-        ibus_text_append_attribute (text, IBUS_ATTR_TYPE_BACKGROUND, 0x00000000, 0, -1);
+    UString *preedit;
+    gint preedit_len;
+
+    // ibus-hangul's preedit string is made up of ibus context's
+    // internal preedit string and libhangul's preedit string.
+    // libhangul only supports one syllable preedit string.
+    // In order to make longer preedit string, ibus-hangul maintains
+    // internal preedit string.
+    hic_preedit = hangul_ic_get_preedit_string (hangul->context);
+
+    preedit = ustring_dup (hangul->preedit);
+    preedit_len = ustring_length(preedit);
+    ustring_append_ucs4 (preedit, hic_preedit, -1);
+
+    if (ustring_length(preedit) > 0) {
+        text = ibus_text_new_from_ucs4 ((gunichar*)preedit->data);
+       // ibus-hangul's internal preedit string
+        ibus_text_append_attribute (text, IBUS_ATTR_TYPE_UNDERLINE,
+               IBUS_ATTR_UNDERLINE_SINGLE, 0, preedit_len);
+       // Preedit string from libhangul context.
+       // This is currently composing syllable.
+        ibus_text_append_attribute (text, IBUS_ATTR_TYPE_FOREGROUND,
+               0x00ffffff, preedit_len, -1);
+       ibus_text_append_attribute (text, IBUS_ATTR_TYPE_BACKGROUND,
+               0x00000000, preedit_len, -1);
         ibus_engine_update_preedit_text ((IBusEngine *)hangul,
                                          text,
                                          ibus_text_get_length (text),
                                          TRUE);
         g_object_unref (text);
-    }
-    else {
+    } else {
         text = ibus_text_new_from_static_string ("");
         ibus_engine_update_preedit_text ((IBusEngine *)hangul, text, 0, FALSE);
         g_object_unref (text);
     }
+
+    ustring_delete(preedit);
 }
 
 static void
@@ -295,15 +335,28 @@ static void
 ibus_hangul_engine_commit_current_candidate (IBusHangulEngine *hangul)
 {
     guint cursor_pos;
+    const char* key;
     const char* value;
-    IBusText* text;
+    int key_len;
+    int preedit_len;
+    int len;
 
-    hangul_ic_reset (hangul->context);
-    ibus_hangul_engine_update_preedit_text (hangul);
+    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);
 
+    key_len = g_utf8_strlen(key, -1);
+    preedit_len = ustring_length(hangul->preedit);
+
+    len = MIN(key_len, preedit_len);
+    ustring_erase (hangul->preedit, 0, len);
+    if (key_len > preedit_len)
+       hangul_ic_reset (hangul->context);
+
+    ibus_hangul_engine_update_preedit_text (hangul);
+
     text = ibus_text_new_from_string (value);
     ibus_engine_commit_text ((IBusEngine *)hangul, text);
     g_object_unref (text);
@@ -313,12 +366,17 @@ static void
 ibus_hangul_engine_open_lookup_table (IBusHangulEngine *hangul)
 {
     char* utf8;
-    const gunichar* str;
+    const ucschar* hic_preedit;
+    UString* preedit;
+
+    hic_preedit = hangul_ic_get_preedit_string (hangul->context);
 
-    str = hangul_ic_get_preedit_string (hangul->context);
-    utf8 = g_ucs4_to_utf8 (str, -1, NULL, NULL, NULL);
+    preedit = ustring_dup (hangul->preedit);
+    ustring_append_ucs4 (preedit, hic_preedit, -1);
+
+    utf8 = ustring_to_utf8 (preedit, -1);
     if (utf8 != NULL) {
-        HanjaList* list = hanja_table_match_suffix (hanja_table, utf8);
+        HanjaList* list = hanja_table_match_prefix (hanja_table, utf8);
         if (list != NULL) {
             int i, n;
             n = hanja_list_get_size (list);
@@ -340,6 +398,8 @@ ibus_hangul_engine_open_lookup_table (IBusHangulEngine *hangul)
         }
         g_free (utf8);
     }
+
+    ustring_delete (preedit);
 }
 
 static void
@@ -368,9 +428,11 @@ ibus_hangul_engine_process_candidate_key_event (IBusHangulEngine    *hangul,
 {
     if (keyval == IBUS_Escape) {
         ibus_hangul_engine_close_lookup_table (hangul);
+       return TRUE;
     } else if (keyval == IBUS_Return) {
        ibus_hangul_engine_commit_current_candidate (hangul);
         ibus_hangul_engine_close_lookup_table (hangul);
+       return TRUE;
     } else if (keyval >= IBUS_1 && keyval <= IBUS_9) {
        guint page_no;
        guint page_size;
@@ -385,49 +447,64 @@ ibus_hangul_engine_process_candidate_key_event (IBusHangulEngine    *hangul,
 
        ibus_hangul_engine_commit_current_candidate (hangul);
         ibus_hangul_engine_close_lookup_table (hangul);
+       return TRUE;
     } else if (keyval == IBUS_Left) {
         ibus_lookup_table_cursor_up (hangul->table);
         ibus_hangul_engine_update_lookup_table (hangul);
         ibus_hangul_engine_update_auxiliary_text (hangul);
+       return TRUE;
     } else if (keyval == IBUS_Right) {
         ibus_lookup_table_cursor_down (hangul->table);
         ibus_hangul_engine_update_lookup_table (hangul);
         ibus_hangul_engine_update_auxiliary_text (hangul);
+       return TRUE;
     } else if (keyval == IBUS_Up) {
         ibus_lookup_table_page_up (hangul->table);
         ibus_hangul_engine_update_lookup_table (hangul);
         ibus_hangul_engine_update_auxiliary_text (hangul);
+       return TRUE;
     } else if (keyval == IBUS_Down) {
         ibus_lookup_table_page_down (hangul->table);
         ibus_hangul_engine_update_lookup_table (hangul);
         ibus_hangul_engine_update_auxiliary_text (hangul);
+       return TRUE;
     } else if (keyval == IBUS_Page_Up) {
         ibus_lookup_table_page_up (hangul->table);
         ibus_hangul_engine_update_lookup_table (hangul);
         ibus_hangul_engine_update_auxiliary_text (hangul);
+       return TRUE;
     } else if (keyval == IBUS_Page_Down) {
         ibus_lookup_table_page_down (hangul->table);
         ibus_hangul_engine_update_lookup_table (hangul);
         ibus_hangul_engine_update_auxiliary_text (hangul);
-    } else if (keyval == IBUS_h) {
-        ibus_lookup_table_cursor_up (hangul->table);
-        ibus_hangul_engine_update_lookup_table (hangul);
-        ibus_hangul_engine_update_auxiliary_text (hangul);
-    } else if (keyval == IBUS_l) {
-        ibus_lookup_table_cursor_down (hangul->table);
-        ibus_hangul_engine_update_lookup_table (hangul);
-        ibus_hangul_engine_update_auxiliary_text (hangul);
-    } else if (keyval == IBUS_k) {
-        ibus_lookup_table_page_up (hangul->table);
-        ibus_hangul_engine_update_lookup_table (hangul);
-        ibus_hangul_engine_update_auxiliary_text (hangul);
-    } else if (keyval == IBUS_j) {
-        ibus_lookup_table_page_down (hangul->table);
-        ibus_hangul_engine_update_lookup_table (hangul);
-        ibus_hangul_engine_update_auxiliary_text (hangul);
+       return TRUE;
+    }
+
+    if (!hangul->hanja_mode) {
+       if (keyval == IBUS_h) {
+           ibus_lookup_table_cursor_up (hangul->table);
+           ibus_hangul_engine_update_lookup_table (hangul);
+           ibus_hangul_engine_update_auxiliary_text (hangul);
+           return TRUE;
+       } else if (keyval == IBUS_l) {
+           ibus_lookup_table_cursor_down (hangul->table);
+           ibus_hangul_engine_update_lookup_table (hangul);
+           ibus_hangul_engine_update_auxiliary_text (hangul);
+           return TRUE;
+       } else if (keyval == IBUS_k) {
+           ibus_lookup_table_page_up (hangul->table);
+           ibus_hangul_engine_update_lookup_table (hangul);
+           ibus_hangul_engine_update_auxiliary_text (hangul);
+           return TRUE;
+       } else if (keyval == IBUS_j) {
+           ibus_lookup_table_page_down (hangul->table);
+           ibus_hangul_engine_update_lookup_table (hangul);
+           ibus_hangul_engine_update_auxiliary_text (hangul);
+           return TRUE;
+       }
     }
 
-    return TRUE;
+    return FALSE;
 }
 
 static gboolean
@@ -439,7 +516,7 @@ ibus_hangul_engine_process_key_event (IBusEngine     *engine,
     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
 
     gboolean retval;
-    const gunichar *str;
+    const ucschar *str;
 
     if (modifiers & IBUS_RELEASE_MASK)
         return FALSE;
@@ -461,8 +538,14 @@ ibus_hangul_engine_process_key_event (IBusEngine     *engine,
         return FALSE;
 
     if (hangul->hanja_list != NULL) {
-        return ibus_hangul_engine_process_candidate_key_event (hangul,
-                     keyval, modifiers);
+       retval = ibus_hangul_engine_process_candidate_key_event (hangul,
+                    keyval, modifiers);
+       if (hangul->hanja_mode) {
+           if (retval)
+               return TRUE;
+       } else {
+           return TRUE;
+       }
     }
 
     if (keyval == IBUS_BackSpace) {
@@ -472,14 +555,44 @@ ibus_hangul_engine_process_key_event (IBusEngine     *engine,
     }
 
     str = hangul_ic_get_commit_string (hangul->context);
-    if (str != NULL && str[0] != 0) {
-        IBusText *text = ibus_text_new_from_ucs4 (str);
-        ibus_engine_commit_text ((IBusEngine *)hangul, text);
-        g_object_unref (text);
+    if (hangul->hanja_mode) {
+       const ucschar* hic_preedit;
+
+       hic_preedit = hangul_ic_get_preedit_string (hangul->context);
+       if (hic_preedit != NULL && hic_preedit[0] != 0) {
+           ustring_append_ucs4 (hangul->preedit, str, -1);
+       } else {
+           IBusText *text;
+           const ucschar* preedit;
+
+           ustring_append_ucs4 (hangul->preedit, str, -1);
+
+           preedit = ustring_begin (hangul->preedit);
+           text = ibus_text_new_from_ucs4 ((gunichar*)preedit);
+           ibus_engine_commit_text (engine, text);
+           g_object_unref (text);
+           ustring_clear (hangul->preedit);
+       }
+    } else {
+       if (str != NULL && str[0] != 0) {
+           IBusText *text = ibus_text_new_from_ucs4 (str);
+           ibus_engine_commit_text (engine, text);
+           g_object_unref (text);
+       }
     }
 
     ibus_hangul_engine_update_preedit_text (hangul);
 
+    if (hangul->hanja_mode) {
+       hanja_list_delete (hangul->hanja_list);
+       hangul->hanja_list = NULL;
+
+       ibus_hangul_engine_open_lookup_table (hangul);
+
+       if (hangul->hanja_list == NULL)
+           ibus_hangul_engine_close_lookup_table (hangul);
+    }
+
     if (!retval)
         ibus_hangul_engine_flush (hangul);
 
@@ -510,6 +623,15 @@ ibus_hangul_engine_focus_in (IBusEngine *engine)
 {
     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
 
+    IBusText* label = NULL;
+    if (hangul->hanja_mode) {
+       label = ibus_text_new_from_static_string("漢");
+    } else {
+       label = ibus_text_new_from_static_string("韓");
+    }
+    ibus_property_set_label (hangul->prop_hanja_mode, label);
+    g_object_unref (label);
+
     ibus_engine_register_properties (engine, hangul->prop_list);
 
     if (hangul->hanja_list != NULL) {
@@ -621,6 +743,23 @@ ibus_hangul_engine_property_activate (IBusEngine    *engine,
         g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, NULL, &error);
 
        g_free(path);
+    } else if (strcmp(prop_name, "hanja_mode") == 0) {
+       IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
+       IBusText *label;
+
+       hangul->hanja_mode = !hangul->hanja_mode;
+
+       if (hangul->hanja_mode) {
+           label = ibus_text_new_from_static_string("漢");
+           hangul->prop_hanja_mode->state = PROP_STATE_CHECKED;
+       } else {
+           label = ibus_text_new_from_static_string("韓");
+           hangul->prop_hanja_mode->state = PROP_STATE_UNCHECKED;
+       }
+
+       ibus_property_set_label (hangul->prop_hanja_mode, label);
+       ibus_engine_update_property (engine, hangul->prop_hanja_mode);
+       g_object_unref(label);
     }
 }
 
diff --git a/src/ustring.c b/src/ustring.c
new file mode 100644 (file)
index 0000000..eb80811
--- /dev/null
@@ -0,0 +1,113 @@
+/* ibus-hangul - korean input method engine for IBus
+ * This file is from Korean XIM Nabi.
+ */
+
+/* Nabi - X Input Method server for hangul
+ * Copyright (C) 2008 Choe Hwanjin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "ustring.h"
+
+UString*
+ustring_new()
+{
+    return g_array_new(TRUE, TRUE, sizeof(ucschar));
+}
+
+UString*
+ustring_dup(const UString* str)
+{
+    UString* dup;
+    dup = ustring_new();
+    ustring_append(dup, str);
+    return dup;
+}
+
+void
+ustring_delete(UString* str)
+{
+    g_array_free(str, TRUE);
+}
+
+void
+ustring_clear(UString* str)
+{
+    if (str->len > 0)
+       g_array_remove_range(str, 0, str->len);
+}
+
+UString*
+ustring_erase(UString* str, guint pos, guint len)
+{
+    return g_array_remove_range(str, pos, len);
+}
+
+ucschar*
+ustring_begin(UString* str)
+{
+    return (ucschar*)str->data;
+}
+
+ucschar*
+ustring_end(UString* str)
+{
+    return &g_array_index(str, ucschar, str->len);
+}
+
+guint
+ustring_length(const UString* str)
+{
+    return str->len;
+}
+
+UString*
+ustring_append(UString* str, const UString* s)
+{
+    return g_array_append_vals(str, s->data, s->len);
+}
+
+UString*
+ustring_append_ucs4(UString* str, const ucschar* s, gint len)
+{
+    if (len < 0) {
+       const ucschar*p = s;
+       while (*p != 0)
+           p++;
+       len = p - s;
+    }
+
+    return g_array_append_vals(str, s, len);
+}
+
+UString*
+ustring_append_utf8(UString* str, const char* utf8)
+{
+    while (*utf8 != '\0') {
+       ucschar c = g_utf8_get_char(utf8);
+       g_array_append_vals(str, &c, 1);
+       utf8 = g_utf8_next_char(utf8);
+    }
+    return str;
+}
+
+gchar*
+ustring_to_utf8(const UString* str, guint len)
+{
+    if (len < 0)
+       len = str->len;
+    return g_ucs4_to_utf8((const gunichar*)str->data, len, NULL, NULL, NULL);
+}
diff --git a/src/ustring.h b/src/ustring.h
new file mode 100644 (file)
index 0000000..f3cb3a4
--- /dev/null
@@ -0,0 +1,48 @@
+/* ibus-hangul - korean input method engine for IBus
+ * This file is from Korean XIM Nabi.
+ */
+
+/* Nabi - X Input Method server for hangul
+ * Copyright (C) 2008 Choe Hwanjin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef nabi_ustring_h
+#define nabi_ustring_h
+
+#include <glib.h>
+#include <hangul.h>
+
+typedef GArray UString;
+
+UString* ustring_new();
+UString* ustring_dup(const UString* str);
+void     ustring_delete(UString* str);
+
+void     ustring_clear(UString* str);
+UString* ustring_erase(UString* str, guint pos, guint len);
+
+ucschar* ustring_begin(UString* str);
+ucschar* ustring_end(UString* str);
+guint    ustring_length(const UString* str);
+
+UString* ustring_append(UString* str, const UString* s);
+UString* ustring_append_ucs4(UString* str, const ucschar* s, gint len);
+UString* ustring_append_utf8(UString* str, const char* utf8);
+
+gchar*   ustring_to_utf8(const UString* str, guint len);
+
+#endif // nabi_ustring_h