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