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