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