Update IBusConfig routine: issue #304
[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                       modifiers);
41 static void ibus_hangul_engine_focus_in     (IBusEngine             *engine);
42 static void ibus_hangul_engine_focus_out    (IBusEngine             *engine);
43 static void ibus_hangul_engine_reset        (IBusEngine             *engine);
44 static void ibus_hangul_engine_enable       (IBusEngine             *engine);
45 static void ibus_hangul_engine_disable      (IBusEngine             *engine);
46 #if 0
47 static void ibus_engine_set_cursor_location (IBusEngine             *engine,
48                                              gint                    x,
49                                              gint                    y,
50                                              gint                    w,
51                                              gint                    h);
52 static void ibus_hangul_engine_set_capabilities
53                                             (IBusEngine             *engine,
54                                              guint                   caps);
55 #endif
56 static void ibus_hangul_engine_page_up      (IBusEngine             *engine);
57 static void ibus_hangul_engine_page_down    (IBusEngine             *engine);
58 static void ibus_hangul_engine_cursor_up    (IBusEngine             *engine);
59 static void ibus_hangul_engine_cursor_down  (IBusEngine             *engine);
60 static void ibus_hangul_engine_toggle_hangul_mode
61                                             (IBusHangulEngine       *hangul);
62 #if 0
63 static void ibus_hangul_property_activate   (IBusEngine             *engine,
64                                              const gchar            *prop_name,
65                                              gint                    prop_state);
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
175 static void
176 ibus_hangul_engine_init (IBusHangulEngine *hangul)
177 {
178     hangul->context = hangul_ic_new (hangul_keyboard->str);
179     hangul->hanja_list = NULL;
180     hangul->hangul_mode = TRUE;
181     hangul->hangul_mode_prop = ibus_property_new ("hangul_mode_prop",
182                                            PROP_TYPE_NORMAL,
183                                            NULL,
184                                            NULL,
185                                            NULL,
186                                            TRUE,
187                                            FALSE,
188                                            0,
189                                            NULL);
190
191     hangul->prop_list = ibus_prop_list_new ();
192     ibus_prop_list_append (hangul->prop_list,  hangul->hangul_mode_prop);
193
194     hangul->table = ibus_lookup_table_new (9, 0, TRUE, FALSE);
195
196     g_signal_connect (config, "value-changed",
197                       G_CALLBACK(ibus_config_value_changed), hangul);
198 }
199
200 static GObject*
201 ibus_hangul_engine_constructor (GType                   type,
202                                 guint                   n_construct_params,
203                                 GObjectConstructParam  *construct_params)
204 {
205     IBusHangulEngine *hangul;
206
207     hangul = (IBusHangulEngine *) G_OBJECT_CLASS (parent_class)->constructor (type,
208                                                        n_construct_params,
209                                                        construct_params);
210
211     return (GObject *)hangul;
212 }
213
214
215 static void
216 ibus_hangul_engine_destroy (IBusHangulEngine *hangul)
217 {
218     if (hangul->prop_list) {
219         g_object_unref (hangul->prop_list);
220         hangul->prop_list = NULL;
221     }
222
223     if (hangul->hangul_mode_prop) {
224         g_object_unref (hangul->hangul_mode_prop);
225         hangul->hangul_mode_prop = NULL;
226     }
227
228     if (hangul->table) {
229         g_object_unref (hangul->table);
230         hangul->table = NULL;
231     }
232
233     if (hangul->context) {
234         hangul_ic_delete (hangul->context);
235         hangul->context = NULL;
236     }
237
238         IBUS_OBJECT_CLASS (parent_class)->destroy ((IBusObject *)hangul);
239 }
240
241 static void
242 ibus_hangul_engine_update_preedit_text (IBusHangulEngine *hangul)
243 {
244     const gunichar *str;
245     IBusText *text;
246
247     str = hangul_ic_get_preedit_string (hangul->context);
248
249     if (str != NULL && str[0] != 0) {
250         text = ibus_text_new_from_ucs4 (str);
251         ibus_text_append_attribute (text, IBUS_ATTR_TYPE_FOREGROUND, 0x00ffffff, 0, -1);
252         ibus_text_append_attribute (text, IBUS_ATTR_TYPE_BACKGROUND, 0x00000000, 0, -1);
253         ibus_engine_update_preedit_text ((IBusEngine *)hangul,
254                                          text,
255                                          ibus_text_get_length (text),
256                                          TRUE);
257         g_object_unref (text);
258     }
259     else {
260         text = ibus_text_new_from_static_string ("");
261         ibus_engine_update_preedit_text ((IBusEngine *)hangul, text, 0, FALSE);
262         g_object_unref (text);
263     }
264 }
265
266 static void
267 ibus_hangul_engine_update_auxiliary_text (IBusHangulEngine *hangul)
268 {
269     guint cursor_pos;
270     const char* comment;
271     IBusText* text;
272
273     cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
274     comment = hanja_list_get_nth_comment (hangul->hanja_list, cursor_pos);
275
276     text = ibus_text_new_from_string (comment);
277     ibus_engine_update_auxiliary_text ((IBusEngine *)hangul, text, TRUE);
278     g_object_unref (text);
279 }
280
281 static void
282 ibus_hangul_engine_update_lookup_table (IBusHangulEngine *hangul)
283 {
284     ibus_engine_update_lookup_table ((IBusEngine *)hangul, hangul->table, TRUE);
285 }
286
287 static void
288 ibus_hangul_engine_commit_current_candidate (IBusHangulEngine *hangul)
289 {
290     guint cursor_pos;
291     const char* value;
292     IBusText* text;
293
294     hangul_ic_reset (hangul->context);
295     ibus_hangul_engine_update_preedit_text (hangul);
296
297     cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
298     value = hanja_list_get_nth_value (hangul->hanja_list, cursor_pos);
299
300     text = ibus_text_new_from_string (value);
301     ibus_engine_commit_text ((IBusEngine *)hangul, text);
302     g_object_unref (text);
303 }
304
305 static void
306 ibus_hangul_engine_open_lookup_table (IBusHangulEngine *hangul)
307 {
308     char* utf8;
309     const gunichar* str;
310
311     str = hangul_ic_get_preedit_string (hangul->context);
312     utf8 = g_ucs4_to_utf8 (str, -1, NULL, NULL, NULL);
313     if (utf8 != NULL) {
314         HanjaList* list = hanja_table_match_suffix (hanja_table, utf8);
315         if (list != NULL) {
316             int i, n;
317             n = hanja_list_get_size (list);
318
319             ibus_lookup_table_clear (hangul->table);
320             for (i = 0; i < n; i++) {
321                 const char* value = hanja_list_get_nth_value (list, i);
322                 IBusText* text = ibus_text_new_from_string (value);
323                 ibus_lookup_table_append_candidate (hangul->table, text);
324                 g_object_unref (text);
325             }
326
327             hanja_list_delete (hangul->hanja_list);
328             hangul->hanja_list = list;
329
330             ibus_lookup_table_set_cursor_pos (hangul->table, 0);
331             ibus_hangul_engine_update_auxiliary_text (hangul);
332             ibus_hangul_engine_update_lookup_table (hangul);
333         }
334         g_free (utf8);
335     }
336 }
337
338 static void
339 ibus_hangul_engine_close_lookup_table (IBusHangulEngine *hangul)
340 {
341     ibus_engine_hide_lookup_table ((IBusEngine *)hangul);
342     ibus_engine_hide_auxiliary_text ((IBusEngine *)hangul);
343     hanja_list_delete (hangul->hanja_list);
344     hangul->hanja_list = NULL;
345 }
346
347 static void
348 ibus_hangul_engine_toggle_lookup_table (IBusHangulEngine *hangul)
349 {
350     if (hangul->hanja_list != NULL) {
351         ibus_hangul_engine_close_lookup_table (hangul);
352     } else {
353         ibus_hangul_engine_open_lookup_table (hangul);
354     }
355 }
356
357 static gboolean
358 ibus_hangul_engine_process_candidate_key_event (IBusHangulEngine    *hangul,
359                                                 guint                keyval,
360                                                 guint                modifiers)
361 {
362     if (keyval == IBUS_Escape) {
363         ibus_hangul_engine_close_lookup_table (hangul);
364     } else if (keyval == IBUS_Return) {
365         ibus_hangul_engine_commit_current_candidate (hangul);
366         ibus_hangul_engine_close_lookup_table (hangul);
367     } else if (keyval >= IBUS_1 && keyval <= IBUS_9) {
368         guint page_no;
369         guint page_size;
370         guint cursor_pos;
371
372         page_size = ibus_lookup_table_get_page_size (hangul->table);
373         cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
374         page_no = cursor_pos / page_size;
375
376         cursor_pos = page_no * page_size + (keyval - IBUS_1);
377         ibus_lookup_table_set_cursor_pos (hangul->table, cursor_pos);
378
379         ibus_hangul_engine_commit_current_candidate (hangul);
380         ibus_hangul_engine_close_lookup_table (hangul);
381     } else if (keyval == IBUS_Left) {
382         ibus_lookup_table_cursor_up (hangul->table);
383         ibus_hangul_engine_update_lookup_table (hangul);
384         ibus_hangul_engine_update_auxiliary_text (hangul);
385     } else if (keyval == IBUS_Right) {
386         ibus_lookup_table_cursor_down (hangul->table);
387         ibus_hangul_engine_update_lookup_table (hangul);
388         ibus_hangul_engine_update_auxiliary_text (hangul);
389     } else if (keyval == IBUS_Up) {
390         ibus_lookup_table_page_up (hangul->table);
391         ibus_hangul_engine_update_lookup_table (hangul);
392         ibus_hangul_engine_update_auxiliary_text (hangul);
393     } else if (keyval == IBUS_Down) {
394         ibus_lookup_table_page_down (hangul->table);
395         ibus_hangul_engine_update_lookup_table (hangul);
396         ibus_hangul_engine_update_auxiliary_text (hangul);
397     } else if (keyval == IBUS_Page_Up) {
398         ibus_lookup_table_page_up (hangul->table);
399         ibus_hangul_engine_update_lookup_table (hangul);
400         ibus_hangul_engine_update_auxiliary_text (hangul);
401     } else if (keyval == IBUS_Page_Down) {
402         ibus_lookup_table_page_down (hangul->table);
403         ibus_hangul_engine_update_lookup_table (hangul);
404         ibus_hangul_engine_update_auxiliary_text (hangul);
405     } else if (keyval == IBUS_h) {
406         ibus_lookup_table_cursor_up (hangul->table);
407         ibus_hangul_engine_update_lookup_table (hangul);
408         ibus_hangul_engine_update_auxiliary_text (hangul);
409     } else if (keyval == IBUS_l) {
410         ibus_lookup_table_cursor_down (hangul->table);
411         ibus_hangul_engine_update_lookup_table (hangul);
412         ibus_hangul_engine_update_auxiliary_text (hangul);
413     } else if (keyval == IBUS_k) {
414         ibus_lookup_table_page_up (hangul->table);
415         ibus_hangul_engine_update_lookup_table (hangul);
416         ibus_hangul_engine_update_auxiliary_text (hangul);
417     } else if (keyval == IBUS_j) {
418         ibus_lookup_table_page_down (hangul->table);
419         ibus_hangul_engine_update_lookup_table (hangul);
420         ibus_hangul_engine_update_auxiliary_text (hangul);
421     }
422
423     return TRUE;
424 }
425
426 static gboolean
427 ibus_hangul_engine_process_key_event (IBusEngine     *engine,
428                                       guint           keyval,
429                                       guint           modifiers)
430 {
431     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
432
433     gboolean retval;
434     const gunichar *str;
435
436     if (modifiers & IBUS_RELEASE_MASK)
437         return FALSE;
438
439     // if we don't ignore shift keys, shift key will make flush the preedit 
440     // string. So you cannot input shift+key.
441     // Let's think about these examples:
442     //   dlTek (2 set)
443     //   qhRdmaqkq (2 set)
444     if (keyval == IBUS_Shift_L || keyval == IBUS_Shift_R)
445         return FALSE;
446
447     if (keyval == IBUS_F9 || keyval == IBUS_Hangul_Hanja) {
448         ibus_hangul_engine_toggle_lookup_table (hangul);
449         return TRUE;
450     }
451
452     if (modifiers & (IBUS_CONTROL_MASK | IBUS_MOD1_MASK))
453         return FALSE;
454
455     if (hangul->hanja_list != NULL) {
456         return ibus_hangul_engine_process_candidate_key_event (hangul,
457                      keyval, modifiers);
458     }
459
460     if (keyval == IBUS_BackSpace) {
461         retval = hangul_ic_backspace (hangul->context);
462     } else {
463         retval = hangul_ic_process (hangul->context, keyval);
464     }
465
466     str = hangul_ic_get_commit_string (hangul->context);
467     if (str != NULL && str[0] != 0) {
468         IBusText *text = ibus_text_new_from_ucs4 (str);
469         ibus_engine_commit_text ((IBusEngine *)hangul, text);
470         g_object_unref (text);
471     }
472
473     ibus_hangul_engine_update_preedit_text (hangul);
474
475     if (!retval)
476         ibus_hangul_engine_flush (hangul);
477
478     return retval;
479 }
480
481 static void
482 ibus_hangul_engine_flush (IBusHangulEngine *hangul)
483 {
484     const gunichar *str;
485     IBusText *text;
486
487     str = hangul_ic_flush (hangul->context);
488
489     if (str == NULL || str[0] == 0)
490         return;
491
492     text = ibus_text_new_from_ucs4 (str);
493
494     ibus_engine_hide_preedit_text ((IBusEngine *) hangul);
495     ibus_engine_commit_text ((IBusEngine *) hangul, text);
496
497     g_object_unref (text);
498 }
499
500 static void
501 ibus_hangul_engine_toggle_hangul_mode (IBusHangulEngine *hangul)
502 {
503     IBusText *text;
504     hangul->hangul_mode = ! hangul->hangul_mode;
505
506     ibus_hangul_engine_flush (hangul);
507
508     if (hangul->hangul_mode) {
509         text = ibus_text_new_from_static_string ("한");
510     }
511     else {
512         text = ibus_text_new_from_static_string ("A");
513     }
514
515     ibus_property_set_label (hangul->hangul_mode_prop, text);
516     ibus_engine_update_property ((IBusEngine *)hangul, hangul->hangul_mode_prop);
517     g_object_unref (text);
518 }
519
520 static void
521 ibus_hangul_engine_focus_in (IBusEngine *engine)
522 {
523     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
524
525     ibus_engine_register_properties (engine, hangul->prop_list);
526
527     if (hangul->hanja_list != NULL) {
528         ibus_hangul_engine_update_lookup_table (hangul);
529         ibus_hangul_engine_update_auxiliary_text (hangul);
530     }
531
532     parent_class->focus_in (engine);
533 }
534
535 static void
536 ibus_hangul_engine_focus_out (IBusEngine *engine)
537 {
538     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
539
540     if (hangul->hanja_list == NULL) {
541         ibus_hangul_engine_flush (hangul);
542     } else {
543         ibus_engine_hide_lookup_table (engine);
544         ibus_engine_hide_auxiliary_text (engine);
545     }
546
547     parent_class->focus_out ((IBusEngine *) hangul);
548 }
549
550 static void
551 ibus_hangul_engine_reset (IBusEngine *engine)
552 {
553     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
554
555     ibus_hangul_engine_flush (hangul);
556     if (hangul->hanja_list != NULL) {
557         ibus_hangul_engine_close_lookup_table (hangul);
558     }
559     parent_class->reset (engine);
560 }
561
562 static void
563 ibus_hangul_engine_enable (IBusEngine *engine)
564 {
565     parent_class->enable (engine);
566 }
567
568 static void
569 ibus_hangul_engine_disable (IBusEngine *engine)
570 {
571     ibus_hangul_engine_focus_out (engine);
572     parent_class->disable (engine);
573 }
574
575 static void
576 ibus_hangul_engine_page_up (IBusEngine *engine)
577 {
578     parent_class->page_up (engine);
579 }
580
581 static void
582 ibus_hangul_engine_page_down (IBusEngine *engine)
583 {
584     parent_class->page_down (engine);
585 }
586
587 static void
588 ibus_hangul_engine_cursor_up (IBusEngine *engine)
589 {
590     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
591
592     if (hangul->hanja_list != NULL) {
593         ibus_lookup_table_cursor_up (hangul->table);
594         ibus_hangul_engine_update_lookup_table (hangul);
595         ibus_hangul_engine_update_auxiliary_text (hangul);
596     }
597
598     parent_class->cursor_up (engine);
599 }
600
601 static void
602 ibus_hangul_engine_cursor_down (IBusEngine *engine)
603 {
604     IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
605
606     if (hangul->hanja_list != NULL) {
607         ibus_lookup_table_cursor_down (hangul->table);
608         ibus_hangul_engine_update_lookup_table (hangul);
609         ibus_hangul_engine_update_auxiliary_text (hangul);
610     }
611
612     parent_class->cursor_down (engine);
613 }
614
615 static void
616 ibus_config_value_changed (IBusConfig   *config,
617                            const gchar  *section,
618                            const gchar  *name,
619                            GValue       *value,
620                            gpointer      user_data)
621 {
622     IBusHangulEngine *hangul = (IBusHangulEngine *) user_data;
623
624     if (strcmp(section, "engine/Hangul") == 0) {
625         if (strcmp(name, "HangulKeyboard") == 0) {
626             const gchar *str = g_value_get_string (value);
627             g_string_assign (hangul_keyboard, str);
628             hangul_ic_select_keyboard (hangul->context, hangul_keyboard->str);
629         }
630     }
631 }