Modify process_key_event method and bump version.
[platform/upstream/ibus-hangul.git] / src / engine.c
1 /* vim:set et sts=4: */
2
3 #include <ibus.h>
4 #include <hangul.h>
5 #include <string.h>
6 #include "engine.h"
7
8 typedef struct _IBusHangulEngine IBusHangulEngine;
9 typedef struct _IBusHangulEngineClass IBusHangulEngineClass;
10
11 struct _IBusHangulEngine {
12         IBusEngine parent;
13
14     /* members */
15     HangulInputContext *context;
16     gboolean hangul_mode;
17     HanjaList* hanja_list;
18
19     IBusLookupTable *table;
20     IBusProperty    *hangul_mode_prop;
21     IBusPropList    *prop_list;
22 };
23
24 struct _IBusHangulEngineClass {
25         IBusEngineClass parent;
26 };
27
28 /* functions prototype */
29 static void     ibus_hangul_engine_class_init   (IBusHangulEngineClass  *klass);
30 static void     ibus_hangul_engine_init             (IBusHangulEngine           *hangul);
31 static GObject*
32             ibus_hangul_engine_constructor  (GType                   type,
33                                              guint                   n_construct_params,
34                                              GObjectConstructParam  *construct_params);
35 static void     ibus_hangul_engine_destroy              (IBusHangulEngine               *hangul);
36 static gboolean
37                         ibus_hangul_engine_process_key_event
38                                             (IBusEngine             *engine,
39                                              guint                       keyval,
40                                              guint                   keycode,
41                                              guint                       modifiers);
42 static void ibus_hangul_engine_focus_in     (IBusEngine             *engine);
43 static void ibus_hangul_engine_focus_out    (IBusEngine             *engine);
44 static void ibus_hangul_engine_reset        (IBusEngine             *engine);
45 static void ibus_hangul_engine_enable       (IBusEngine             *engine);
46 static void ibus_hangul_engine_disable      (IBusEngine             *engine);
47 #if 0
48 static void ibus_engine_set_cursor_location (IBusEngine             *engine,
49                                              gint                    x,
50                                              gint                    y,
51                                              gint                    w,
52                                              gint                    h);
53 static void ibus_hangul_engine_set_capabilities
54                                             (IBusEngine             *engine,
55                                              guint                   caps);
56 #endif
57 static void ibus_hangul_engine_page_up      (IBusEngine             *engine);
58 static void ibus_hangul_engine_page_down    (IBusEngine             *engine);
59 static void ibus_hangul_engine_cursor_up    (IBusEngine             *engine);
60 static void ibus_hangul_engine_cursor_down  (IBusEngine             *engine);
61 static void ibus_hangul_engine_property_activate
62                                             (IBusEngine             *engine,
63                                              const gchar            *prop_name,
64                                              guint                   prop_state);
65 #if 0
66 static void ibus_hangul_engine_property_show
67                                                                                         (IBusEngine             *engine,
68                                              const gchar            *prop_name);
69 static void ibus_hangul_engine_property_hide
70                                                                                         (IBusEngine             *engine,
71                                              const gchar            *prop_name);
72 #endif
73
74 static void ibus_hangul_engine_flush        (IBusHangulEngine       *hangul);
75 static void ibus_hangul_engine_update_preedit_text
76                                             (IBusHangulEngine       *hangul);
77 static void ibus_config_value_changed       (IBusConfig             *config,
78                                              const gchar            *section,
79                                              const gchar            *name,
80                                              GValue                 *value,
81                                              gpointer                user_data);
82
83 static IBusEngineClass *parent_class = NULL;
84 static HanjaTable *hanja_table = NULL;
85 static IBusConfig *config = NULL;
86 static GString    *hangul_keyboard;
87
88 GType
89 ibus_hangul_engine_get_type (void)
90 {
91         static GType type = 0;
92
93         static const GTypeInfo type_info = {
94                 sizeof (IBusHangulEngineClass),
95                 (GBaseInitFunc)         NULL,
96                 (GBaseFinalizeFunc) NULL,
97                 (GClassInitFunc)        ibus_hangul_engine_class_init,
98                 NULL,
99                 NULL,
100                 sizeof (IBusHangulEngine),
101                 0,
102                 (GInstanceInitFunc)     ibus_hangul_engine_init,
103         };
104
105         if (type == 0) {
106                 type = g_type_register_static (IBUS_TYPE_ENGINE,
107                                                                            "IBusHangulEngine",
108                                                                            &type_info,
109                                                                            (GTypeFlags) 0);
110         }
111
112         return type;
113 }
114
115 void
116 ibus_hangul_init (IBusBus *bus)
117 {
118     gboolean res;
119     GValue value = { 0, };
120
121     hanja_table = hanja_table_load (NULL);
122
123     config = ibus_bus_get_config (bus);
124
125     hangul_keyboard = g_string_new_len ("2", 8);
126     res = ibus_config_get_value (config, "engine/Hangul",
127                                          "HangulKeyboard", &value);
128     if (res) {
129         const gchar* str = g_value_get_string (&value);
130         g_string_assign (hangul_keyboard, str);
131     }
132 }
133
134 void
135 ibus_hangul_exit (void)
136 {
137     hanja_table_delete (hanja_table);
138     hanja_table = NULL;
139
140     g_object_unref (config);
141     config = NULL;
142
143     g_string_free (hangul_keyboard, TRUE);
144     hangul_keyboard = NULL;
145 }
146
147 static void
148 ibus_hangul_engine_class_init (IBusHangulEngineClass *klass)
149 {
150     GObjectClass *object_class = G_OBJECT_CLASS (klass);
151         IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
152         IBusEngineClass *engine_class = IBUS_ENGINE_CLASS (klass);
153
154         parent_class = (IBusEngineClass *) g_type_class_peek_parent (klass);
155
156     object_class->constructor = ibus_hangul_engine_constructor;
157         ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_hangul_engine_destroy;
158
159     engine_class->process_key_event = ibus_hangul_engine_process_key_event;
160
161     engine_class->reset = ibus_hangul_engine_reset;
162     engine_class->enable = ibus_hangul_engine_enable;
163     engine_class->disable = ibus_hangul_engine_disable;
164
165     engine_class->focus_in = ibus_hangul_engine_focus_in;
166     engine_class->focus_out = ibus_hangul_engine_focus_out;
167
168     engine_class->page_up = ibus_hangul_engine_page_up;
169     engine_class->page_down = ibus_hangul_engine_page_down;
170
171     engine_class->cursor_up = ibus_hangul_engine_cursor_up;
172     engine_class->cursor_down = ibus_hangul_engine_cursor_down;
173
174     engine_class->property_activate = ibus_hangul_engine_property_activate;
175 }
176
177 static void
178 ibus_hangul_engine_init (IBusHangulEngine *hangul)
179 {
180     IBusProperty* prop;
181     IBusText* label;
182     IBusText* tooltip;
183
184     hangul->context = hangul_ic_new (hangul_keyboard->str);
185     hangul->hanja_list = NULL;
186     hangul->hangul_mode = TRUE;
187     label = ibus_text_new_from_string("Setup");
188     tooltip = ibus_text_new_from_string("Configure hangul engine");
189     prop = ibus_property_new ("setup",
190                               PROP_TYPE_NORMAL,
191                               label,
192                               "gtk-preferences",
193                               tooltip,
194                               TRUE, TRUE, 0, NULL);
195     g_object_unref (label);
196     g_object_unref (tooltip);
197
198     hangul->prop_list = ibus_prop_list_new ();
199     ibus_prop_list_append (hangul->prop_list,  prop);
200
201     hangul->table = ibus_lookup_table_new (9, 0, TRUE, FALSE);
202
203     g_signal_connect (config, "value-changed",
204                       G_CALLBACK(ibus_config_value_changed), hangul);
205 }
206
207 static GObject*
208 ibus_hangul_engine_constructor (GType                   type,
209                                 guint                   n_construct_params,
210                                 GObjectConstructParam  *construct_params)
211 {
212     IBusHangulEngine *hangul;
213
214     hangul = (IBusHangulEngine *) G_OBJECT_CLASS (parent_class)->constructor (type,
215                                                        n_construct_params,
216                                                        construct_params);
217
218     return (GObject *)hangul;
219 }
220
221
222 static void
223 ibus_hangul_engine_destroy (IBusHangulEngine *hangul)
224 {
225     if (hangul->prop_list) {
226         g_object_unref (hangul->prop_list);
227         hangul->prop_list = NULL;
228     }
229
230     if (hangul->hangul_mode_prop) {
231         g_object_unref (hangul->hangul_mode_prop);
232         hangul->hangul_mode_prop = NULL;
233     }
234
235     if (hangul->table) {
236         g_object_unref (hangul->table);
237         hangul->table = NULL;
238     }
239
240     if (hangul->context) {
241         hangul_ic_delete (hangul->context);
242         hangul->context = NULL;
243     }
244
245         IBUS_OBJECT_CLASS (parent_class)->destroy ((IBusObject *)hangul);
246 }
247
248 static void
249 ibus_hangul_engine_update_preedit_text (IBusHangulEngine *hangul)
250 {
251     const gunichar *str;
252     IBusText *text;
253
254     str = hangul_ic_get_preedit_string (hangul->context);
255
256     if (str != NULL && str[0] != 0) {
257         text = ibus_text_new_from_ucs4 (str);
258         ibus_text_append_attribute (text, IBUS_ATTR_TYPE_FOREGROUND, 0x00ffffff, 0, -1);
259         ibus_text_append_attribute (text, IBUS_ATTR_TYPE_BACKGROUND, 0x00000000, 0, -1);
260         ibus_engine_update_preedit_text ((IBusEngine *)hangul,
261                                          text,
262                                          ibus_text_get_length (text),
263                                          TRUE);
264         g_object_unref (text);
265     }
266     else {
267         text = ibus_text_new_from_static_string ("");
268         ibus_engine_update_preedit_text ((IBusEngine *)hangul, text, 0, FALSE);
269         g_object_unref (text);
270     }
271 }
272
273 static void
274 ibus_hangul_engine_update_auxiliary_text (IBusHangulEngine *hangul)
275 {
276     guint cursor_pos;
277     const char* comment;
278     IBusText* text;
279
280     cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
281     comment = hanja_list_get_nth_comment (hangul->hanja_list, cursor_pos);
282
283     text = ibus_text_new_from_string (comment);
284     ibus_engine_update_auxiliary_text ((IBusEngine *)hangul, text, TRUE);
285     g_object_unref (text);
286 }
287
288 static void
289 ibus_hangul_engine_update_lookup_table (IBusHangulEngine *hangul)
290 {
291     ibus_engine_update_lookup_table ((IBusEngine *)hangul, hangul->table, TRUE);
292 }
293
294 static void
295 ibus_hangul_engine_commit_current_candidate (IBusHangulEngine *hangul)
296 {
297     guint cursor_pos;
298     const char* value;
299     IBusText* text;
300
301     hangul_ic_reset (hangul->context);
302     ibus_hangul_engine_update_preedit_text (hangul);
303
304     cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
305     value = hanja_list_get_nth_value (hangul->hanja_list, cursor_pos);
306
307     text = ibus_text_new_from_string (value);
308     ibus_engine_commit_text ((IBusEngine *)hangul, text);
309     g_object_unref (text);
310 }
311
312 static void
313 ibus_hangul_engine_open_lookup_table (IBusHangulEngine *hangul)
314 {
315     char* utf8;
316     const gunichar* str;
317
318     str = hangul_ic_get_preedit_string (hangul->context);
319     utf8 = g_ucs4_to_utf8 (str, -1, NULL, NULL, NULL);
320     if (utf8 != NULL) {
321         HanjaList* list = hanja_table_match_suffix (hanja_table, utf8);
322         if (list != NULL) {
323             int i, n;
324             n = hanja_list_get_size (list);
325
326             ibus_lookup_table_clear (hangul->table);
327             for (i = 0; i < n; i++) {
328                 const char* value = hanja_list_get_nth_value (list, i);
329                 IBusText* text = ibus_text_new_from_string (value);
330                 ibus_lookup_table_append_candidate (hangul->table, text);
331                 g_object_unref (text);
332             }
333
334             hanja_list_delete (hangul->hanja_list);
335             hangul->hanja_list = list;
336
337             ibus_lookup_table_set_cursor_pos (hangul->table, 0);
338             ibus_hangul_engine_update_auxiliary_text (hangul);
339             ibus_hangul_engine_update_lookup_table (hangul);
340         }
341         g_free (utf8);
342     }
343 }
344
345 static void
346 ibus_hangul_engine_close_lookup_table (IBusHangulEngine *hangul)
347 {
348     ibus_engine_hide_lookup_table ((IBusEngine *)hangul);
349     ibus_engine_hide_auxiliary_text ((IBusEngine *)hangul);
350     hanja_list_delete (hangul->hanja_list);
351     hangul->hanja_list = NULL;
352 }
353
354 static void
355 ibus_hangul_engine_toggle_lookup_table (IBusHangulEngine *hangul)
356 {
357     if (hangul->hanja_list != NULL) {
358         ibus_hangul_engine_close_lookup_table (hangul);
359     } else {
360         ibus_hangul_engine_open_lookup_table (hangul);
361     }
362 }
363
364 static gboolean
365 ibus_hangul_engine_process_candidate_key_event (IBusHangulEngine    *hangul,
366                                                 guint                keyval,
367                                                 guint                modifiers)
368 {
369     if (keyval == IBUS_Escape) {
370         ibus_hangul_engine_close_lookup_table (hangul);
371     } else if (keyval == IBUS_Return) {
372         ibus_hangul_engine_commit_current_candidate (hangul);
373         ibus_hangul_engine_close_lookup_table (hangul);
374     } else if (keyval >= IBUS_1 && keyval <= IBUS_9) {
375         guint page_no;
376         guint page_size;
377         guint cursor_pos;
378
379         page_size = ibus_lookup_table_get_page_size (hangul->table);
380         cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
381         page_no = cursor_pos / page_size;
382
383         cursor_pos = page_no * page_size + (keyval - IBUS_1);
384         ibus_lookup_table_set_cursor_pos (hangul->table, cursor_pos);
385
386         ibus_hangul_engine_commit_current_candidate (hangul);
387         ibus_hangul_engine_close_lookup_table (hangul);
388     } else if (keyval == IBUS_Left) {
389         ibus_lookup_table_cursor_up (hangul->table);
390         ibus_hangul_engine_update_lookup_table (hangul);
391         ibus_hangul_engine_update_auxiliary_text (hangul);
392     } else if (keyval == IBUS_Right) {
393         ibus_lookup_table_cursor_down (hangul->table);
394         ibus_hangul_engine_update_lookup_table (hangul);
395         ibus_hangul_engine_update_auxiliary_text (hangul);
396     } else if (keyval == IBUS_Up) {
397         ibus_lookup_table_page_up (hangul->table);
398         ibus_hangul_engine_update_lookup_table (hangul);
399         ibus_hangul_engine_update_auxiliary_text (hangul);
400     } else if (keyval == IBUS_Down) {
401         ibus_lookup_table_page_down (hangul->table);
402         ibus_hangul_engine_update_lookup_table (hangul);
403         ibus_hangul_engine_update_auxiliary_text (hangul);
404     } else if (keyval == IBUS_Page_Up) {
405         ibus_lookup_table_page_up (hangul->table);
406         ibus_hangul_engine_update_lookup_table (hangul);
407         ibus_hangul_engine_update_auxiliary_text (hangul);
408     } else if (keyval == IBUS_Page_Down) {
409         ibus_lookup_table_page_down (hangul->table);
410         ibus_hangul_engine_update_lookup_table (hangul);
411         ibus_hangul_engine_update_auxiliary_text (hangul);
412     } else if (keyval == IBUS_h) {
413         ibus_lookup_table_cursor_up (hangul->table);
414         ibus_hangul_engine_update_lookup_table (hangul);
415         ibus_hangul_engine_update_auxiliary_text (hangul);
416     } else if (keyval == IBUS_l) {
417         ibus_lookup_table_cursor_down (hangul->table);
418         ibus_hangul_engine_update_lookup_table (hangul);
419         ibus_hangul_engine_update_auxiliary_text (hangul);
420     } else if (keyval == IBUS_k) {
421         ibus_lookup_table_page_up (hangul->table);
422         ibus_hangul_engine_update_lookup_table (hangul);
423         ibus_hangul_engine_update_auxiliary_text (hangul);
424     } else if (keyval == IBUS_j) {
425         ibus_lookup_table_page_down (hangul->table);
426         ibus_hangul_engine_update_lookup_table (hangul);
427         ibus_hangul_engine_update_auxiliary_text (hangul);
428     }
429
430     return TRUE;
431 }
432
433 static gboolean
434 ibus_hangul_engine_process_key_event (IBusEngine     *engine,
435                                       guint           keyval,
436                                       guint           keycode,
437                                       guint           modifiers)
438 {
439     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
440
441     gboolean retval;
442     const gunichar *str;
443
444     if (modifiers & IBUS_RELEASE_MASK)
445         return FALSE;
446
447     // if we don't ignore shift keys, shift key will make flush the preedit 
448     // string. So you cannot input shift+key.
449     // Let's think about these examples:
450     //   dlTek (2 set)
451     //   qhRdmaqkq (2 set)
452     if (keyval == IBUS_Shift_L || keyval == IBUS_Shift_R)
453         return FALSE;
454
455     if (keyval == IBUS_F9 || keyval == IBUS_Hangul_Hanja) {
456         ibus_hangul_engine_toggle_lookup_table (hangul);
457         return TRUE;
458     }
459
460     if (modifiers & (IBUS_CONTROL_MASK | IBUS_MOD1_MASK))
461         return FALSE;
462
463     if (hangul->hanja_list != NULL) {
464         return ibus_hangul_engine_process_candidate_key_event (hangul,
465                      keyval, modifiers);
466     }
467
468     if (keyval == IBUS_BackSpace) {
469         retval = hangul_ic_backspace (hangul->context);
470     } else {
471         retval = hangul_ic_process (hangul->context, keyval);
472     }
473
474     str = hangul_ic_get_commit_string (hangul->context);
475     if (str != NULL && str[0] != 0) {
476         IBusText *text = ibus_text_new_from_ucs4 (str);
477         ibus_engine_commit_text ((IBusEngine *)hangul, text);
478         g_object_unref (text);
479     }
480
481     ibus_hangul_engine_update_preedit_text (hangul);
482
483     if (!retval)
484         ibus_hangul_engine_flush (hangul);
485
486     return retval;
487 }
488
489 static void
490 ibus_hangul_engine_flush (IBusHangulEngine *hangul)
491 {
492     const gunichar *str;
493     IBusText *text;
494
495     str = hangul_ic_flush (hangul->context);
496
497     if (str == NULL || str[0] == 0)
498         return;
499
500     text = ibus_text_new_from_ucs4 (str);
501
502     ibus_engine_hide_preedit_text ((IBusEngine *) hangul);
503     ibus_engine_commit_text ((IBusEngine *) hangul, text);
504
505     g_object_unref (text);
506 }
507
508 static void
509 ibus_hangul_engine_focus_in (IBusEngine *engine)
510 {
511     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
512
513     ibus_engine_register_properties (engine, hangul->prop_list);
514
515     if (hangul->hanja_list != NULL) {
516         ibus_hangul_engine_update_lookup_table (hangul);
517         ibus_hangul_engine_update_auxiliary_text (hangul);
518     }
519
520     parent_class->focus_in (engine);
521 }
522
523 static void
524 ibus_hangul_engine_focus_out (IBusEngine *engine)
525 {
526     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
527
528     if (hangul->hanja_list == NULL) {
529         ibus_hangul_engine_flush (hangul);
530     } else {
531         ibus_engine_hide_lookup_table (engine);
532         ibus_engine_hide_auxiliary_text (engine);
533     }
534
535     parent_class->focus_out ((IBusEngine *) hangul);
536 }
537
538 static void
539 ibus_hangul_engine_reset (IBusEngine *engine)
540 {
541     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
542
543     ibus_hangul_engine_flush (hangul);
544     if (hangul->hanja_list != NULL) {
545         ibus_hangul_engine_close_lookup_table (hangul);
546     }
547     parent_class->reset (engine);
548 }
549
550 static void
551 ibus_hangul_engine_enable (IBusEngine *engine)
552 {
553     parent_class->enable (engine);
554 }
555
556 static void
557 ibus_hangul_engine_disable (IBusEngine *engine)
558 {
559     ibus_hangul_engine_focus_out (engine);
560     parent_class->disable (engine);
561 }
562
563 static void
564 ibus_hangul_engine_page_up (IBusEngine *engine)
565 {
566     parent_class->page_up (engine);
567 }
568
569 static void
570 ibus_hangul_engine_page_down (IBusEngine *engine)
571 {
572     parent_class->page_down (engine);
573 }
574
575 static void
576 ibus_hangul_engine_cursor_up (IBusEngine *engine)
577 {
578     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
579
580     if (hangul->hanja_list != NULL) {
581         ibus_lookup_table_cursor_up (hangul->table);
582         ibus_hangul_engine_update_lookup_table (hangul);
583         ibus_hangul_engine_update_auxiliary_text (hangul);
584     }
585
586     parent_class->cursor_up (engine);
587 }
588
589 static void
590 ibus_hangul_engine_cursor_down (IBusEngine *engine)
591 {
592     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
593
594     if (hangul->hanja_list != NULL) {
595         ibus_lookup_table_cursor_down (hangul->table);
596         ibus_hangul_engine_update_lookup_table (hangul);
597         ibus_hangul_engine_update_auxiliary_text (hangul);
598     }
599
600     parent_class->cursor_down (engine);
601 }
602
603 static void
604 ibus_hangul_engine_property_activate (IBusEngine    *engine,
605                                       const gchar   *prop_name,
606                                       guint          prop_state)
607 {
608     if (strcmp(prop_name, "setup") == 0) {
609         GError *error = NULL;
610         gchar *argv[2] = { NULL, };
611         gchar *path;
612         const char* libexecdir;
613
614         libexecdir = g_getenv("LIBEXECDIR");
615         if (libexecdir == NULL)
616             libexecdir = LIBEXECDIR;
617
618         path = g_build_filename(libexecdir, "ibus-setup-hangul", NULL);
619         argv[0] = path;
620         argv[1] = NULL;
621         g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, NULL, &error);
622
623         g_free(path);
624     }
625 }
626
627 static void
628 ibus_config_value_changed (IBusConfig   *config,
629                            const gchar  *section,
630                            const gchar  *name,
631                            GValue       *value,
632                            gpointer      user_data)
633 {
634     IBusHangulEngine *hangul = (IBusHangulEngine *) user_data;
635
636     if (strcmp(section, "engine/Hangul") == 0) {
637         if (strcmp(name, "HangulKeyboard") == 0) {
638             const gchar *str = g_value_get_string (value);
639             g_string_assign (hangul_keyboard, str);
640             hangul_ic_select_keyboard (hangul->context, hangul_keyboard->str);
641         }
642     }
643 }