Install icons to hicolor icon theme directory
[platform/upstream/ibus-hangul.git] / src / engine.c
index 6e8c049..602f073 100644 (file)
@@ -1,4 +1,23 @@
 /* vim:set et sts=4: */
+/* ibus-hangul - The Hangul Engine For IBus
+ * Copyright (C) 2008-2009 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright (C) 2009-2011 Choe Hwanjin <choe.hwanjin@gmail.com>
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 typedef struct _IBusHangulEngine IBusHangulEngine;
 typedef struct _IBusHangulEngineClass IBusHangulEngineClass;
 
+typedef struct _HanjaKeyList HanjaKeyList;
+
 struct _IBusHangulEngine {
-       IBusEngine parent;
+    IBusEngine parent;
 
     /* members */
     HangulInputContext *context;
@@ -25,6 +46,7 @@ struct _IBusHangulEngine {
     gboolean hangul_mode;
     gboolean hanja_mode;
     HanjaList* hanja_list;
+    int last_lookup_method;
 
     IBusLookupTable *table;
 
@@ -33,7 +55,7 @@ struct _IBusHangulEngine {
 };
 
 struct _IBusHangulEngineClass {
-       IBusEngineClass parent;
+    IBusEngineClass parent;
 };
 
 struct KeyEvent {
@@ -41,16 +63,29 @@ struct KeyEvent {
     guint modifiers;
 };
 
+struct _HanjaKeyList {
+    guint   all_modifiers;
+    GArray *keys;
+};
+
+enum {
+    LOOKUP_METHOD_EXACT,
+    LOOKUP_METHOD_PREFIX,
+    LOOKUP_METHOD_SUFFIX,
+};
+
 /* functions prototype */
-static void    ibus_hangul_engine_class_init   (IBusHangulEngineClass  *klass);
-static void    ibus_hangul_engine_init             (IBusHangulEngine           *hangul);
+static void     ibus_hangul_engine_class_init
+                                            (IBusHangulEngineClass  *klass);
+static void     ibus_hangul_engine_init     (IBusHangulEngine       *hangul);
 static GObject*
-            ibus_hangul_engine_constructor  (GType                   type,
+                ibus_hangul_engine_constructor
+                                            (GType                   type,
                                              guint                   n_construct_params,
                                              GObjectConstructParam  *construct_params);
-static void    ibus_hangul_engine_destroy              (IBusHangulEngine               *hangul);
+static void     ibus_hangul_engine_destroy  (IBusHangulEngine       *hangul);
 static gboolean
-                       ibus_hangul_engine_process_key_event
+                ibus_hangul_engine_process_key_event
                                             (IBusEngine             *engine,
                                              guint                   keyval,
                                              guint                   keycode,
@@ -80,112 +115,173 @@ static void ibus_hangul_engine_property_activate
                                              guint                   prop_state);
 #if 0
 static void ibus_hangul_engine_property_show
-                                                                                       (IBusEngine             *engine,
+                                                                                        (IBusEngine             *engine,
                                              const gchar            *prop_name);
 static void ibus_hangul_engine_property_hide
-                                                                                       (IBusEngine             *engine,
+                                                                                        (IBusEngine             *engine,
                                              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_clear_preedit_text
+                                            (IBusHangulEngine       *hangul);
 static void ibus_hangul_engine_update_preedit_text
                                             (IBusHangulEngine       *hangul);
 
 static void ibus_hangul_engine_update_lookup_table
-                                           (IBusHangulEngine       *hangul);
+                                            (IBusHangulEngine       *hangul);
+static gboolean ibus_hangul_engine_has_preedit
+                                            (IBusHangulEngine       *hangul);
+static bool ibus_hangul_engine_on_transition
+                                            (HangulInputContext     *hic,
+                                             ucschar                 c,
+                                             const ucschar          *preedit,
+                                             void                   *data);
+
 static void ibus_config_value_changed       (IBusConfig             *config,
                                              const gchar            *section,
                                              const gchar            *name,
-                                             GValue                 *value,
+                                             GVariant               *value,
                                              gpointer                user_data);
 
-static void    lookup_table_set_visible    (IBusLookupTable *table,
-                                            gboolean flag);
-static gboolean        lookup_table_is_visible     (IBusLookupTable *table);
+static void        lookup_table_set_visible (IBusLookupTable        *table,
+                                             gboolean                flag);
+static gboolean        lookup_table_is_visible
+                                            (IBusLookupTable        *table);
+
+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 void     key_event_list_set          (GArray* list, const char* str);
-static gboolean key_event_list_match        (GArray* list,
-                                             guint keyval,
-                                             guint modifiers);
+static glong ucschar_strlen (const ucschar* str);
 
 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;
+static IBusKeymap *keymap = NULL;
+static gboolean word_commit = FALSE;
+static gboolean auto_reorder = TRUE;
+
+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)
 {
-       static GType type = 0;
-
-       static const GTypeInfo type_info = {
-               sizeof (IBusHangulEngineClass),
-               (GBaseInitFunc)         NULL,
-               (GBaseFinalizeFunc) NULL,
-               (GClassInitFunc)        ibus_hangul_engine_class_init,
-               NULL,
-               NULL,
-               sizeof (IBusHangulEngine),
-               0,
-               (GInstanceInitFunc)     ibus_hangul_engine_init,
-       };
-
-       if (type == 0) {
-               type = g_type_register_static (IBUS_TYPE_ENGINE,
-                                                                          "IBusHangulEngine",
-                                                                          &type_info,
-                                                                          (GTypeFlags) 0);
-       }
+    static GType type = 0;
+
+    static const GTypeInfo type_info = {
+        sizeof (IBusHangulEngineClass),
+        (GBaseInitFunc)     NULL,
+        (GBaseFinalizeFunc) NULL,
+        (GClassInitFunc)    ibus_hangul_engine_class_init,
+        NULL,
+        NULL,
+        sizeof (IBusHangulEngine),
+        0,
+        (GInstanceInitFunc) ibus_hangul_engine_init,
+    };
+
+    if (type == 0) {
+            type = g_type_register_static (IBUS_TYPE_ENGINE,
+                                           "IBusHangulEngine",
+                                           &type_info,
+                                           (GTypeFlags) 0);
+    }
 
-       return type;
+    return type;
 }
 
 void
 ibus_hangul_init (IBusBus *bus)
 {
-    gboolean res;
-    GValue value = { 0, };
+    GVariant* value;
 
     hanja_table = hanja_table_load (NULL);
 
     symbol_table = hanja_table_load (IBUSHANGUL_DATADIR "/data/symbol.txt");
 
     config = ibus_bus_get_config (bus);
+    if (config)
+        g_object_ref_sink (config);
 
     hangul_keyboard = g_string_new_len ("2", 8);
-    res = ibus_config_get_value (config, "engine/Hangul",
-                                         "HangulKeyboard", &value);
-    if (res) {
-        const gchar* str = g_value_get_string (&value);
+    value = ibus_config_get_value (config, "engine/Hangul",
+                                         "HangulKeyboard");
+    if (value != NULL) {
+        const gchar* str = g_variant_get_string (value, NULL);
         g_string_assign (hangul_keyboard, str);
-       g_value_unset(&value);
+        g_variant_unref(value);
     }
 
-    hanja_keys = g_array_sized_new(FALSE, TRUE, sizeof(struct KeyEvent), 4);
-    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);
-       g_value_unset(&value);
+    hanja_key_list_init(&hanja_keys);
+
+    value = ibus_config_get_value (config, "engine/Hangul",
+                                         "HanjaKeys");
+    if (value != NULL) {
+        const gchar* str = g_variant_get_string (value, NULL);
+        hanja_key_list_set_from_string(&hanja_keys, str);
+        g_variant_unref(value);
     } else {
-       struct KeyEvent ev;
+       hanja_key_list_append(&hanja_keys, IBUS_Hangul_Hanja, 0);
+       hanja_key_list_append(&hanja_keys, IBUS_F9, 0);
+    }
 
-       ev.keyval = IBUS_Hangul_Hanja;
-       ev.modifiers = 0;
-       g_array_append_val(hanja_keys, ev);
+    value = ibus_config_get_value (config, "engine/Hangul",
+                                         "WordCommit");
+    if (value != NULL) {
+        word_commit = g_variant_get_boolean (value);
+        g_variant_unref(value);
+    }
 
-       ev.keyval = IBUS_F9;
-       ev.modifiers = 0;
-       g_array_append_val(hanja_keys, ev);
+    value = ibus_config_get_value (config, "engine/Hangul", "AutoReorder");
+    if (value != NULL) {
+        auto_reorder = g_variant_get_boolean (value);
+        g_variant_unref (value);
     }
+
+    keymap = ibus_keymap_get("us");
 }
 
 void
 ibus_hangul_exit (void)
 {
+    if (keymap != NULL) {
+       g_object_unref(keymap);
+       keymap = NULL;
+    }
+
+    hanja_key_list_fini(&hanja_keys);
+
     hanja_table_delete (hanja_table);
     hanja_table = NULL;
 
@@ -203,13 +299,13 @@ static void
 ibus_hangul_engine_class_init (IBusHangulEngineClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
-       IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
-       IBusEngineClass *engine_class = IBUS_ENGINE_CLASS (klass);
+    IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
+    IBusEngineClass *engine_class = IBUS_ENGINE_CLASS (klass);
 
-       parent_class = (IBusEngineClass *) g_type_class_peek_parent (klass);
+    parent_class = (IBusEngineClass *) g_type_class_peek_parent (klass);
 
     object_class->constructor = ibus_hangul_engine_constructor;
-       ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_hangul_engine_destroy;
+    ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_hangul_engine_destroy;
 
     engine_class->process_key_event = ibus_hangul_engine_process_key_event;
 
@@ -227,6 +323,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
@@ -237,23 +335,27 @@ ibus_hangul_engine_init (IBusHangulEngine *hangul)
     IBusText* tooltip;
 
     hangul->context = hangul_ic_new (hangul_keyboard->str);
+    hangul_ic_connect_callback (hangul->context, "transition",
+                                ibus_hangul_engine_on_transition, hangul);
+
     hangul->preedit = ustring_new();
     hangul->hanja_list = NULL;
     hangul->hangul_mode = TRUE;
     hangul->hanja_mode = FALSE;
+    hangul->last_lookup_method = LOOKUP_METHOD_PREFIX;
 
     hangul->prop_list = ibus_prop_list_new ();
+    g_object_ref_sink (hangul->prop_list);
 
     label = ibus_text_new_from_string (_("Hanja lock"));
     tooltip = ibus_text_new_from_string (_("Enable/Disable Hanja mode"));
     prop = ibus_property_new ("hanja_mode",
                               PROP_TYPE_TOGGLE,
                               label,
-                             NULL,
+                              NULL,
                               tooltip,
                               TRUE, TRUE, PROP_STATE_UNCHECKED, NULL);
-    g_object_unref (label);
-    g_object_unref (tooltip);
+    g_object_ref_sink (prop);
     ibus_prop_list_append (hangul->prop_list, prop);
     hangul->prop_hanja_mode = prop;
 
@@ -262,18 +364,16 @@ ibus_hangul_engine_init (IBusHangulEngine *hangul)
     prop = ibus_property_new ("setup",
                               PROP_TYPE_NORMAL,
                               label,
-                             "gtk-preferences",
+                              "gtk-preferences",
                               tooltip,
                               TRUE, TRUE, PROP_STATE_UNCHECKED, NULL);
-    g_object_unref (label);
-    g_object_unref (tooltip);
     ibus_prop_list_append (hangul->prop_list, prop);
-    g_object_unref (prop);
 
     hangul->table = ibus_lookup_table_new (9, 0, TRUE, FALSE);
+    g_object_ref_sink (hangul->table);
 
     g_signal_connect (config, "value-changed",
-                     G_CALLBACK(ibus_config_value_changed), hangul);
+                      G_CALLBACK(ibus_config_value_changed), hangul);
 }
 
 static GObject*
@@ -314,7 +414,16 @@ ibus_hangul_engine_destroy (IBusHangulEngine *hangul)
         hangul->context = NULL;
     }
 
-       IBUS_OBJECT_CLASS (parent_class)->destroy ((IBusObject *)hangul);
+    IBUS_OBJECT_CLASS (parent_class)->destroy ((IBusObject *)hangul);
+}
+
+static void
+ibus_hangul_engine_clear_preedit_text (IBusHangulEngine *hangul)
+{
+    IBusText *text;
+
+    text = ibus_text_new_from_static_string ("");
+    ibus_engine_update_preedit_text ((IBusEngine *)hangul, text, 0, FALSE);
 }
 
 static void
@@ -337,25 +446,29 @@ 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-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_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);
+                0x00ffffff, preedit_len, -1);
+        ibus_text_append_attribute (text, IBUS_ATTR_TYPE_BACKGROUND,
+                0x00000000, preedit_len, -1);
+        ibus_engine_update_preedit_text_with_mode ((IBusEngine *)hangul,
+                                                   text,
+                                                   ibus_text_get_length (text),
+                                                   TRUE,
+                                                   preedit_option);
     } 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);
@@ -374,7 +487,6 @@ ibus_hangul_engine_update_lookup_table_ui (IBusHangulEngine *hangul)
 
     text = ibus_text_new_from_string (comment);
     ibus_engine_update_auxiliary_text ((IBusEngine *)hangul, text, TRUE);
-    g_object_unref (text);
 
     // update lookup table
     ibus_engine_update_lookup_table ((IBusEngine *)hangul, hangul->table, TRUE);
@@ -386,81 +498,237 @@ 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);
+
+    if (hangul->last_lookup_method == LOOKUP_METHOD_PREFIX) {
+        if (preedit_len == 0 && hic_preedit_len == 0) {
+            /* remove surrounding_text */
+            if (key_len > 0) {
+                ibus_engine_delete_surrounding_text ((IBusEngine *)hangul,
+                        -key_len , key_len);
+            }
+        } else {
+            /* remove ibus preedit text */
+            if (key_len > 0) {
+                glong n = MIN(key_len, preedit_len);
+                ustring_erase (hangul->preedit, 0, n);
+                key_len -= preedit_len;
+            }
+
+            /* remove hic preedit text */
+            if (key_len > 0) {
+                hangul_ic_reset (hangul->context);
+                key_len -= hic_preedit_len;
+            }
+        }
+    } else {
+        /* 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);
+        }
+    }
 
-    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);
+    /* clear preedit text before commit */
+    ibus_hangul_engine_clear_preedit_text (hangul);
 
     text = ibus_text_new_from_string (value);
     ibus_engine_commit_text ((IBusEngine *)hangul, text);
-    g_object_unref (text);
+
+    ibus_hangul_engine_update_preedit_text (hangul);
+}
+
+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);
-       hangul->hanja_list = NULL;
+        hanja_list_delete (hangul->hanja_list);
+        hangul->hanja_list = NULL;
     }
 
     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 (word_commit || 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);
+        hangul->last_lookup_method = 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)
 {
     HanjaList* list = hangul->hanja_list;
     if (list != NULL) {
-       int i, n;
-       n = hanja_list_get_size (list);
-
-       ibus_lookup_table_clear (hangul->table);
-       for (i = 0; i < n; i++) {
-           const char* value = hanja_list_get_nth_value (list, i);
-           IBusText* text = ibus_text_new_from_string (value);
-           ibus_lookup_table_append_candidate (hangul->table, text);
-           g_object_unref (text);
-       }
+        int i, n;
+        n = hanja_list_get_size (list);
 
-       ibus_lookup_table_set_cursor_pos (hangul->table, 0);
-       ibus_hangul_engine_update_lookup_table_ui (hangul);
-       lookup_table_set_visible (hangul->table, TRUE);
+        ibus_lookup_table_clear (hangul->table);
+        for (i = 0; i < n; i++) {
+            const char* value = hanja_list_get_nth_value (list, i);
+            IBusText* text = ibus_text_new_from_string (value);
+            ibus_lookup_table_append_candidate (hangul->table, text);
+        }
+
+        ibus_lookup_table_set_cursor_pos (hangul->table, 0);
+        ibus_hangul_engine_update_lookup_table_ui (hangul);
+        lookup_table_set_visible (hangul->table, TRUE);
     }
 }
 
@@ -474,14 +742,14 @@ ibus_hangul_engine_hide_lookup_table (IBusHangulEngine *hangul)
     // is not visible results wrong behavior. So I have to check
     // whether the table is visible or not before to hide.
     if (is_visible) {
-       ibus_engine_hide_lookup_table ((IBusEngine *)hangul);
-       ibus_engine_hide_auxiliary_text ((IBusEngine *)hangul);
-       lookup_table_set_visible (hangul->table, FALSE);
+        ibus_engine_hide_lookup_table ((IBusEngine *)hangul);
+        ibus_engine_hide_auxiliary_text ((IBusEngine *)hangul);
+        lookup_table_set_visible (hangul->table, FALSE);
     }
 
     if (hangul->hanja_list != NULL) {
-       hanja_list_delete (hangul->hanja_list);
-       hangul->hanja_list = NULL;
+        hanja_list_delete (hangul->hanja_list);
+        hangul->hanja_list = NULL;
     }
 }
 
@@ -491,6 +759,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);
@@ -504,80 +775,132 @@ ibus_hangul_engine_process_candidate_key_event (IBusHangulEngine    *hangul,
 {
     if (keyval == IBUS_Escape) {
         ibus_hangul_engine_hide_lookup_table (hangul);
-       return TRUE;
+       // 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);
-
-       if (hangul->hanja_mode) {
-           ibus_hangul_engine_update_lookup_table (hangul);
-       } else {
-           ibus_hangul_engine_hide_lookup_table (hangul);
-       }
-       return TRUE;
+        ibus_hangul_engine_commit_current_candidate (hangul);
+
+        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);
+        }
+        return TRUE;
     } else if (keyval >= IBUS_1 && keyval <= IBUS_9) {
-       guint page_no;
-       guint page_size;
-       guint cursor_pos;
+        guint page_no;
+        guint page_size;
+        guint cursor_pos;
 
-       page_size = ibus_lookup_table_get_page_size (hangul->table);
-       cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
-       page_no = cursor_pos / page_size;
+        page_size = ibus_lookup_table_get_page_size (hangul->table);
+        cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
+        page_no = cursor_pos / page_size;
 
-       cursor_pos = page_no * page_size + (keyval - IBUS_1);
-       ibus_lookup_table_set_cursor_pos (hangul->table, cursor_pos);
+        cursor_pos = page_no * page_size + (keyval - IBUS_1);
+        ibus_lookup_table_set_cursor_pos (hangul->table, cursor_pos);
 
-       ibus_hangul_engine_commit_current_candidate (hangul);
+        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);
-       }
-       return TRUE;
-    } else if (keyval == IBUS_Left) {
-        ibus_lookup_table_cursor_up (hangul->table);
-        ibus_hangul_engine_update_lookup_table_ui (hangul);
-       return TRUE;
-    } else if (keyval == IBUS_Right) {
-        ibus_lookup_table_cursor_down (hangul->table);
-        ibus_hangul_engine_update_lookup_table_ui (hangul);
-       return TRUE;
-    } else if (keyval == IBUS_Up) {
-        ibus_lookup_table_page_up (hangul->table);
-        ibus_hangul_engine_update_lookup_table_ui (hangul);
-       return TRUE;
-    } else if (keyval == IBUS_Down) {
-        ibus_lookup_table_page_down (hangul->table);
-        ibus_hangul_engine_update_lookup_table_ui (hangul);
-       return TRUE;
+        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);
+        }
+        return TRUE;
     } else if (keyval == IBUS_Page_Up) {
         ibus_lookup_table_page_up (hangul->table);
         ibus_hangul_engine_update_lookup_table_ui (hangul);
-       return TRUE;
+        return TRUE;
     } else if (keyval == IBUS_Page_Down) {
         ibus_lookup_table_page_down (hangul->table);
         ibus_hangul_engine_update_lookup_table_ui (hangul);
-       return TRUE;
+        return TRUE;
+    } else {
+        if (lookup_table_orientation == 0) {
+            // horizontal
+            if (keyval == IBUS_Left) {
+                ibus_lookup_table_cursor_up (hangul->table);
+                ibus_hangul_engine_update_lookup_table_ui (hangul);
+                return TRUE;
+            } else if (keyval == IBUS_Right) {
+                ibus_lookup_table_cursor_down (hangul->table);
+                ibus_hangul_engine_update_lookup_table_ui (hangul);
+                return TRUE;
+            } else if (keyval == IBUS_Up) {
+                ibus_lookup_table_page_up (hangul->table);
+                ibus_hangul_engine_update_lookup_table_ui (hangul);
+                return TRUE;
+            } else if (keyval == IBUS_Down) {
+                ibus_lookup_table_page_down (hangul->table);
+                ibus_hangul_engine_update_lookup_table_ui (hangul);
+                return TRUE;
+            }
+        } else {
+            // vertical
+            if (keyval == IBUS_Left) {
+                ibus_lookup_table_page_up (hangul->table);
+                ibus_hangul_engine_update_lookup_table_ui (hangul);
+                return TRUE;
+            } else if (keyval == IBUS_Right) {
+                ibus_lookup_table_page_down (hangul->table);
+                ibus_hangul_engine_update_lookup_table_ui (hangul);
+                return TRUE;
+            } else if (keyval == IBUS_Up) {
+                ibus_lookup_table_cursor_up (hangul->table);
+                ibus_hangul_engine_update_lookup_table_ui (hangul);
+                return TRUE;
+            } else if (keyval == IBUS_Down) {
+                ibus_lookup_table_cursor_down (hangul->table);
+                ibus_hangul_engine_update_lookup_table_ui (hangul);
+                return TRUE;
+            }
+        }
     }
 
     if (!hangul->hanja_mode) {
-       if (keyval == IBUS_h) {
-           ibus_lookup_table_cursor_up (hangul->table);
-           ibus_hangul_engine_update_lookup_table_ui (hangul);
-           return TRUE;
-       } else if (keyval == IBUS_l) {
-           ibus_lookup_table_cursor_down (hangul->table);
-           ibus_hangul_engine_update_lookup_table_ui (hangul);
-           return TRUE;
-       } else if (keyval == IBUS_k) {
-           ibus_lookup_table_page_up (hangul->table);
-           ibus_hangul_engine_update_lookup_table_ui (hangul);
-           return TRUE;
-       } else if (keyval == IBUS_j) {
-           ibus_lookup_table_page_down (hangul->table);
-           ibus_hangul_engine_update_lookup_table_ui (hangul);
-           return TRUE;
-       }
+        if (lookup_table_orientation == 0) {
+            // horizontal
+            if (keyval == IBUS_h) {
+                ibus_lookup_table_cursor_up (hangul->table);
+                ibus_hangul_engine_update_lookup_table_ui (hangul);
+                return TRUE;
+            } else if (keyval == IBUS_l) {
+                ibus_lookup_table_cursor_down (hangul->table);
+                ibus_hangul_engine_update_lookup_table_ui (hangul);
+                return TRUE;
+            } else if (keyval == IBUS_k) {
+                ibus_lookup_table_page_up (hangul->table);
+                ibus_hangul_engine_update_lookup_table_ui (hangul);
+                return TRUE;
+            } else if (keyval == IBUS_j) {
+                ibus_lookup_table_page_down (hangul->table);
+                ibus_hangul_engine_update_lookup_table_ui (hangul);
+                return TRUE;
+            }
+        } else {
+            // vertical
+            if (keyval == IBUS_h) {
+                ibus_lookup_table_page_up (hangul->table);
+                ibus_hangul_engine_update_lookup_table_ui (hangul);
+                return TRUE;
+            } else if (keyval == IBUS_l) {
+                ibus_lookup_table_page_down (hangul->table);
+                ibus_hangul_engine_update_lookup_table_ui (hangul);
+                return TRUE;
+            } else if (keyval == IBUS_k) {
+                ibus_lookup_table_cursor_up (hangul->table);
+                ibus_hangul_engine_update_lookup_table_ui (hangul);
+                return TRUE;
+            } else if (keyval == IBUS_j) {
+                ibus_lookup_table_cursor_down (hangul->table);
+                ibus_hangul_engine_update_lookup_table_ui (hangul);
+                return TRUE;
+            }
+        }
     }
 
     return FALSE;
@@ -591,6 +914,7 @@ ibus_hangul_engine_process_key_event (IBusEngine     *engine,
 {
     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
 
+    guint mask;
     gboolean retval;
     const ucschar *str;
 
@@ -605,81 +929,137 @@ 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 (hangul->hanja_list == NULL) {
-           ibus_hangul_engine_update_lookup_table (hangul);
-       } else {
-           ibus_hangul_engine_hide_lookup_table (hangul);
-       }
-       return TRUE;
+    // 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 {
+            ibus_hangul_engine_hide_lookup_table (hangul);
+        }
+        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);
-       if (hangul->hanja_mode) {
-           if (retval)
-               return TRUE;
-       } else {
-           return TRUE;
-       }
+        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) {
-        retval = hangul_ic_backspace (hangul->context);
-    } else {
-       // ignore capslock
-       if (modifiers & IBUS_LOCK_MASK) {
-           if (keyval >= 'A' && keyval <= 'z') {
-               if (isupper(keyval))
-                   keyval = tolower(keyval);
-               else
-                   keyval = toupper(keyval);
-           }
-       }
-        retval = hangul_ic_process (hangul->context, keyval);
+    // If we've got a key event with some 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.
+    // Ignore key event with control, alt, super or mod5
+    mask = IBUS_CONTROL_MASK |
+           IBUS_MOD1_MASK | IBUS_MOD3_MASK | IBUS_MOD4_MASK | IBUS_MOD5_MASK;
+    if (modifiers & mask) {
+        ibus_hangul_engine_flush (hangul);
+        return FALSE;
     }
 
-    str = hangul_ic_get_commit_string (hangul->context);
-    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);
-           if (ustring_length (hangul->preedit) > 0) {
-               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);
-       }
+    if (keyval == IBUS_BackSpace) {
+        retval = hangul_ic_backspace (hangul->context);
+        if (!retval) {
+            guint preedit_len = ustring_length (hangul->preedit);
+            if (preedit_len > 0) {
+                ustring_erase (hangul->preedit, preedit_len - 1, 1);
+                retval = TRUE;
+            }
+        }
+
+        ibus_hangul_engine_update_preedit_text (hangul);
+
+        if (hangul->hanja_mode) {
+            if (ibus_hangul_engine_has_preedit (hangul)) {
+                ibus_hangul_engine_update_lookup_table (hangul);
+            } else {
+                ibus_hangul_engine_hide_lookup_table (hangul);
+            }
+        }
     } 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);
+       // We need to normalize the keyval to US qwerty keylayout,
+       // because the korean input method is depend on the position of
+       // each key, not the character. We make the keyval from keycode
+       // as if the keyboard is US qwerty layout. Then we can assume the
+       // keyval represent the position of the each key.
+       // But if the hic is in transliteration mode, then we should not
+       // normalize the keyval.
+       bool is_transliteration_mode =
+                hangul_ic_is_transliteration(hangul->context);
+       if (!is_transliteration_mode) {
+           if (keymap != NULL)
+               keyval = ibus_keymap_lookup_keysym(keymap, keycode, modifiers);
        }
-    }
 
-    ibus_hangul_engine_update_preedit_text (hangul);
+        // ignore capslock
+        if (modifiers & IBUS_LOCK_MASK) {
+            if (keyval >= 'A' && keyval <= 'z') {
+                if (isupper(keyval))
+                    keyval = tolower(keyval);
+                else
+                    keyval = toupper(keyval);
+            }
+        }
+        retval = hangul_ic_process (hangul->context, keyval);
 
-    if (hangul->hanja_mode) {
-       ibus_hangul_engine_update_lookup_table (hangul);
+        str = hangul_ic_get_commit_string (hangul->context);
+        if (word_commit || 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);
+                if (ustring_length (hangul->preedit) > 0) {
+                    /* clear preedit text before commit */
+                    ibus_hangul_engine_clear_preedit_text (hangul);
+
+                    preedit = ustring_begin (hangul->preedit);
+                    text = ibus_text_new_from_ucs4 ((gunichar*)preedit);
+                    ibus_engine_commit_text (engine, text);
+                }
+                ustring_clear (hangul->preedit);
+            }
+        } else {
+            if (str != NULL && str[0] != 0) {
+                IBusText *text;
+
+                /* clear preedit text before commit */
+                ibus_hangul_engine_clear_preedit_text (hangul);
+
+                text = ibus_text_new_from_ucs4 (str);
+                ibus_engine_commit_text (engine, text);
+            }
+        }
+
+        ibus_hangul_engine_update_preedit_text (hangul);
+
+        if (hangul->hanja_mode) {
+            ibus_hangul_engine_update_lookup_table (hangul);
+        }
+
+        if (!retval)
+            ibus_hangul_engine_flush (hangul);
     }
 
-    if (!retval)
-        ibus_hangul_engine_flush (hangul);
-
     return retval;
 }
 
@@ -695,18 +1075,19 @@ 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) {
+        /* clear preedit text before commit */
+        ibus_hangul_engine_clear_preedit_text (hangul);
 
-    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_hide_preedit_text ((IBusEngine *) hangul);
-    ibus_engine_commit_text ((IBusEngine *) hangul, text);
+       ibus_engine_commit_text ((IBusEngine *) hangul, text);
 
-    g_object_unref (text);
+       ustring_clear(hangul->preedit);
+    }
 
-    ustring_clear(hangul->preedit);
+    ibus_hangul_engine_update_preedit_text (hangul);
 }
 
 static void
@@ -715,13 +1096,15 @@ ibus_hangul_engine_focus_in (IBusEngine *engine)
     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
 
     if (hangul->hanja_mode) {
-       hangul->prop_hanja_mode->state = PROP_STATE_CHECKED;
+        ibus_property_set_state (hangul->prop_hanja_mode, PROP_STATE_CHECKED);
     } else {
-       hangul->prop_hanja_mode->state = PROP_STATE_UNCHECKED;
+        ibus_property_set_state (hangul->prop_hanja_mode, PROP_STATE_UNCHECKED);
     }
 
     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);
     }
@@ -735,10 +1118,14 @@ 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);
+        ibus_engine_hide_lookup_table (engine);
+        ibus_engine_hide_auxiliary_text (engine);
     }
 
     parent_class->focus_out ((IBusEngine *) hangul);
@@ -750,9 +1137,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);
 }
 
@@ -760,6 +1144,8 @@ static void
 ibus_hangul_engine_enable (IBusEngine *engine)
 {
     parent_class->enable (engine);
+
+    ibus_engine_get_surrounding_text (engine, NULL, NULL, NULL);
 }
 
 static void
@@ -787,8 +1173,8 @@ ibus_hangul_engine_cursor_up (IBusEngine *engine)
     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
 
     if (hangul->hanja_list != NULL) {
-       ibus_lookup_table_cursor_up (hangul->table);
-       ibus_hangul_engine_update_lookup_table_ui (hangul);
+        ibus_lookup_table_cursor_up (hangul->table);
+        ibus_hangul_engine_update_lookup_table_ui (hangul);
     }
 
     parent_class->cursor_up (engine);
@@ -800,8 +1186,8 @@ ibus_hangul_engine_cursor_down (IBusEngine *engine)
     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
 
     if (hangul->hanja_list != NULL) {
-       ibus_lookup_table_cursor_down (hangul->table);
-       ibus_hangul_engine_update_lookup_table_ui (hangul);
+        ibus_lookup_table_cursor_down (hangul->table);
+        ibus_hangul_engine_update_lookup_table_ui (hangul);
     }
 
     parent_class->cursor_down (engine);
@@ -815,52 +1201,91 @@ ibus_hangul_engine_property_activate (IBusEngine    *engine,
     if (strcmp(prop_name, "setup") == 0) {
         GError *error = NULL;
         gchar *argv[2] = { NULL, };
-       gchar *path;
-       const char* libexecdir;
 
-       libexecdir = g_getenv("LIBEXECDIR");
-       if (libexecdir == NULL)
-           libexecdir = LIBEXECDIR;
+        argv[0] = "ibus-setup-hangul";
+        argv[1] = NULL;
+        g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error);
+    } else if (strcmp(prop_name, "hanja_mode") == 0) {
+        IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
+
+        hangul->hanja_mode = !hangul->hanja_mode;
+        if (hangul->hanja_mode) {
+            ibus_property_set_state (hangul->prop_hanja_mode,
+                    PROP_STATE_CHECKED);
+        } else {
+            ibus_property_set_state (hangul->prop_hanja_mode,
+                    PROP_STATE_UNCHECKED);
+        }
+
+        ibus_engine_update_property (engine, hangul->prop_hanja_mode);
+        ibus_hangul_engine_flush (hangul);
+    }
+}
+
+static gboolean
+ibus_hangul_engine_has_preedit (IBusHangulEngine *hangul)
+{
+    guint preedit_len;
+    const ucschar *hic_preedit;
 
-       path = g_build_filename(libexecdir, "ibus-setup-hangul", NULL);
-       argv[0] = path;
-       argv[1] = NULL;
-        g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, NULL, &error);
+    hic_preedit = hangul_ic_get_preedit_string (hangul->context);
+    if (hic_preedit[0] != 0)
+        return TRUE;
 
-       g_free(path);
-    } else if (strcmp(prop_name, "hanja_mode") == 0) {
-       IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
+    preedit_len = ustring_length (hangul->preedit);
+    if (preedit_len > 0)
+        return TRUE;
 
-       hangul->hanja_mode = !hangul->hanja_mode;
-       if (hangul->hanja_mode) {
-           hangul->prop_hanja_mode->state = PROP_STATE_CHECKED;
-       } else {
-           hangul->prop_hanja_mode->state = PROP_STATE_UNCHECKED;
-       }
+    return FALSE;
+}
 
-       ibus_engine_update_property (engine, hangul->prop_hanja_mode);
-       ibus_hangul_engine_flush (hangul);
+static bool
+ibus_hangul_engine_on_transition (HangulInputContext     *hic,
+                                  ucschar                 c,
+                                  const ucschar          *preedit,
+                                  void                   *data)
+{
+    if (!auto_reorder) {
+        if (hangul_is_choseong (c)) {
+            if (hangul_ic_has_jungseong (hic) || hangul_ic_has_jongseong (hic))
+                return false;
+        }
+
+        if (hangul_is_jungseong (c)) {
+            if (hangul_ic_has_jongseong (hic))
+                return false;
+        }
     }
+
+    return true;
 }
 
 static void
 ibus_config_value_changed (IBusConfig   *config,
                            const gchar  *section,
                            const gchar  *name,
-                           GValue       *value,
+                           GVariant     *value,
                            gpointer      user_data)
 {
     IBusHangulEngine *hangul = (IBusHangulEngine *) user_data;
 
     if (strcmp(section, "engine/Hangul") == 0) {
         if (strcmp(name, "HangulKeyboard") == 0) {
-            const gchar *str = g_value_get_string (value);
+            const gchar *str = g_variant_get_string(value, NULL);
             g_string_assign (hangul_keyboard, str);
             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);
-       }
+            const gchar* str = g_variant_get_string(value, NULL);
+           hanja_key_list_set_from_string(&hanja_keys, str);
+        } else if (strcmp(name, "WordCommit") == 0) {
+            word_commit = g_variant_get_boolean (value);
+        } else if (strcmp (name, "AutoReorder") == 0) {
+            auto_reorder = g_variant_get_boolean (value);
+        }
+    } else if (strcmp(section, "panel") == 0) {
+        if (strcmp(name, "lookup_table_orientation") == 0) {
+            lookup_table_orientation = g_variant_get_int32(value);
+        }
     }
 }
 
@@ -878,41 +1303,142 @@ 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)
+{
+    struct KeyEvent ev = { keyval, modifiers};
+    g_array_append_val(list, ev);
+}
+
+static gboolean
+key_event_list_match(GArray* list, guint keyval, guint modifiers)
+{
+    guint i;
+    guint mask;
+
+    /* ignore capslock and numlock */
+    mask = IBUS_SHIFT_MASK |
+           IBUS_CONTROL_MASK |
+           IBUS_MOD1_MASK |
+           IBUS_MOD3_MASK |
+           IBUS_MOD4_MASK |
+           IBUS_MOD5_MASK;
+
+    modifiers &= mask;
+    for (i = 0; i < list->len; ++i) {
+        struct KeyEvent* ev = &g_array_index(list, struct KeyEvent, i);
+        if (ev->keyval == keyval && ev->modifiers == modifiers) {
+            return TRUE;
+        }
+    }
+
+    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);
 
-    g_array_set_size(list, 0);
+    list->all_modifiers = 0;
+    g_array_set_size(list->keys, 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);
+        int i;
+        for (i = 0; items[i] != NULL; ++i) {
+           hanja_key_list_append_from_string(list, items[i]);
+        }
+        g_strfreev(items);
     }
 }
 
 static gboolean
-key_event_list_match(GArray* list, guint keyval, guint modifiers)
+hanja_key_list_match(HanjaKeyList* list, guint keyval, guint modifiers)
 {
-    guint i;
+    return key_event_list_match(list->keys, keyval, modifiers);
+}
 
-    modifiers &= 0x7FFF;          /* ignore ibus internal values */
-    modifiers &= ~IBUS_LOCK_MASK; /* ignore capslock */
-    modifiers &= ~IBUS_MOD2_MASK; /* ignore numlock */
-    for (i = 0; i < list->len; ++i) {
-       struct KeyEvent* ev = &g_array_index(list, struct KeyEvent, i);
-       if (ev->keyval == keyval && ev->modifiers == 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;