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