Add a new feature
[platform/upstream/ibus-hangul.git] / src / engine.c
1 /* vim:set et sts=4: */
2 #ifdef HAVE_CONFIG_H
3 #include <config.h>
4 #endif
5
6 #include <ibus.h>
7 #include <hangul.h>
8 #include <string.h>
9 #include <ctype.h>
10
11 #include "i18n.h"
12 #include "engine.h"
13 #include "ustring.h"
14
15
16 typedef struct _IBusHangulEngine IBusHangulEngine;
17 typedef struct _IBusHangulEngineClass IBusHangulEngineClass;
18
19 struct _IBusHangulEngine {
20     IBusEngine parent;
21
22     /* members */
23     HangulInputContext *context;
24     UString* preedit;
25     gboolean hangul_mode;
26     gboolean hanja_mode;
27     HanjaList* hanja_list;
28
29     IBusLookupTable *table;
30
31     IBusProperty    *prop_hanja_mode;
32     IBusPropList    *prop_list;
33 };
34
35 struct _IBusHangulEngineClass {
36     IBusEngineClass parent;
37 };
38
39 struct KeyEvent {
40     guint keyval;
41     guint modifiers;
42 };
43
44 /* functions prototype */
45 static void     ibus_hangul_engine_class_init
46                                             (IBusHangulEngineClass  *klass);
47 static void     ibus_hangul_engine_init     (IBusHangulEngine       *hangul);
48 static GObject*
49                 ibus_hangul_engine_constructor
50                                             (GType                   type,
51                                              guint                   n_construct_params,
52                                              GObjectConstructParam  *construct_params);
53 static void     ibus_hangul_engine_destroy  (IBusHangulEngine       *hangul);
54 static gboolean
55                 ibus_hangul_engine_process_key_event
56                                             (IBusEngine             *engine,
57                                              guint                   keyval,
58                                              guint                   keycode,
59                                              guint                   modifiers);
60 static void ibus_hangul_engine_focus_in     (IBusEngine             *engine);
61 static void ibus_hangul_engine_focus_out    (IBusEngine             *engine);
62 static void ibus_hangul_engine_reset        (IBusEngine             *engine);
63 static void ibus_hangul_engine_enable       (IBusEngine             *engine);
64 static void ibus_hangul_engine_disable      (IBusEngine             *engine);
65 #if 0
66 static void ibus_engine_set_cursor_location (IBusEngine             *engine,
67                                              gint                    x,
68                                              gint                    y,
69                                              gint                    w,
70                                              gint                    h);
71 static void ibus_hangul_engine_set_capabilities
72                                             (IBusEngine             *engine,
73                                              guint                   caps);
74 #endif
75 static void ibus_hangul_engine_page_up      (IBusEngine             *engine);
76 static void ibus_hangul_engine_page_down    (IBusEngine             *engine);
77 static void ibus_hangul_engine_cursor_up    (IBusEngine             *engine);
78 static void ibus_hangul_engine_cursor_down  (IBusEngine             *engine);
79 static void ibus_hangul_engine_property_activate
80                                             (IBusEngine             *engine,
81                                              const gchar            *prop_name,
82                                              guint                   prop_state);
83 #if 0
84 static void ibus_hangul_engine_property_show
85                                                                                         (IBusEngine             *engine,
86                                              const gchar            *prop_name);
87 static void ibus_hangul_engine_property_hide
88                                                                                         (IBusEngine             *engine,
89                                              const gchar            *prop_name);
90 #endif
91
92 static void ibus_hangul_engine_candidate_clicked
93                                             (IBusEngine             *engine,
94                                              guint                   index,
95                                              guint                   button,
96                                              guint                   state);
97
98 static void ibus_hangul_engine_flush        (IBusHangulEngine       *hangul);
99 static void ibus_hangul_engine_update_preedit_text
100                                             (IBusHangulEngine       *hangul);
101
102 static void ibus_hangul_engine_update_lookup_table
103                                             (IBusHangulEngine       *hangul);
104 static void ibus_config_value_changed       (IBusConfig             *config,
105                                              const gchar            *section,
106                                              const gchar            *name,
107                                              GValue                 *value,
108                                              gpointer                user_data);
109
110 static void        lookup_table_set_visible (IBusLookupTable        *table,
111                                              gboolean                flag);
112 static gboolean        lookup_table_is_visible
113                                             (IBusLookupTable        *table);
114
115 static void     key_event_list_set          (GArray                 *list,
116                                              const gchar            *str);
117 static gboolean key_event_list_match        (GArray                 *list,
118                                              guint                   keyval,
119                                              guint                   modifiers);
120
121 static IBusEngineClass *parent_class = NULL;
122 static HanjaTable *hanja_table = NULL;
123 static HanjaTable *symbol_table = NULL;
124 static IBusConfig *config = NULL;
125 static GString    *hangul_keyboard = NULL;
126 static GArray     *hanja_keys = NULL;
127 static int lookup_table_orientation = 0;
128
129 GType
130 ibus_hangul_engine_get_type (void)
131 {
132     static GType type = 0;
133
134     static const GTypeInfo type_info = {
135         sizeof (IBusHangulEngineClass),
136         (GBaseInitFunc)     NULL,
137         (GBaseFinalizeFunc) NULL,
138         (GClassInitFunc)    ibus_hangul_engine_class_init,
139         NULL,
140         NULL,
141         sizeof (IBusHangulEngine),
142         0,
143         (GInstanceInitFunc) ibus_hangul_engine_init,
144     };
145
146     if (type == 0) {
147             type = g_type_register_static (IBUS_TYPE_ENGINE,
148                                            "IBusHangulEngine",
149                                            &type_info,
150                                            (GTypeFlags) 0);
151     }
152
153     return type;
154 }
155
156 void
157 ibus_hangul_init (IBusBus *bus)
158 {
159     gboolean res;
160     GValue value = { 0, };
161
162     hanja_table = hanja_table_load (NULL);
163
164     symbol_table = hanja_table_load (IBUSHANGUL_DATADIR "/data/symbol.txt");
165
166     config = ibus_bus_get_config (bus);
167     if (config)
168         g_object_ref_sink (config);
169
170     hangul_keyboard = g_string_new_len ("2", 8);
171     res = ibus_config_get_value (config, "engine/Hangul",
172                                          "HangulKeyboard", &value);
173     if (res) {
174         const gchar* str = g_value_get_string (&value);
175         g_string_assign (hangul_keyboard, str);
176         g_value_unset(&value);
177     }
178
179     hanja_keys = g_array_sized_new(FALSE, TRUE, sizeof(struct KeyEvent), 4);
180     res = ibus_config_get_value (config, "engine/Hangul",
181                                          "HanjaKeys", &value);
182     if (res) {
183         const gchar* str = g_value_get_string (&value);
184         key_event_list_set(hanja_keys, str);
185         g_value_unset(&value);
186     } else {
187         struct KeyEvent ev;
188
189         ev.keyval = IBUS_Hangul_Hanja;
190         ev.modifiers = 0;
191         g_array_append_val(hanja_keys, ev);
192
193         ev.keyval = IBUS_F9;
194         ev.modifiers = 0;
195         g_array_append_val(hanja_keys, ev);
196     }
197 }
198
199 void
200 ibus_hangul_exit (void)
201 {
202     hanja_table_delete (hanja_table);
203     hanja_table = NULL;
204
205     hanja_table_delete (symbol_table);
206     symbol_table = NULL;
207
208     g_object_unref (config);
209     config = NULL;
210
211     g_string_free (hangul_keyboard, TRUE);
212     hangul_keyboard = NULL;
213 }
214
215 static void
216 ibus_hangul_engine_class_init (IBusHangulEngineClass *klass)
217 {
218     GObjectClass *object_class = G_OBJECT_CLASS (klass);
219     IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
220     IBusEngineClass *engine_class = IBUS_ENGINE_CLASS (klass);
221
222     parent_class = (IBusEngineClass *) g_type_class_peek_parent (klass);
223
224     object_class->constructor = ibus_hangul_engine_constructor;
225     ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_hangul_engine_destroy;
226
227     engine_class->process_key_event = ibus_hangul_engine_process_key_event;
228
229     engine_class->reset = ibus_hangul_engine_reset;
230     engine_class->enable = ibus_hangul_engine_enable;
231     engine_class->disable = ibus_hangul_engine_disable;
232
233     engine_class->focus_in = ibus_hangul_engine_focus_in;
234     engine_class->focus_out = ibus_hangul_engine_focus_out;
235
236     engine_class->page_up = ibus_hangul_engine_page_up;
237     engine_class->page_down = ibus_hangul_engine_page_down;
238
239     engine_class->cursor_up = ibus_hangul_engine_cursor_up;
240     engine_class->cursor_down = ibus_hangul_engine_cursor_down;
241
242     engine_class->property_activate = ibus_hangul_engine_property_activate;
243
244     engine_class->candidate_clicked = ibus_hangul_engine_candidate_clicked;
245 }
246
247 static void
248 ibus_hangul_engine_init (IBusHangulEngine *hangul)
249 {
250     IBusProperty* prop;
251     IBusText* label;
252     IBusText* tooltip;
253
254     hangul->context = hangul_ic_new (hangul_keyboard->str);
255     hangul->preedit = ustring_new();
256     hangul->hanja_list = NULL;
257     hangul->hangul_mode = TRUE;
258     hangul->hanja_mode = FALSE;
259
260     hangul->prop_list = ibus_prop_list_new ();
261     g_object_ref_sink (hangul->prop_list);
262
263     label = ibus_text_new_from_string (_("Hanja lock"));
264     tooltip = ibus_text_new_from_string (_("Enable/Disable Hanja mode"));
265     prop = ibus_property_new ("hanja_mode",
266                               PROP_TYPE_TOGGLE,
267                               label,
268                               NULL,
269                               tooltip,
270                               TRUE, TRUE, PROP_STATE_UNCHECKED, NULL);
271     g_object_ref_sink (prop);
272     ibus_prop_list_append (hangul->prop_list, prop);
273     hangul->prop_hanja_mode = prop;
274
275     label = ibus_text_new_from_string (_("Setup"));
276     tooltip = ibus_text_new_from_string (_("Configure hangul engine"));
277     prop = ibus_property_new ("setup",
278                               PROP_TYPE_NORMAL,
279                               label,
280                               "gtk-preferences",
281                               tooltip,
282                               TRUE, TRUE, PROP_STATE_UNCHECKED, NULL);
283     ibus_prop_list_append (hangul->prop_list, prop);
284
285     hangul->table = ibus_lookup_table_new (9, 0, TRUE, FALSE);
286     g_object_ref_sink (hangul->table);
287
288     g_signal_connect (config, "value-changed",
289                       G_CALLBACK(ibus_config_value_changed), hangul);
290 }
291
292 static GObject*
293 ibus_hangul_engine_constructor (GType                   type,
294                                 guint                   n_construct_params,
295                                 GObjectConstructParam  *construct_params)
296 {
297     IBusHangulEngine *hangul;
298
299     hangul = (IBusHangulEngine *) G_OBJECT_CLASS (parent_class)->constructor (type,
300                                                        n_construct_params,
301                                                        construct_params);
302
303     return (GObject *)hangul;
304 }
305
306
307 static void
308 ibus_hangul_engine_destroy (IBusHangulEngine *hangul)
309 {
310     if (hangul->prop_hanja_mode) {
311         g_object_unref (hangul->prop_hanja_mode);
312         hangul->prop_hanja_mode = NULL;
313     }
314
315     if (hangul->prop_list) {
316         g_object_unref (hangul->prop_list);
317         hangul->prop_list = NULL;
318     }
319
320     if (hangul->table) {
321         g_object_unref (hangul->table);
322         hangul->table = NULL;
323     }
324
325     if (hangul->context) {
326         hangul_ic_delete (hangul->context);
327         hangul->context = NULL;
328     }
329
330     IBUS_OBJECT_CLASS (parent_class)->destroy ((IBusObject *)hangul);
331 }
332
333 static void
334 ibus_hangul_engine_update_preedit_text (IBusHangulEngine *hangul)
335 {
336     const ucschar *hic_preedit;
337     IBusText *text;
338     UString *preedit;
339     gint preedit_len;
340
341     // ibus-hangul's preedit string is made up of ibus context's
342     // internal preedit string and libhangul's preedit string.
343     // libhangul only supports one syllable preedit string.
344     // In order to make longer preedit string, ibus-hangul maintains
345     // internal preedit string.
346     hic_preedit = hangul_ic_get_preedit_string (hangul->context);
347
348     preedit = ustring_dup (hangul->preedit);
349     preedit_len = ustring_length(preedit);
350     ustring_append_ucs4 (preedit, hic_preedit, -1);
351
352     if (ustring_length(preedit) > 0) {
353         text = ibus_text_new_from_ucs4 ((gunichar*)preedit->data);
354         // ibus-hangul's internal preedit string
355         ibus_text_append_attribute (text, IBUS_ATTR_TYPE_UNDERLINE,
356                 IBUS_ATTR_UNDERLINE_SINGLE, 0, preedit_len);
357         // Preedit string from libhangul context.
358         // This is currently composing syllable.
359         ibus_text_append_attribute (text, IBUS_ATTR_TYPE_FOREGROUND,
360                 0x00ffffff, preedit_len, -1);
361         ibus_text_append_attribute (text, IBUS_ATTR_TYPE_BACKGROUND,
362                 0x00000000, preedit_len, -1);
363         ibus_engine_update_preedit_text_with_mode ((IBusEngine *)hangul,
364                                                    text,
365                                                    ibus_text_get_length (text),
366                                                    TRUE,
367                                                    IBUS_ENGINE_PREEDIT_COMMIT);
368     } else {
369         text = ibus_text_new_from_static_string ("");
370         ibus_engine_update_preedit_text ((IBusEngine *)hangul, text, 0, FALSE);
371     }
372
373     ustring_delete(preedit);
374 }
375
376 static void
377 ibus_hangul_engine_update_lookup_table_ui (IBusHangulEngine *hangul)
378 {
379     guint cursor_pos;
380     const char* comment;
381     IBusText* text;
382
383     // update aux text
384     cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
385     comment = hanja_list_get_nth_comment (hangul->hanja_list, cursor_pos);
386
387     text = ibus_text_new_from_string (comment);
388     ibus_engine_update_auxiliary_text ((IBusEngine *)hangul, text, TRUE);
389
390     // update lookup table
391     ibus_engine_update_lookup_table ((IBusEngine *)hangul, hangul->table, TRUE);
392 }
393
394 static void
395 ibus_hangul_engine_commit_current_candidate (IBusHangulEngine *hangul)
396 {
397     guint cursor_pos;
398     const char* key;
399     const char* value;
400     int key_len;
401     int preedit_len;
402     int len;
403
404     IBusText* text;
405
406     cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
407     key = hanja_list_get_nth_key (hangul->hanja_list, cursor_pos);
408     value = hanja_list_get_nth_value (hangul->hanja_list, cursor_pos);
409
410     key_len = g_utf8_strlen(key, -1);
411     preedit_len = ustring_length(hangul->preedit);
412
413     len = MIN(key_len, preedit_len);
414     ustring_erase (hangul->preedit, 0, len);
415     if (key_len > preedit_len)
416         hangul_ic_reset (hangul->context);
417
418     ibus_hangul_engine_update_preedit_text (hangul);
419
420     text = ibus_text_new_from_string (value);
421     ibus_engine_commit_text ((IBusEngine *)hangul, text);
422 }
423
424 static void
425 ibus_hangul_engine_update_hanja_list (IBusHangulEngine *hangul)
426 {
427     char* utf8;
428     const ucschar* hic_preedit;
429     UString* preedit;
430
431     if (hangul->hanja_list != NULL) {
432         hanja_list_delete (hangul->hanja_list);
433         hangul->hanja_list = NULL;
434     }
435
436     hic_preedit = hangul_ic_get_preedit_string (hangul->context);
437
438     preedit = ustring_dup (hangul->preedit);
439     ustring_append_ucs4 (preedit, hic_preedit, -1);
440     if (ustring_length(preedit) > 0) {
441         utf8 = ustring_to_utf8 (preedit, -1);
442         if (utf8 != NULL) {
443             if (symbol_table != NULL)
444                 hangul->hanja_list = hanja_table_match_prefix (symbol_table, utf8);
445             if (hangul->hanja_list == NULL)
446                 hangul->hanja_list = hanja_table_match_prefix (hanja_table, utf8);
447             g_free (utf8);
448         }
449     }
450
451     ustring_delete (preedit);
452 }
453
454
455 static void
456 ibus_hangul_engine_apply_hanja_list (IBusHangulEngine *hangul)
457 {
458     HanjaList* list = hangul->hanja_list;
459     if (list != NULL) {
460         int i, n;
461         n = hanja_list_get_size (list);
462
463         ibus_lookup_table_clear (hangul->table);
464         for (i = 0; i < n; i++) {
465             const char* value = hanja_list_get_nth_value (list, i);
466             IBusText* text = ibus_text_new_from_string (value);
467             ibus_lookup_table_append_candidate (hangul->table, text);
468         }
469
470         ibus_lookup_table_set_cursor_pos (hangul->table, 0);
471         ibus_hangul_engine_update_lookup_table_ui (hangul);
472         lookup_table_set_visible (hangul->table, TRUE);
473     }
474 }
475
476 static void
477 ibus_hangul_engine_hide_lookup_table (IBusHangulEngine *hangul)
478 {
479     gboolean is_visible;
480     is_visible = lookup_table_is_visible (hangul->table);
481
482     // Sending hide lookup table message when the lookup table
483     // is not visible results wrong behavior. So I have to check
484     // whether the table is visible or not before to hide.
485     if (is_visible) {
486         ibus_engine_hide_lookup_table ((IBusEngine *)hangul);
487         ibus_engine_hide_auxiliary_text ((IBusEngine *)hangul);
488         lookup_table_set_visible (hangul->table, FALSE);
489     }
490
491     if (hangul->hanja_list != NULL) {
492         hanja_list_delete (hangul->hanja_list);
493         hangul->hanja_list = NULL;
494     }
495 }
496
497 static void
498 ibus_hangul_engine_update_lookup_table (IBusHangulEngine *hangul)
499 {
500     ibus_hangul_engine_update_hanja_list (hangul);
501
502     if (hangul->hanja_list != NULL) {
503         ibus_hangul_engine_apply_hanja_list (hangul);
504     } else {
505         ibus_hangul_engine_hide_lookup_table (hangul);
506     }
507 }
508
509 static gboolean
510 ibus_hangul_engine_process_candidate_key_event (IBusHangulEngine    *hangul,
511                                                 guint                keyval,
512                                                 guint                modifiers)
513 {
514     if (keyval == IBUS_Escape) {
515         ibus_hangul_engine_hide_lookup_table (hangul);
516         return TRUE;
517     } else if (keyval == IBUS_Return) {
518         ibus_hangul_engine_commit_current_candidate (hangul);
519
520         if (hangul->hanja_mode) {
521             ibus_hangul_engine_update_lookup_table (hangul);
522         } else {
523             ibus_hangul_engine_hide_lookup_table (hangul);
524         }
525         return TRUE;
526     } else if (keyval >= IBUS_1 && keyval <= IBUS_9) {
527         guint page_no;
528         guint page_size;
529         guint cursor_pos;
530
531         page_size = ibus_lookup_table_get_page_size (hangul->table);
532         cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
533         page_no = cursor_pos / page_size;
534
535         cursor_pos = page_no * page_size + (keyval - IBUS_1);
536         ibus_lookup_table_set_cursor_pos (hangul->table, cursor_pos);
537
538         ibus_hangul_engine_commit_current_candidate (hangul);
539
540         if (hangul->hanja_mode) {
541             ibus_hangul_engine_update_lookup_table (hangul);
542         } else {
543             ibus_hangul_engine_hide_lookup_table (hangul);
544         }
545         return TRUE;
546     } else if (keyval == IBUS_Page_Up) {
547         ibus_lookup_table_page_up (hangul->table);
548         ibus_hangul_engine_update_lookup_table_ui (hangul);
549         return TRUE;
550     } else if (keyval == IBUS_Page_Down) {
551         ibus_lookup_table_page_down (hangul->table);
552         ibus_hangul_engine_update_lookup_table_ui (hangul);
553         return TRUE;
554     } else {
555         if (lookup_table_orientation == 0) {
556             // horizontal
557             if (keyval == IBUS_Left) {
558                 ibus_lookup_table_cursor_up (hangul->table);
559                 ibus_hangul_engine_update_lookup_table_ui (hangul);
560                 return TRUE;
561             } else if (keyval == IBUS_Right) {
562                 ibus_lookup_table_cursor_down (hangul->table);
563                 ibus_hangul_engine_update_lookup_table_ui (hangul);
564                 return TRUE;
565             } else if (keyval == IBUS_Up) {
566                 ibus_lookup_table_page_up (hangul->table);
567                 ibus_hangul_engine_update_lookup_table_ui (hangul);
568                 return TRUE;
569             } else if (keyval == IBUS_Down) {
570                 ibus_lookup_table_page_down (hangul->table);
571                 ibus_hangul_engine_update_lookup_table_ui (hangul);
572                 return TRUE;
573             }
574         } else {
575             // vertical
576             if (keyval == IBUS_Left) {
577                 ibus_lookup_table_page_up (hangul->table);
578                 ibus_hangul_engine_update_lookup_table_ui (hangul);
579                 return TRUE;
580             } else if (keyval == IBUS_Right) {
581                 ibus_lookup_table_page_down (hangul->table);
582                 ibus_hangul_engine_update_lookup_table_ui (hangul);
583                 return TRUE;
584             } else if (keyval == IBUS_Up) {
585                 ibus_lookup_table_cursor_up (hangul->table);
586                 ibus_hangul_engine_update_lookup_table_ui (hangul);
587                 return TRUE;
588             } else if (keyval == IBUS_Down) {
589                 ibus_lookup_table_cursor_down (hangul->table);
590                 ibus_hangul_engine_update_lookup_table_ui (hangul);
591                 return TRUE;
592             }
593         }
594     }
595
596     if (!hangul->hanja_mode) {
597         if (lookup_table_orientation == 0) {
598             // horizontal
599             if (keyval == IBUS_h) {
600                 ibus_lookup_table_cursor_up (hangul->table);
601                 ibus_hangul_engine_update_lookup_table_ui (hangul);
602                 return TRUE;
603             } else if (keyval == IBUS_l) {
604                 ibus_lookup_table_cursor_down (hangul->table);
605                 ibus_hangul_engine_update_lookup_table_ui (hangul);
606                 return TRUE;
607             } else if (keyval == IBUS_k) {
608                 ibus_lookup_table_page_up (hangul->table);
609                 ibus_hangul_engine_update_lookup_table_ui (hangul);
610                 return TRUE;
611             } else if (keyval == IBUS_j) {
612                 ibus_lookup_table_page_down (hangul->table);
613                 ibus_hangul_engine_update_lookup_table_ui (hangul);
614                 return TRUE;
615             }
616         } else {
617             // vertical
618             if (keyval == IBUS_h) {
619                 ibus_lookup_table_page_up (hangul->table);
620                 ibus_hangul_engine_update_lookup_table_ui (hangul);
621                 return TRUE;
622             } else if (keyval == IBUS_l) {
623                 ibus_lookup_table_page_down (hangul->table);
624                 ibus_hangul_engine_update_lookup_table_ui (hangul);
625                 return TRUE;
626             } else if (keyval == IBUS_k) {
627                 ibus_lookup_table_cursor_up (hangul->table);
628                 ibus_hangul_engine_update_lookup_table_ui (hangul);
629                 return TRUE;
630             } else if (keyval == IBUS_j) {
631                 ibus_lookup_table_cursor_down (hangul->table);
632                 ibus_hangul_engine_update_lookup_table_ui (hangul);
633                 return TRUE;
634             }
635         }
636     }
637
638     return FALSE;
639 }
640
641 static gboolean
642 ibus_hangul_engine_process_key_event (IBusEngine     *engine,
643                                       guint           keyval,
644                                       guint           keycode,
645                                       guint           modifiers)
646 {
647     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
648
649     gboolean retval;
650     const ucschar *str;
651
652     if (modifiers & IBUS_RELEASE_MASK)
653         return FALSE;
654
655     // if we don't ignore shift keys, shift key will make flush the preedit 
656     // string. So you cannot input shift+key.
657     // Let's think about these examples:
658     //   dlTek (2 set)
659     //   qhRdmaqkq (2 set)
660     if (keyval == IBUS_Shift_L || keyval == IBUS_Shift_R)
661         return FALSE;
662
663     if (key_event_list_match(hanja_keys, keyval, modifiers)) {
664         if (hangul->hanja_list == NULL) {
665             ibus_hangul_engine_update_lookup_table (hangul);
666         } else {
667             ibus_hangul_engine_hide_lookup_table (hangul);
668         }
669         return TRUE;
670     }
671
672     if (modifiers & (IBUS_CONTROL_MASK | IBUS_MOD1_MASK))
673         return FALSE;
674
675     if (hangul->hanja_list != NULL) {
676         retval = ibus_hangul_engine_process_candidate_key_event (hangul,
677                      keyval, modifiers);
678         if (hangul->hanja_mode) {
679             if (retval)
680                 return TRUE;
681         } else {
682             return TRUE;
683         }
684     }
685
686     if (keyval == IBUS_BackSpace) {
687         retval = hangul_ic_backspace (hangul->context);
688     } else {
689         // ignore capslock
690         if (modifiers & IBUS_LOCK_MASK) {
691             if (keyval >= 'A' && keyval <= 'z') {
692                 if (isupper(keyval))
693                     keyval = tolower(keyval);
694                 else
695                     keyval = toupper(keyval);
696             }
697         }
698         retval = hangul_ic_process (hangul->context, keyval);
699     }
700
701     str = hangul_ic_get_commit_string (hangul->context);
702     if (hangul->hanja_mode) {
703         const ucschar* hic_preedit;
704
705         hic_preedit = hangul_ic_get_preedit_string (hangul->context);
706         if (hic_preedit != NULL && hic_preedit[0] != 0) {
707             ustring_append_ucs4 (hangul->preedit, str, -1);
708         } else {
709             IBusText *text;
710             const ucschar* preedit;
711
712             ustring_append_ucs4 (hangul->preedit, str, -1);
713             if (ustring_length (hangul->preedit) > 0) {
714                 preedit = ustring_begin (hangul->preedit);
715                 text = ibus_text_new_from_ucs4 ((gunichar*)preedit);
716                 ibus_engine_commit_text (engine, text);
717             }
718             ustring_clear (hangul->preedit);
719         }
720     } else {
721         if (str != NULL && str[0] != 0) {
722             IBusText *text = ibus_text_new_from_ucs4 (str);
723             ibus_engine_commit_text (engine, text);
724         }
725     }
726
727     ibus_hangul_engine_update_preedit_text (hangul);
728
729     if (hangul->hanja_mode) {
730         ibus_hangul_engine_update_lookup_table (hangul);
731     }
732
733     if (!retval)
734         ibus_hangul_engine_flush (hangul);
735
736     return retval;
737 }
738
739 static void
740 ibus_hangul_engine_flush (IBusHangulEngine *hangul)
741 {
742     const gunichar *str;
743     IBusText *text;
744
745     ibus_hangul_engine_hide_lookup_table (hangul);
746
747     str = hangul_ic_flush (hangul->context);
748
749     ustring_append_ucs4 (hangul->preedit, str, -1);
750
751     if (ustring_length (hangul->preedit) == 0)
752         return;
753
754     str = ustring_begin (hangul->preedit);
755     text = ibus_text_new_from_ucs4 (str);
756
757     ibus_engine_hide_preedit_text ((IBusEngine *) hangul);
758     // Use ibus_engine_update_preedit_text_with_mode instead.
759     //ibus_engine_commit_text ((IBusEngine *) hangul, text);
760
761     ustring_clear(hangul->preedit);
762 }
763
764 static void
765 ibus_hangul_engine_focus_in (IBusEngine *engine)
766 {
767     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
768
769     if (hangul->hanja_mode) {
770         hangul->prop_hanja_mode->state = PROP_STATE_CHECKED;
771     } else {
772         hangul->prop_hanja_mode->state = PROP_STATE_UNCHECKED;
773     }
774
775     ibus_engine_register_properties (engine, hangul->prop_list);
776
777     if (hangul->hanja_list != NULL) {
778         ibus_hangul_engine_update_lookup_table_ui (hangul);
779     }
780
781     parent_class->focus_in (engine);
782 }
783
784 static void
785 ibus_hangul_engine_focus_out (IBusEngine *engine)
786 {
787     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
788
789     if (hangul->hanja_list == NULL) {
790         ibus_hangul_engine_flush (hangul);
791     } else {
792         ibus_engine_hide_lookup_table (engine);
793         ibus_engine_hide_auxiliary_text (engine);
794     }
795
796     parent_class->focus_out ((IBusEngine *) hangul);
797 }
798
799 static void
800 ibus_hangul_engine_reset (IBusEngine *engine)
801 {
802     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
803
804     ibus_hangul_engine_flush (hangul);
805     if (hangul->hanja_list != NULL) {
806         ibus_hangul_engine_hide_lookup_table (hangul);
807     }
808     parent_class->reset (engine);
809 }
810
811 static void
812 ibus_hangul_engine_enable (IBusEngine *engine)
813 {
814     parent_class->enable (engine);
815 }
816
817 static void
818 ibus_hangul_engine_disable (IBusEngine *engine)
819 {
820     ibus_hangul_engine_focus_out (engine);
821     parent_class->disable (engine);
822 }
823
824 static void
825 ibus_hangul_engine_page_up (IBusEngine *engine)
826 {
827     parent_class->page_up (engine);
828 }
829
830 static void
831 ibus_hangul_engine_page_down (IBusEngine *engine)
832 {
833     parent_class->page_down (engine);
834 }
835
836 static void
837 ibus_hangul_engine_cursor_up (IBusEngine *engine)
838 {
839     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
840
841     if (hangul->hanja_list != NULL) {
842         ibus_lookup_table_cursor_up (hangul->table);
843         ibus_hangul_engine_update_lookup_table_ui (hangul);
844     }
845
846     parent_class->cursor_up (engine);
847 }
848
849 static void
850 ibus_hangul_engine_cursor_down (IBusEngine *engine)
851 {
852     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
853
854     if (hangul->hanja_list != NULL) {
855         ibus_lookup_table_cursor_down (hangul->table);
856         ibus_hangul_engine_update_lookup_table_ui (hangul);
857     }
858
859     parent_class->cursor_down (engine);
860 }
861
862 static void
863 ibus_hangul_engine_property_activate (IBusEngine    *engine,
864                                       const gchar   *prop_name,
865                                       guint          prop_state)
866 {
867     if (strcmp(prop_name, "setup") == 0) {
868         GError *error = NULL;
869         gchar *argv[2] = { NULL, };
870         gchar *path;
871         const char* libexecdir;
872
873         libexecdir = g_getenv("LIBEXECDIR");
874         if (libexecdir == NULL)
875             libexecdir = LIBEXECDIR;
876
877         path = g_build_filename(libexecdir, "ibus-setup-hangul", NULL);
878         argv[0] = path;
879         argv[1] = NULL;
880         g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, NULL, &error);
881
882         g_free(path);
883     } else if (strcmp(prop_name, "hanja_mode") == 0) {
884         IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
885
886         hangul->hanja_mode = !hangul->hanja_mode;
887         if (hangul->hanja_mode) {
888             hangul->prop_hanja_mode->state = PROP_STATE_CHECKED;
889         } else {
890             hangul->prop_hanja_mode->state = PROP_STATE_UNCHECKED;
891         }
892
893         ibus_engine_update_property (engine, hangul->prop_hanja_mode);
894         ibus_hangul_engine_flush (hangul);
895     }
896 }
897
898 static void
899 ibus_config_value_changed (IBusConfig   *config,
900                            const gchar  *section,
901                            const gchar  *name,
902                            GValue       *value,
903                            gpointer      user_data)
904 {
905     IBusHangulEngine *hangul = (IBusHangulEngine *) user_data;
906
907     if (strcmp(section, "engine/Hangul") == 0) {
908         if (strcmp(name, "HangulKeyboard") == 0) {
909             const gchar *str = g_value_get_string (value);
910             g_string_assign (hangul_keyboard, str);
911             hangul_ic_select_keyboard (hangul->context, hangul_keyboard->str);
912         } else if (strcmp(name, "HanjaKeys") == 0) {
913             const gchar* str = g_value_get_string (value);
914             key_event_list_set(hanja_keys, str);
915         }
916     } else if (strcmp(section, "panel") == 0) {
917         if (strcmp(name, "lookup_table_orientation") == 0) {
918             lookup_table_orientation = g_value_get_int (value);
919         }
920     }
921 }
922
923 static void
924 lookup_table_set_visible (IBusLookupTable *table, gboolean flag)
925 {
926     g_object_set_data (G_OBJECT(table), "visible", GUINT_TO_POINTER(flag));
927 }
928
929 static gboolean
930 lookup_table_is_visible (IBusLookupTable *table)
931 {
932     gpointer res = g_object_get_data (G_OBJECT(table), "visible");
933     return GPOINTER_TO_UINT(res);
934 }
935
936 static void
937 key_event_list_set (GArray* list, const char* str)
938 {
939     gchar** items = g_strsplit(str, ",", 0);
940
941     g_array_set_size(list, 0);
942
943     if (items != NULL) {
944         int i;
945         for (i = 0; items[i] != NULL; ++i) {
946             guint keyval = 0;
947             guint modifiers = 0;
948             gboolean res;
949             res = ibus_key_event_from_string(items[i], &keyval, &modifiers);
950             if (res) {
951                 struct KeyEvent ev = { keyval, modifiers };
952                 g_array_append_val(list, ev);
953             }
954         }
955         g_strfreev(items);
956     }
957 }
958
959 static gboolean
960 key_event_list_match(GArray* list, guint keyval, guint modifiers)
961 {
962     guint i;
963     guint mask;
964
965     /* ignore capslock and numlock */
966     mask = IBUS_SHIFT_MASK |
967            IBUS_CONTROL_MASK |
968            IBUS_MOD1_MASK |
969            IBUS_MOD3_MASK |
970            IBUS_MOD4_MASK |
971            IBUS_MOD5_MASK;
972
973     modifiers &= mask;
974     for (i = 0; i < list->len; ++i) {
975         struct KeyEvent* ev = &g_array_index(list, struct KeyEvent, i);
976         if (ev->keyval == keyval && ev->modifiers == modifiers) {
977             return TRUE;
978         }
979     }
980
981     return FALSE;
982 }
983
984 static void
985 ibus_hangul_engine_candidate_clicked (IBusEngine     *engine,
986                                       guint           index,
987                                       guint           button,
988                                       guint           state)
989 {
990     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
991     if (hangul == NULL)
992         return;
993
994     if (hangul->table == NULL)
995         return;
996
997     ibus_lookup_table_set_cursor_pos (hangul->table, index);
998     ibus_hangul_engine_commit_current_candidate (hangul);
999
1000     if (hangul->hanja_mode) {
1001         ibus_hangul_engine_update_lookup_table (hangul);
1002     } else {
1003         ibus_hangul_engine_hide_lookup_table (hangul);
1004     }
1005 }