Merge branch 'define-symbol' of git://github.com/ueno/ibus-hangul
[platform/upstream/ibus-hangul.git] / src / engine.c
1 /* vim:set et sts=4: */
2 /* ibus-hangul - The Hangul Engine For IBus
3  * Copyright (C) 2008-2009 Peng Huang <shawn.p.huang@gmail.com>
4  * Copyright (C) 2009-2011 Choe Hwanjin <choe.hwanjin@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <ibus.h>
26 #include <hangul.h>
27 #include <string.h>
28 #include <ctype.h>
29
30 #include "i18n.h"
31 #include "engine.h"
32 #include "ustring.h"
33
34
35 typedef struct _IBusHangulEngine IBusHangulEngine;
36 typedef struct _IBusHangulEngineClass IBusHangulEngineClass;
37
38 typedef struct _HanjaKeyList HanjaKeyList;
39
40 struct _IBusHangulEngine {
41     IBusEngine parent;
42
43     /* members */
44     HangulInputContext *context;
45     UString* preedit;
46     gboolean hangul_mode;
47     gboolean hanja_mode;
48     HanjaList* hanja_list;
49     int last_lookup_method;
50
51     IBusLookupTable *table;
52
53     IBusProperty    *prop_hanja_mode;
54     IBusPropList    *prop_list;
55 };
56
57 struct _IBusHangulEngineClass {
58     IBusEngineClass parent;
59 };
60
61 struct KeyEvent {
62     guint keyval;
63     guint modifiers;
64 };
65
66 struct _HanjaKeyList {
67     guint   all_modifiers;
68     GArray *keys;
69 };
70
71 enum {
72     LOOKUP_METHOD_EXACT,
73     LOOKUP_METHOD_PREFIX,
74     LOOKUP_METHOD_SUFFIX,
75 };
76
77 /* functions prototype */
78 static void     ibus_hangul_engine_class_init
79                                             (IBusHangulEngineClass  *klass);
80 static void     ibus_hangul_engine_init     (IBusHangulEngine       *hangul);
81 static GObject*
82                 ibus_hangul_engine_constructor
83                                             (GType                   type,
84                                              guint                   n_construct_params,
85                                              GObjectConstructParam  *construct_params);
86 static void     ibus_hangul_engine_destroy  (IBusHangulEngine       *hangul);
87 static gboolean
88                 ibus_hangul_engine_process_key_event
89                                             (IBusEngine             *engine,
90                                              guint                   keyval,
91                                              guint                   keycode,
92                                              guint                   modifiers);
93 static void ibus_hangul_engine_focus_in     (IBusEngine             *engine);
94 static void ibus_hangul_engine_focus_out    (IBusEngine             *engine);
95 static void ibus_hangul_engine_reset        (IBusEngine             *engine);
96 static void ibus_hangul_engine_enable       (IBusEngine             *engine);
97 static void ibus_hangul_engine_disable      (IBusEngine             *engine);
98 #if 0
99 static void ibus_engine_set_cursor_location (IBusEngine             *engine,
100                                              gint                    x,
101                                              gint                    y,
102                                              gint                    w,
103                                              gint                    h);
104 static void ibus_hangul_engine_set_capabilities
105                                             (IBusEngine             *engine,
106                                              guint                   caps);
107 #endif
108 static void ibus_hangul_engine_page_up      (IBusEngine             *engine);
109 static void ibus_hangul_engine_page_down    (IBusEngine             *engine);
110 static void ibus_hangul_engine_cursor_up    (IBusEngine             *engine);
111 static void ibus_hangul_engine_cursor_down  (IBusEngine             *engine);
112 static void ibus_hangul_engine_property_activate
113                                             (IBusEngine             *engine,
114                                              const gchar            *prop_name,
115                                              guint                   prop_state);
116 #if 0
117 static void ibus_hangul_engine_property_show
118                                                                                         (IBusEngine             *engine,
119                                              const gchar            *prop_name);
120 static void ibus_hangul_engine_property_hide
121                                                                                         (IBusEngine             *engine,
122                                              const gchar            *prop_name);
123 #endif
124
125 static void ibus_hangul_engine_candidate_clicked
126                                             (IBusEngine             *engine,
127                                              guint                   index,
128                                              guint                   button,
129                                              guint                   state);
130
131 static void ibus_hangul_engine_flush        (IBusHangulEngine       *hangul);
132 static void ibus_hangul_engine_clear_preedit_text
133                                             (IBusHangulEngine       *hangul);
134 static void ibus_hangul_engine_update_preedit_text
135                                             (IBusHangulEngine       *hangul);
136
137 static void ibus_hangul_engine_update_lookup_table
138                                             (IBusHangulEngine       *hangul);
139 static gboolean ibus_hangul_engine_has_preedit
140                                             (IBusHangulEngine       *hangul);
141 static bool ibus_hangul_engine_on_transition
142                                             (HangulInputContext     *hic,
143                                              ucschar                 c,
144                                              const ucschar          *preedit,
145                                              void                   *data);
146
147 static void ibus_config_value_changed       (IBusConfig             *config,
148                                              const gchar            *section,
149                                              const gchar            *name,
150                                              GVariant               *value,
151                                              gpointer                user_data);
152
153 static void        lookup_table_set_visible (IBusLookupTable        *table,
154                                              gboolean                flag);
155 static gboolean        lookup_table_is_visible
156                                             (IBusLookupTable        *table);
157
158 static gboolean key_event_list_match        (GArray                 *list,
159                                              guint                   keyval,
160                                              guint                   modifiers);
161
162 static void     hanja_key_list_init         (HanjaKeyList           *list);
163 static void     hanja_key_list_fini         (HanjaKeyList           *list);
164 static void     hanja_key_list_set_from_string(HanjaKeyList         *list,
165                                              const char             *str);
166 static void     hanja_key_list_append       (HanjaKeyList           *list,
167                                              guint                   keyval,
168                                              guint                   modifiers);
169 static gboolean hanja_key_list_match        (HanjaKeyList           *list,
170                                              guint                   keyval,
171                                              guint                   modifiers);
172 static gboolean hanja_key_list_has_modifier (HanjaKeyList           *list,
173                                              guint                   keyval);
174
175 static glong ucschar_strlen (const ucschar* str);
176
177 static IBusEngineClass *parent_class = NULL;
178 static HanjaTable *hanja_table = NULL;
179 static HanjaTable *symbol_table = NULL;
180 static IBusConfig *config = NULL;
181 static GString    *hangul_keyboard = NULL;
182 static HanjaKeyList hanja_keys;
183 static int lookup_table_orientation = 0;
184 static IBusKeymap *keymap = NULL;
185 static gboolean word_commit = FALSE;
186 static gboolean auto_reorder = TRUE;
187
188 static glong
189 ucschar_strlen (const ucschar* str)
190 {
191     const ucschar* p = str;
192     while (*p != 0)
193         p++;
194     return p - str;
195 }
196
197 GType
198 ibus_hangul_engine_get_type (void)
199 {
200     static GType type = 0;
201
202     static const GTypeInfo type_info = {
203         sizeof (IBusHangulEngineClass),
204         (GBaseInitFunc)     NULL,
205         (GBaseFinalizeFunc) NULL,
206         (GClassInitFunc)    ibus_hangul_engine_class_init,
207         NULL,
208         NULL,
209         sizeof (IBusHangulEngine),
210         0,
211         (GInstanceInitFunc) ibus_hangul_engine_init,
212     };
213
214     if (type == 0) {
215             type = g_type_register_static (IBUS_TYPE_ENGINE,
216                                            "IBusHangulEngine",
217                                            &type_info,
218                                            (GTypeFlags) 0);
219     }
220
221     return type;
222 }
223
224 void
225 ibus_hangul_init (IBusBus *bus)
226 {
227     GVariant* value;
228
229     hanja_table = hanja_table_load (NULL);
230
231     symbol_table = hanja_table_load (IBUSHANGUL_DATADIR "/data/symbol.txt");
232
233     config = ibus_bus_get_config (bus);
234     if (config)
235         g_object_ref_sink (config);
236
237     hangul_keyboard = g_string_new_len ("2", 8);
238     value = ibus_config_get_value (config, "engine/Hangul",
239                                          "HangulKeyboard");
240     if (value != NULL) {
241         const gchar* str = g_variant_get_string (value, NULL);
242         g_string_assign (hangul_keyboard, str);
243         g_variant_unref(value);
244     }
245
246     hanja_key_list_init(&hanja_keys);
247
248     value = ibus_config_get_value (config, "engine/Hangul",
249                                          "HanjaKeys");
250     if (value != NULL) {
251         const gchar* str = g_variant_get_string (value, NULL);
252         hanja_key_list_set_from_string(&hanja_keys, str);
253         g_variant_unref(value);
254     } else {
255         hanja_key_list_append(&hanja_keys, IBUS_Hangul_Hanja, 0);
256         hanja_key_list_append(&hanja_keys, IBUS_F9, 0);
257     }
258
259     value = ibus_config_get_value (config, "engine/Hangul",
260                                          "WordCommit");
261     if (value != NULL) {
262         word_commit = g_variant_get_boolean (value);
263         g_variant_unref(value);
264     }
265
266     value = ibus_config_get_value (config, "engine/Hangul", "AutoReorder");
267     if (value != NULL) {
268         auto_reorder = g_variant_get_boolean (value);
269         g_variant_unref (value);
270     }
271
272     keymap = ibus_keymap_get("us");
273 }
274
275 void
276 ibus_hangul_exit (void)
277 {
278     if (keymap != NULL) {
279         g_object_unref(keymap);
280         keymap = NULL;
281     }
282
283     hanja_key_list_fini(&hanja_keys);
284
285     hanja_table_delete (hanja_table);
286     hanja_table = NULL;
287
288     hanja_table_delete (symbol_table);
289     symbol_table = NULL;
290
291     g_object_unref (config);
292     config = NULL;
293
294     g_string_free (hangul_keyboard, TRUE);
295     hangul_keyboard = NULL;
296 }
297
298 static void
299 ibus_hangul_engine_class_init (IBusHangulEngineClass *klass)
300 {
301     GObjectClass *object_class = G_OBJECT_CLASS (klass);
302     IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
303     IBusEngineClass *engine_class = IBUS_ENGINE_CLASS (klass);
304
305     parent_class = (IBusEngineClass *) g_type_class_peek_parent (klass);
306
307     object_class->constructor = ibus_hangul_engine_constructor;
308     ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_hangul_engine_destroy;
309
310     engine_class->process_key_event = ibus_hangul_engine_process_key_event;
311
312     engine_class->reset = ibus_hangul_engine_reset;
313     engine_class->enable = ibus_hangul_engine_enable;
314     engine_class->disable = ibus_hangul_engine_disable;
315
316     engine_class->focus_in = ibus_hangul_engine_focus_in;
317     engine_class->focus_out = ibus_hangul_engine_focus_out;
318
319     engine_class->page_up = ibus_hangul_engine_page_up;
320     engine_class->page_down = ibus_hangul_engine_page_down;
321
322     engine_class->cursor_up = ibus_hangul_engine_cursor_up;
323     engine_class->cursor_down = ibus_hangul_engine_cursor_down;
324
325     engine_class->property_activate = ibus_hangul_engine_property_activate;
326
327     engine_class->candidate_clicked = ibus_hangul_engine_candidate_clicked;
328 }
329
330 static void
331 ibus_hangul_engine_init (IBusHangulEngine *hangul)
332 {
333     IBusProperty* prop;
334     IBusText* label;
335     IBusText* tooltip;
336
337     hangul->context = hangul_ic_new (hangul_keyboard->str);
338     hangul_ic_connect_callback (hangul->context, "transition",
339                                 ibus_hangul_engine_on_transition, hangul);
340
341     hangul->preedit = ustring_new();
342     hangul->hanja_list = NULL;
343     hangul->hangul_mode = TRUE;
344     hangul->hanja_mode = FALSE;
345     hangul->last_lookup_method = LOOKUP_METHOD_PREFIX;
346
347     hangul->prop_list = ibus_prop_list_new ();
348     g_object_ref_sink (hangul->prop_list);
349
350     label = ibus_text_new_from_string (_("Hanja lock"));
351     tooltip = ibus_text_new_from_string (_("Enable/Disable Hanja mode"));
352     prop = ibus_property_new ("hanja_mode",
353                               PROP_TYPE_TOGGLE,
354                               label,
355                               NULL,
356                               tooltip,
357                               TRUE, TRUE, PROP_STATE_UNCHECKED, NULL);
358     g_object_ref_sink (prop);
359     ibus_prop_list_append (hangul->prop_list, prop);
360     hangul->prop_hanja_mode = prop;
361
362     label = ibus_text_new_from_string (_("Setup"));
363     tooltip = ibus_text_new_from_string (_("Configure hangul engine"));
364     prop = ibus_property_new ("setup",
365                               PROP_TYPE_NORMAL,
366                               label,
367                               "gtk-preferences",
368                               tooltip,
369                               TRUE, TRUE, PROP_STATE_UNCHECKED, NULL);
370     ibus_prop_list_append (hangul->prop_list, prop);
371
372     hangul->table = ibus_lookup_table_new (9, 0, TRUE, FALSE);
373     g_object_ref_sink (hangul->table);
374
375     g_signal_connect (config, "value-changed",
376                       G_CALLBACK(ibus_config_value_changed), hangul);
377 }
378
379 static GObject*
380 ibus_hangul_engine_constructor (GType                   type,
381                                 guint                   n_construct_params,
382                                 GObjectConstructParam  *construct_params)
383 {
384     IBusHangulEngine *hangul;
385
386     hangul = (IBusHangulEngine *) G_OBJECT_CLASS (parent_class)->constructor (type,
387                                                        n_construct_params,
388                                                        construct_params);
389
390     return (GObject *)hangul;
391 }
392
393
394 static void
395 ibus_hangul_engine_destroy (IBusHangulEngine *hangul)
396 {
397     if (hangul->prop_hanja_mode) {
398         g_object_unref (hangul->prop_hanja_mode);
399         hangul->prop_hanja_mode = NULL;
400     }
401
402     if (hangul->prop_list) {
403         g_object_unref (hangul->prop_list);
404         hangul->prop_list = NULL;
405     }
406
407     if (hangul->table) {
408         g_object_unref (hangul->table);
409         hangul->table = NULL;
410     }
411
412     if (hangul->context) {
413         hangul_ic_delete (hangul->context);
414         hangul->context = NULL;
415     }
416
417     IBUS_OBJECT_CLASS (parent_class)->destroy ((IBusObject *)hangul);
418 }
419
420 static void
421 ibus_hangul_engine_clear_preedit_text (IBusHangulEngine *hangul)
422 {
423     IBusText *text;
424
425     text = ibus_text_new_from_static_string ("");
426     ibus_engine_update_preedit_text ((IBusEngine *)hangul, text, 0, FALSE);
427 }
428
429 static void
430 ibus_hangul_engine_update_preedit_text (IBusHangulEngine *hangul)
431 {
432     const ucschar *hic_preedit;
433     IBusText *text;
434     UString *preedit;
435     gint preedit_len;
436
437     // ibus-hangul's preedit string is made up of ibus context's
438     // internal preedit string and libhangul's preedit string.
439     // libhangul only supports one syllable preedit string.
440     // In order to make longer preedit string, ibus-hangul maintains
441     // internal preedit string.
442     hic_preedit = hangul_ic_get_preedit_string (hangul->context);
443
444     preedit = ustring_dup (hangul->preedit);
445     preedit_len = ustring_length(preedit);
446     ustring_append_ucs4 (preedit, hic_preedit, -1);
447
448     if (ustring_length(preedit) > 0) {
449         IBusPreeditFocusMode preedit_option = IBUS_ENGINE_PREEDIT_COMMIT;
450
451         if (hangul->hanja_list != NULL)
452             preedit_option = IBUS_ENGINE_PREEDIT_CLEAR;
453
454         text = ibus_text_new_from_ucs4 ((gunichar*)preedit->data);
455         // ibus-hangul's internal preedit string
456         ibus_text_append_attribute (text, IBUS_ATTR_TYPE_UNDERLINE,
457                 IBUS_ATTR_UNDERLINE_SINGLE, 0, preedit_len);
458         // Preedit string from libhangul context.
459         // This is currently composing syllable.
460         ibus_text_append_attribute (text, IBUS_ATTR_TYPE_FOREGROUND,
461                 0x00ffffff, preedit_len, -1);
462         ibus_text_append_attribute (text, IBUS_ATTR_TYPE_BACKGROUND,
463                 0x00000000, preedit_len, -1);
464         ibus_engine_update_preedit_text_with_mode ((IBusEngine *)hangul,
465                                                    text,
466                                                    ibus_text_get_length (text),
467                                                    TRUE,
468                                                    preedit_option);
469     } else {
470         text = ibus_text_new_from_static_string ("");
471         ibus_engine_update_preedit_text ((IBusEngine *)hangul, text, 0, FALSE);
472     }
473
474     ustring_delete(preedit);
475 }
476
477 static void
478 ibus_hangul_engine_update_lookup_table_ui (IBusHangulEngine *hangul)
479 {
480     guint cursor_pos;
481     const char* comment;
482     IBusText* text;
483
484     // update aux text
485     cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
486     comment = hanja_list_get_nth_comment (hangul->hanja_list, cursor_pos);
487
488     text = ibus_text_new_from_string (comment);
489     ibus_engine_update_auxiliary_text ((IBusEngine *)hangul, text, TRUE);
490
491     // update lookup table
492     ibus_engine_update_lookup_table ((IBusEngine *)hangul, hangul->table, TRUE);
493 }
494
495 static void
496 ibus_hangul_engine_commit_current_candidate (IBusHangulEngine *hangul)
497 {
498     guint cursor_pos;
499     const char* key;
500     const char* value;
501     const ucschar* hic_preedit;
502     glong key_len;
503     glong hic_preedit_len;
504     glong preedit_len;
505
506     IBusText* text;
507
508     cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
509     key = hanja_list_get_nth_key (hangul->hanja_list, cursor_pos);
510     value = hanja_list_get_nth_value (hangul->hanja_list, cursor_pos);
511     hic_preedit = hangul_ic_get_preedit_string (hangul->context);
512
513     key_len = g_utf8_strlen(key, -1);
514     preedit_len = ustring_length(hangul->preedit);
515     hic_preedit_len = ucschar_strlen (hic_preedit);
516
517     if (hangul->last_lookup_method == LOOKUP_METHOD_PREFIX) {
518         if (preedit_len == 0 && hic_preedit_len == 0) {
519             /* remove surrounding_text */
520             if (key_len > 0) {
521                 ibus_engine_delete_surrounding_text ((IBusEngine *)hangul,
522                         -key_len , key_len);
523             }
524         } else {
525             /* remove ibus preedit text */
526             if (key_len > 0) {
527                 glong n = MIN(key_len, preedit_len);
528                 ustring_erase (hangul->preedit, 0, n);
529                 key_len -= preedit_len;
530             }
531
532             /* remove hic preedit text */
533             if (key_len > 0) {
534                 hangul_ic_reset (hangul->context);
535                 key_len -= hic_preedit_len;
536             }
537         }
538     } else {
539         /* remove hic preedit text */
540         if (hic_preedit_len > 0) {
541             hangul_ic_reset (hangul->context);
542             key_len -= hic_preedit_len;
543         }
544
545         /* remove ibus preedit text */
546         if (key_len > preedit_len) {
547             ustring_erase (hangul->preedit, 0, preedit_len);
548             key_len -= preedit_len;
549         } else if (key_len > 0) {
550             ustring_erase (hangul->preedit, 0, key_len);
551             key_len = 0;
552         }
553
554         /* remove surrounding_text */
555         if (key_len > 0) {
556             ibus_engine_delete_surrounding_text ((IBusEngine *)hangul,
557                     -key_len , key_len);
558         }
559     }
560
561     /* clear preedit text before commit */
562     ibus_hangul_engine_clear_preedit_text (hangul);
563
564     text = ibus_text_new_from_string (value);
565     ibus_engine_commit_text ((IBusEngine *)hangul, text);
566
567     ibus_hangul_engine_update_preedit_text (hangul);
568 }
569
570 static gchar*
571 h_ibus_text_get_substring (IBusText* ibus_text, glong p1, glong p2)
572 {
573     const gchar* text;
574     const gchar* begin;
575     const gchar* end;
576     gchar* substring;
577     glong limit;
578     glong pos;
579     glong n;
580
581     text = ibus_text_get_text (ibus_text);
582     limit = ibus_text_get_length (ibus_text) + 1;
583     if (text == NULL || limit == 0)
584         return NULL;
585
586     p1 = MAX(0, p1);
587     p2 = MAX(0, p2);
588
589     pos = MIN(p1, p2);
590     n = ABS(p2 - p1);
591
592     if (pos + n > limit)
593         n = limit - pos;
594
595     begin = g_utf8_offset_to_pointer (text, pos);
596     end = g_utf8_offset_to_pointer (begin, n);
597
598     substring = g_strndup (begin, end - begin);
599     return substring;
600 }
601
602 static HanjaList*
603 ibus_hangul_engine_lookup_hanja_table (const char* key, int method)
604 {
605     HanjaList* list;
606
607     if (key == NULL)
608         return NULL;
609
610     switch (method) {
611     case LOOKUP_METHOD_EXACT:
612         if (symbol_table != NULL)
613             list = hanja_table_match_exact (symbol_table, key);
614
615         if (list == NULL)
616             list = hanja_table_match_exact (hanja_table, key);
617
618         break;
619     case LOOKUP_METHOD_PREFIX:
620         if (symbol_table != NULL)
621             list = hanja_table_match_prefix (symbol_table, key);
622
623         if (list == NULL)
624             list = hanja_table_match_prefix (hanja_table, key);
625
626         break;
627     case LOOKUP_METHOD_SUFFIX:
628         if (symbol_table != NULL)
629             list = hanja_table_match_suffix (symbol_table, key);
630
631         if (list == NULL)
632             list = hanja_table_match_suffix (hanja_table, key);
633
634         break;
635     }
636
637     return list;
638 }
639
640 static void
641 ibus_hangul_engine_update_hanja_list (IBusHangulEngine *hangul)
642 {
643     gchar* hanja_key;
644     gchar* preedit_utf8;
645     const ucschar* hic_preedit;
646     UString* preedit;
647     int lookup_method;
648     IBusText* ibus_text = NULL;
649     guint cursor_pos = 0;
650     guint anchor_pos = 0;
651
652     if (hangul->hanja_list != NULL) {
653         hanja_list_delete (hangul->hanja_list);
654         hangul->hanja_list = NULL;
655     }
656
657     hic_preedit = hangul_ic_get_preedit_string (hangul->context);
658
659     hanja_key = NULL;
660     lookup_method = LOOKUP_METHOD_PREFIX;
661
662     preedit = ustring_dup (hangul->preedit);
663     ustring_append_ucs4 (preedit, hic_preedit, -1);
664
665     if (ustring_length(preedit) > 0) {
666         preedit_utf8 = ustring_to_utf8 (preedit, -1);
667         if (word_commit || hangul->hanja_mode) {
668             hanja_key = preedit_utf8;
669             lookup_method = LOOKUP_METHOD_PREFIX;
670         } else {
671             gchar* substr;
672             ibus_engine_get_surrounding_text ((IBusEngine *)hangul, &ibus_text,
673                     &cursor_pos, &anchor_pos);
674
675             substr = h_ibus_text_get_substring (ibus_text,
676                     cursor_pos - 64, cursor_pos);
677
678             if (substr != NULL) {
679                 hanja_key = g_strconcat (substr, preedit_utf8, NULL);
680                 g_free (preedit_utf8);
681             } else {
682                 hanja_key = preedit_utf8;
683             }
684             lookup_method = LOOKUP_METHOD_SUFFIX;
685         }
686     } else {
687         ibus_engine_get_surrounding_text ((IBusEngine *)hangul, &ibus_text,
688                 &cursor_pos, &anchor_pos);
689         if (cursor_pos != anchor_pos) {
690             // If we have selection in surrounding text, we use that.
691             hanja_key = h_ibus_text_get_substring (ibus_text,
692                     cursor_pos, anchor_pos);
693             lookup_method = LOOKUP_METHOD_EXACT;
694         } else {
695             hanja_key = h_ibus_text_get_substring (ibus_text,
696                     cursor_pos - 64, cursor_pos);
697             lookup_method = LOOKUP_METHOD_SUFFIX;
698         }
699     }
700
701     if (hanja_key != NULL) {
702         hangul->hanja_list = ibus_hangul_engine_lookup_hanja_table (hanja_key,
703                 lookup_method);
704         hangul->last_lookup_method = lookup_method;
705         g_free (hanja_key);
706     }
707
708     ustring_delete (preedit);
709
710     if (ibus_text != NULL)
711         g_object_unref (ibus_text);
712 }
713
714 static void
715 ibus_hangul_engine_apply_hanja_list (IBusHangulEngine *hangul)
716 {
717     HanjaList* list = hangul->hanja_list;
718     if (list != NULL) {
719         int i, n;
720         n = hanja_list_get_size (list);
721
722         ibus_lookup_table_clear (hangul->table);
723         for (i = 0; i < n; i++) {
724             const char* value = hanja_list_get_nth_value (list, i);
725             IBusText* text = ibus_text_new_from_string (value);
726             ibus_lookup_table_append_candidate (hangul->table, text);
727         }
728
729         ibus_lookup_table_set_cursor_pos (hangul->table, 0);
730         ibus_hangul_engine_update_lookup_table_ui (hangul);
731         lookup_table_set_visible (hangul->table, TRUE);
732     }
733 }
734
735 static void
736 ibus_hangul_engine_hide_lookup_table (IBusHangulEngine *hangul)
737 {
738     gboolean is_visible;
739     is_visible = lookup_table_is_visible (hangul->table);
740
741     // Sending hide lookup table message when the lookup table
742     // is not visible results wrong behavior. So I have to check
743     // whether the table is visible or not before to hide.
744     if (is_visible) {
745         ibus_engine_hide_lookup_table ((IBusEngine *)hangul);
746         ibus_engine_hide_auxiliary_text ((IBusEngine *)hangul);
747         lookup_table_set_visible (hangul->table, FALSE);
748     }
749
750     if (hangul->hanja_list != NULL) {
751         hanja_list_delete (hangul->hanja_list);
752         hangul->hanja_list = NULL;
753     }
754 }
755
756 static void
757 ibus_hangul_engine_update_lookup_table (IBusHangulEngine *hangul)
758 {
759     ibus_hangul_engine_update_hanja_list (hangul);
760
761     if (hangul->hanja_list != NULL) {
762         // We should redraw preedit text with IBUS_ENGINE_PREEDIT_CLEAR option
763         // here to prevent committing it on focus out event incidentally.
764         ibus_hangul_engine_update_preedit_text (hangul);
765         ibus_hangul_engine_apply_hanja_list (hangul);
766     } else {
767         ibus_hangul_engine_hide_lookup_table (hangul);
768     }
769 }
770
771 static gboolean
772 ibus_hangul_engine_process_candidate_key_event (IBusHangulEngine    *hangul,
773                                                 guint                keyval,
774                                                 guint                modifiers)
775 {
776     if (keyval == IBUS_Escape) {
777         ibus_hangul_engine_hide_lookup_table (hangul);
778         // When the lookup table is poped up, preedit string is 
779         // updated with IBUS_ENGINE_PREEDIT_CLEAR option.
780         // So, when focus is out, the preedit text will not be committed.
781         // To prevent this problem, we have to update preedit text here
782         // with IBUS_ENGINE_PREEDIT_COMMIT option.
783         ibus_hangul_engine_update_preedit_text (hangul);
784         return TRUE;
785     } else if (keyval == IBUS_Return) {
786         ibus_hangul_engine_commit_current_candidate (hangul);
787
788         if (hangul->hanja_mode && ibus_hangul_engine_has_preedit (hangul)) {
789             ibus_hangul_engine_update_lookup_table (hangul);
790         } else {
791             ibus_hangul_engine_hide_lookup_table (hangul);
792         }
793         return TRUE;
794     } else if (keyval >= IBUS_1 && keyval <= IBUS_9) {
795         guint page_no;
796         guint page_size;
797         guint cursor_pos;
798
799         page_size = ibus_lookup_table_get_page_size (hangul->table);
800         cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
801         page_no = cursor_pos / page_size;
802
803         cursor_pos = page_no * page_size + (keyval - IBUS_1);
804         ibus_lookup_table_set_cursor_pos (hangul->table, cursor_pos);
805
806         ibus_hangul_engine_commit_current_candidate (hangul);
807
808         if (hangul->hanja_mode && ibus_hangul_engine_has_preedit (hangul)) {
809             ibus_hangul_engine_update_lookup_table (hangul);
810         } else {
811             ibus_hangul_engine_hide_lookup_table (hangul);
812         }
813         return TRUE;
814     } else if (keyval == IBUS_Page_Up) {
815         ibus_lookup_table_page_up (hangul->table);
816         ibus_hangul_engine_update_lookup_table_ui (hangul);
817         return TRUE;
818     } else if (keyval == IBUS_Page_Down) {
819         ibus_lookup_table_page_down (hangul->table);
820         ibus_hangul_engine_update_lookup_table_ui (hangul);
821         return TRUE;
822     } else {
823         if (lookup_table_orientation == 0) {
824             // horizontal
825             if (keyval == IBUS_Left) {
826                 ibus_lookup_table_cursor_up (hangul->table);
827                 ibus_hangul_engine_update_lookup_table_ui (hangul);
828                 return TRUE;
829             } else if (keyval == IBUS_Right) {
830                 ibus_lookup_table_cursor_down (hangul->table);
831                 ibus_hangul_engine_update_lookup_table_ui (hangul);
832                 return TRUE;
833             } else if (keyval == IBUS_Up) {
834                 ibus_lookup_table_page_up (hangul->table);
835                 ibus_hangul_engine_update_lookup_table_ui (hangul);
836                 return TRUE;
837             } else if (keyval == IBUS_Down) {
838                 ibus_lookup_table_page_down (hangul->table);
839                 ibus_hangul_engine_update_lookup_table_ui (hangul);
840                 return TRUE;
841             }
842         } else {
843             // vertical
844             if (keyval == IBUS_Left) {
845                 ibus_lookup_table_page_up (hangul->table);
846                 ibus_hangul_engine_update_lookup_table_ui (hangul);
847                 return TRUE;
848             } else if (keyval == IBUS_Right) {
849                 ibus_lookup_table_page_down (hangul->table);
850                 ibus_hangul_engine_update_lookup_table_ui (hangul);
851                 return TRUE;
852             } else if (keyval == IBUS_Up) {
853                 ibus_lookup_table_cursor_up (hangul->table);
854                 ibus_hangul_engine_update_lookup_table_ui (hangul);
855                 return TRUE;
856             } else if (keyval == IBUS_Down) {
857                 ibus_lookup_table_cursor_down (hangul->table);
858                 ibus_hangul_engine_update_lookup_table_ui (hangul);
859                 return TRUE;
860             }
861         }
862     }
863
864     if (!hangul->hanja_mode) {
865         if (lookup_table_orientation == 0) {
866             // horizontal
867             if (keyval == IBUS_h) {
868                 ibus_lookup_table_cursor_up (hangul->table);
869                 ibus_hangul_engine_update_lookup_table_ui (hangul);
870                 return TRUE;
871             } else if (keyval == IBUS_l) {
872                 ibus_lookup_table_cursor_down (hangul->table);
873                 ibus_hangul_engine_update_lookup_table_ui (hangul);
874                 return TRUE;
875             } else if (keyval == IBUS_k) {
876                 ibus_lookup_table_page_up (hangul->table);
877                 ibus_hangul_engine_update_lookup_table_ui (hangul);
878                 return TRUE;
879             } else if (keyval == IBUS_j) {
880                 ibus_lookup_table_page_down (hangul->table);
881                 ibus_hangul_engine_update_lookup_table_ui (hangul);
882                 return TRUE;
883             }
884         } else {
885             // vertical
886             if (keyval == IBUS_h) {
887                 ibus_lookup_table_page_up (hangul->table);
888                 ibus_hangul_engine_update_lookup_table_ui (hangul);
889                 return TRUE;
890             } else if (keyval == IBUS_l) {
891                 ibus_lookup_table_page_down (hangul->table);
892                 ibus_hangul_engine_update_lookup_table_ui (hangul);
893                 return TRUE;
894             } else if (keyval == IBUS_k) {
895                 ibus_lookup_table_cursor_up (hangul->table);
896                 ibus_hangul_engine_update_lookup_table_ui (hangul);
897                 return TRUE;
898             } else if (keyval == IBUS_j) {
899                 ibus_lookup_table_cursor_down (hangul->table);
900                 ibus_hangul_engine_update_lookup_table_ui (hangul);
901                 return TRUE;
902             }
903         }
904     }
905
906     return FALSE;
907 }
908
909 static gboolean
910 ibus_hangul_engine_process_key_event (IBusEngine     *engine,
911                                       guint           keyval,
912                                       guint           keycode,
913                                       guint           modifiers)
914 {
915     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
916
917     guint mask;
918     gboolean retval;
919     const ucschar *str;
920
921     if (modifiers & IBUS_RELEASE_MASK)
922         return FALSE;
923
924     // if we don't ignore shift keys, shift key will make flush the preedit 
925     // string. So you cannot input shift+key.
926     // Let's think about these examples:
927     //   dlTek (2 set)
928     //   qhRdmaqkq (2 set)
929     if (keyval == IBUS_Shift_L || keyval == IBUS_Shift_R)
930         return FALSE;
931
932     // If hanja key has any modifiers, we ignore that modifier keyval,
933     // or we cannot make the hanja key work.
934     // Because when we get the modifier key alone, we commit the
935     // current preedit string. So after that, even if we get the
936     // right hanja key event, we don't have preedit string to be changed
937     // to hanja word.
938     // See this bug: http://code.google.com/p/ibus/issues/detail?id=1036
939     if (hanja_key_list_has_modifier(&hanja_keys, keyval))
940         return FALSE; 
941
942     if (hanja_key_list_match(&hanja_keys, keyval, modifiers)) {
943         if (hangul->hanja_list == NULL) {
944             ibus_hangul_engine_update_lookup_table (hangul);
945         } else {
946             ibus_hangul_engine_hide_lookup_table (hangul);
947         }
948         return TRUE;
949     }
950
951     if (hangul->hanja_list != NULL) {
952         retval = ibus_hangul_engine_process_candidate_key_event (hangul,
953                      keyval, modifiers);
954         if (hangul->hanja_mode) {
955             if (retval)
956                 return TRUE;
957         } else {
958             return TRUE;
959         }
960     }
961
962     // If we've got a key event with some modifiers, commit current
963     // preedit string and ignore this key event.
964     // So, if you want to add some key event handler, put it 
965     // before this code.
966     // Ignore key event with control, alt, super or mod5
967     mask = IBUS_CONTROL_MASK |
968             IBUS_MOD1_MASK | IBUS_MOD3_MASK | IBUS_MOD4_MASK | IBUS_MOD5_MASK;
969     if (modifiers & mask) {
970         ibus_hangul_engine_flush (hangul);
971         return FALSE;
972     }
973
974     if (keyval == IBUS_BackSpace) {
975         retval = hangul_ic_backspace (hangul->context);
976         if (!retval) {
977             guint preedit_len = ustring_length (hangul->preedit);
978             if (preedit_len > 0) {
979                 ustring_erase (hangul->preedit, preedit_len - 1, 1);
980                 retval = TRUE;
981             }
982         }
983
984         ibus_hangul_engine_update_preedit_text (hangul);
985
986         if (hangul->hanja_mode) {
987             if (ibus_hangul_engine_has_preedit (hangul)) {
988                 ibus_hangul_engine_update_lookup_table (hangul);
989             } else {
990                 ibus_hangul_engine_hide_lookup_table (hangul);
991             }
992         }
993     } else {
994         // We need to normalize the keyval to US qwerty keylayout,
995         // because the korean input method is depend on the position of
996         // each key, not the character. We make the keyval from keycode
997         // as if the keyboard is US qwerty layout. Then we can assume the
998         // keyval represent the position of the each key.
999         // But if the hic is in transliteration mode, then we should not
1000         // normalize the keyval.
1001         bool is_transliteration_mode =
1002                  hangul_ic_is_transliteration(hangul->context);
1003         if (!is_transliteration_mode) {
1004             if (keymap != NULL)
1005                 keyval = ibus_keymap_lookup_keysym(keymap, keycode, modifiers);
1006         }
1007
1008         // ignore capslock
1009         if (modifiers & IBUS_LOCK_MASK) {
1010             if (keyval >= 'A' && keyval <= 'z') {
1011                 if (isupper(keyval))
1012                     keyval = tolower(keyval);
1013                 else
1014                     keyval = toupper(keyval);
1015             }
1016         }
1017         retval = hangul_ic_process (hangul->context, keyval);
1018
1019         str = hangul_ic_get_commit_string (hangul->context);
1020         if (word_commit || hangul->hanja_mode) {
1021             const ucschar* hic_preedit;
1022
1023             hic_preedit = hangul_ic_get_preedit_string (hangul->context);
1024             if (hic_preedit != NULL && hic_preedit[0] != 0) {
1025                 ustring_append_ucs4 (hangul->preedit, str, -1);
1026             } else {
1027                 IBusText *text;
1028                 const ucschar* preedit;
1029
1030                 ustring_append_ucs4 (hangul->preedit, str, -1);
1031                 if (ustring_length (hangul->preedit) > 0) {
1032                     /* clear preedit text before commit */
1033                     ibus_hangul_engine_clear_preedit_text (hangul);
1034
1035                     preedit = ustring_begin (hangul->preedit);
1036                     text = ibus_text_new_from_ucs4 ((gunichar*)preedit);
1037                     ibus_engine_commit_text (engine, text);
1038                 }
1039                 ustring_clear (hangul->preedit);
1040             }
1041         } else {
1042             if (str != NULL && str[0] != 0) {
1043                 IBusText *text;
1044
1045                 /* clear preedit text before commit */
1046                 ibus_hangul_engine_clear_preedit_text (hangul);
1047
1048                 text = ibus_text_new_from_ucs4 (str);
1049                 ibus_engine_commit_text (engine, text);
1050             }
1051         }
1052
1053         ibus_hangul_engine_update_preedit_text (hangul);
1054
1055         if (hangul->hanja_mode) {
1056             ibus_hangul_engine_update_lookup_table (hangul);
1057         }
1058
1059         if (!retval)
1060             ibus_hangul_engine_flush (hangul);
1061     }
1062
1063     return retval;
1064 }
1065
1066 static void
1067 ibus_hangul_engine_flush (IBusHangulEngine *hangul)
1068 {
1069     const gunichar *str;
1070     IBusText *text;
1071
1072     ibus_hangul_engine_hide_lookup_table (hangul);
1073
1074     str = hangul_ic_flush (hangul->context);
1075
1076     ustring_append_ucs4 (hangul->preedit, str, -1);
1077
1078     if (ustring_length (hangul->preedit) != 0) {
1079         /* clear preedit text before commit */
1080         ibus_hangul_engine_clear_preedit_text (hangul);
1081
1082         str = ustring_begin (hangul->preedit);
1083         text = ibus_text_new_from_ucs4 (str);
1084
1085         ibus_engine_commit_text ((IBusEngine *) hangul, text);
1086
1087         ustring_clear(hangul->preedit);
1088     }
1089
1090     ibus_hangul_engine_update_preedit_text (hangul);
1091 }
1092
1093 static void
1094 ibus_hangul_engine_focus_in (IBusEngine *engine)
1095 {
1096     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1097
1098     if (hangul->hanja_mode) {
1099         ibus_property_set_state (hangul->prop_hanja_mode, PROP_STATE_CHECKED);
1100     } else {
1101         ibus_property_set_state (hangul->prop_hanja_mode, PROP_STATE_UNCHECKED);
1102     }
1103
1104     ibus_engine_register_properties (engine, hangul->prop_list);
1105
1106     ibus_hangul_engine_update_preedit_text (hangul);
1107
1108     if (hangul->hanja_list != NULL) {
1109         ibus_hangul_engine_update_lookup_table_ui (hangul);
1110     }
1111
1112     parent_class->focus_in (engine);
1113 }
1114
1115 static void
1116 ibus_hangul_engine_focus_out (IBusEngine *engine)
1117 {
1118     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1119
1120     if (hangul->hanja_list == NULL) {
1121         // ibus-hangul uses
1122         // ibus_engine_update_preedit_text_with_mode() function which makes
1123         // the preedit string committed automatically when the focus is out.
1124         // So we don't need to commit the preedit here.
1125         hangul_ic_reset (hangul->context);
1126     } else {
1127         ibus_engine_hide_lookup_table (engine);
1128         ibus_engine_hide_auxiliary_text (engine);
1129     }
1130
1131     parent_class->focus_out ((IBusEngine *) hangul);
1132 }
1133
1134 static void
1135 ibus_hangul_engine_reset (IBusEngine *engine)
1136 {
1137     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1138
1139     ibus_hangul_engine_flush (hangul);
1140     parent_class->reset (engine);
1141 }
1142
1143 static void
1144 ibus_hangul_engine_enable (IBusEngine *engine)
1145 {
1146     parent_class->enable (engine);
1147
1148     ibus_engine_get_surrounding_text (engine, NULL, NULL, NULL);
1149 }
1150
1151 static void
1152 ibus_hangul_engine_disable (IBusEngine *engine)
1153 {
1154     ibus_hangul_engine_focus_out (engine);
1155     parent_class->disable (engine);
1156 }
1157
1158 static void
1159 ibus_hangul_engine_page_up (IBusEngine *engine)
1160 {
1161     parent_class->page_up (engine);
1162 }
1163
1164 static void
1165 ibus_hangul_engine_page_down (IBusEngine *engine)
1166 {
1167     parent_class->page_down (engine);
1168 }
1169
1170 static void
1171 ibus_hangul_engine_cursor_up (IBusEngine *engine)
1172 {
1173     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1174
1175     if (hangul->hanja_list != NULL) {
1176         ibus_lookup_table_cursor_up (hangul->table);
1177         ibus_hangul_engine_update_lookup_table_ui (hangul);
1178     }
1179
1180     parent_class->cursor_up (engine);
1181 }
1182
1183 static void
1184 ibus_hangul_engine_cursor_down (IBusEngine *engine)
1185 {
1186     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1187
1188     if (hangul->hanja_list != NULL) {
1189         ibus_lookup_table_cursor_down (hangul->table);
1190         ibus_hangul_engine_update_lookup_table_ui (hangul);
1191     }
1192
1193     parent_class->cursor_down (engine);
1194 }
1195
1196 static void
1197 ibus_hangul_engine_property_activate (IBusEngine    *engine,
1198                                       const gchar   *prop_name,
1199                                       guint          prop_state)
1200 {
1201     if (strcmp(prop_name, "setup") == 0) {
1202         GError *error = NULL;
1203         gchar *argv[2] = { NULL, };
1204
1205         argv[0] = "ibus-setup-hangul";
1206         argv[1] = NULL;
1207         g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error);
1208     } else if (strcmp(prop_name, "hanja_mode") == 0) {
1209         IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1210
1211         hangul->hanja_mode = !hangul->hanja_mode;
1212         if (hangul->hanja_mode) {
1213             ibus_property_set_state (hangul->prop_hanja_mode,
1214                     PROP_STATE_CHECKED);
1215         } else {
1216             ibus_property_set_state (hangul->prop_hanja_mode,
1217                     PROP_STATE_UNCHECKED);
1218         }
1219
1220         ibus_engine_update_property (engine, hangul->prop_hanja_mode);
1221         ibus_hangul_engine_flush (hangul);
1222     }
1223 }
1224
1225 static gboolean
1226 ibus_hangul_engine_has_preedit (IBusHangulEngine *hangul)
1227 {
1228     guint preedit_len;
1229     const ucschar *hic_preedit;
1230
1231     hic_preedit = hangul_ic_get_preedit_string (hangul->context);
1232     if (hic_preedit[0] != 0)
1233         return TRUE;
1234
1235     preedit_len = ustring_length (hangul->preedit);
1236     if (preedit_len > 0)
1237         return TRUE;
1238
1239     return FALSE;
1240 }
1241
1242 static bool
1243 ibus_hangul_engine_on_transition (HangulInputContext     *hic,
1244                                   ucschar                 c,
1245                                   const ucschar          *preedit,
1246                                   void                   *data)
1247 {
1248     if (!auto_reorder) {
1249         if (hangul_is_choseong (c)) {
1250             if (hangul_ic_has_jungseong (hic) || hangul_ic_has_jongseong (hic))
1251                 return false;
1252         }
1253
1254         if (hangul_is_jungseong (c)) {
1255             if (hangul_ic_has_jongseong (hic))
1256                 return false;
1257         }
1258     }
1259
1260     return true;
1261 }
1262
1263 static void
1264 ibus_config_value_changed (IBusConfig   *config,
1265                            const gchar  *section,
1266                            const gchar  *name,
1267                            GVariant     *value,
1268                            gpointer      user_data)
1269 {
1270     IBusHangulEngine *hangul = (IBusHangulEngine *) user_data;
1271
1272     if (strcmp(section, "engine/Hangul") == 0) {
1273         if (strcmp(name, "HangulKeyboard") == 0) {
1274             const gchar *str = g_variant_get_string(value, NULL);
1275             g_string_assign (hangul_keyboard, str);
1276             hangul_ic_select_keyboard (hangul->context, hangul_keyboard->str);
1277         } else if (strcmp(name, "HanjaKeys") == 0) {
1278             const gchar* str = g_variant_get_string(value, NULL);
1279             hanja_key_list_set_from_string(&hanja_keys, str);
1280         } else if (strcmp(name, "WordCommit") == 0) {
1281             word_commit = g_variant_get_boolean (value);
1282         } else if (strcmp (name, "AutoReorder") == 0) {
1283             auto_reorder = g_variant_get_boolean (value);
1284         }
1285     } else if (strcmp(section, "panel") == 0) {
1286         if (strcmp(name, "lookup_table_orientation") == 0) {
1287             lookup_table_orientation = g_variant_get_int32(value);
1288         }
1289     }
1290 }
1291
1292 static void
1293 lookup_table_set_visible (IBusLookupTable *table, gboolean flag)
1294 {
1295     g_object_set_data (G_OBJECT(table), "visible", GUINT_TO_POINTER(flag));
1296 }
1297
1298 static gboolean
1299 lookup_table_is_visible (IBusLookupTable *table)
1300 {
1301     gpointer res = g_object_get_data (G_OBJECT(table), "visible");
1302     return GPOINTER_TO_UINT(res);
1303 }
1304
1305 static void
1306 key_event_list_append(GArray* list, guint keyval, guint modifiers)
1307 {
1308     struct KeyEvent ev = { keyval, modifiers};
1309     g_array_append_val(list, ev);
1310 }
1311
1312 static gboolean
1313 key_event_list_match(GArray* list, guint keyval, guint modifiers)
1314 {
1315     guint i;
1316     guint mask;
1317
1318     /* ignore capslock and numlock */
1319     mask = IBUS_SHIFT_MASK |
1320            IBUS_CONTROL_MASK |
1321            IBUS_MOD1_MASK |
1322            IBUS_MOD3_MASK |
1323            IBUS_MOD4_MASK |
1324            IBUS_MOD5_MASK;
1325
1326     modifiers &= mask;
1327     for (i = 0; i < list->len; ++i) {
1328         struct KeyEvent* ev = &g_array_index(list, struct KeyEvent, i);
1329         if (ev->keyval == keyval && ev->modifiers == modifiers) {
1330             return TRUE;
1331         }
1332     }
1333
1334     return FALSE;
1335 }
1336
1337 static void
1338 ibus_hangul_engine_candidate_clicked (IBusEngine     *engine,
1339                                       guint           index,
1340                                       guint           button,
1341                                       guint           state)
1342 {
1343     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1344     if (hangul == NULL)
1345         return;
1346
1347     if (hangul->table == NULL)
1348         return;
1349
1350     ibus_lookup_table_set_cursor_pos (hangul->table, index);
1351     ibus_hangul_engine_commit_current_candidate (hangul);
1352
1353     if (hangul->hanja_mode) {
1354         ibus_hangul_engine_update_lookup_table (hangul);
1355     } else {
1356         ibus_hangul_engine_hide_lookup_table (hangul);
1357     }
1358 }
1359
1360 static void
1361 hanja_key_list_init(HanjaKeyList* list)
1362 {
1363     list->all_modifiers = 0;
1364     list->keys = g_array_sized_new(FALSE, TRUE, sizeof(struct KeyEvent), 4);
1365 }
1366
1367 static void
1368 hanja_key_list_fini(HanjaKeyList* list)
1369 {
1370     g_array_free(list->keys, TRUE);
1371 }
1372
1373 static void
1374 hanja_key_list_append_from_string(HanjaKeyList *list, const char* str)
1375 {
1376     guint keyval = 0;
1377     guint modifiers = 0;
1378     gboolean res;
1379
1380     res = ibus_key_event_from_string(str, &keyval, &modifiers);
1381     if (res) {
1382         hanja_key_list_append(list, keyval, modifiers);
1383     }
1384 }
1385
1386 static void
1387 hanja_key_list_append(HanjaKeyList *list, guint keyval, guint modifiers)
1388 {
1389     list->all_modifiers |= modifiers;
1390     key_event_list_append(list->keys, keyval, modifiers);
1391 }
1392
1393 static void
1394 hanja_key_list_set_from_string(HanjaKeyList *list, const char* str)
1395 {
1396     gchar** items = g_strsplit(str, ",", 0);
1397
1398     list->all_modifiers = 0;
1399     g_array_set_size(list->keys, 0);
1400
1401     if (items != NULL) {
1402         int i;
1403         for (i = 0; items[i] != NULL; ++i) {
1404             hanja_key_list_append_from_string(list, items[i]);
1405         }
1406         g_strfreev(items);
1407     }
1408 }
1409
1410 static gboolean
1411 hanja_key_list_match(HanjaKeyList* list, guint keyval, guint modifiers)
1412 {
1413     return key_event_list_match(list->keys, keyval, modifiers);
1414 }
1415
1416 static gboolean
1417 hanja_key_list_has_modifier(HanjaKeyList* list, guint keyval)
1418 {
1419     if (list->all_modifiers & IBUS_CONTROL_MASK) {
1420         if (keyval == IBUS_Control_L || keyval == IBUS_Control_R)
1421             return TRUE;
1422     }
1423
1424     if (list->all_modifiers & IBUS_MOD1_MASK) {
1425         if (keyval == IBUS_Alt_L || keyval == IBUS_Alt_R)
1426             return TRUE;
1427     }
1428
1429     if (list->all_modifiers & IBUS_SUPER_MASK) {
1430         if (keyval == IBUS_Super_L || keyval == IBUS_Super_R)
1431             return TRUE;
1432     }
1433
1434     if (list->all_modifiers & IBUS_HYPER_MASK) {
1435         if (keyval == IBUS_Hyper_L || keyval == IBUS_Hyper_R)
1436             return TRUE;
1437     }
1438
1439     if (list->all_modifiers & IBUS_META_MASK) {
1440         if (keyval == IBUS_Meta_L || keyval == IBUS_Meta_R)
1441             return TRUE;
1442     }
1443
1444     return FALSE;
1445 }