1 /* vim:set et sts=4: */
2 /* ibus-hangul - The Hangul Engine For IBus
3 * Copyright (C) 2008-2009 Peng Huang <shawn.p.huang@gmail.com>
4 * Copyright (C) 2009-2011 Choe Hwanjin <choe.hwanjin@gmail.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
35 typedef struct _IBusHangulEngine IBusHangulEngine;
36 typedef struct _IBusHangulEngineClass IBusHangulEngineClass;
38 typedef struct _HanjaKeyList HanjaKeyList;
40 struct _IBusHangulEngine {
44 HangulInputContext *context;
48 HanjaList* hanja_list;
49 int last_lookup_method;
51 IBusLookupTable *table;
53 IBusProperty *prop_hanja_mode;
54 IBusPropList *prop_list;
57 struct _IBusHangulEngineClass {
58 IBusEngineClass parent;
66 struct _HanjaKeyList {
77 /* functions prototype */
78 static void ibus_hangul_engine_class_init
79 (IBusHangulEngineClass *klass);
80 static void ibus_hangul_engine_init (IBusHangulEngine *hangul);
82 ibus_hangul_engine_constructor
84 guint n_construct_params,
85 GObjectConstructParam *construct_params);
86 static void ibus_hangul_engine_destroy (IBusHangulEngine *hangul);
88 ibus_hangul_engine_process_key_event
93 static void ibus_hangul_engine_focus_in (IBusEngine *engine);
94 static void ibus_hangul_engine_focus_out (IBusEngine *engine);
95 static void ibus_hangul_engine_reset (IBusEngine *engine);
96 static void ibus_hangul_engine_enable (IBusEngine *engine);
97 static void ibus_hangul_engine_disable (IBusEngine *engine);
99 static void ibus_engine_set_cursor_location (IBusEngine *engine,
104 static void ibus_hangul_engine_set_capabilities
108 static void ibus_hangul_engine_page_up (IBusEngine *engine);
109 static void ibus_hangul_engine_page_down (IBusEngine *engine);
110 static void ibus_hangul_engine_cursor_up (IBusEngine *engine);
111 static void ibus_hangul_engine_cursor_down (IBusEngine *engine);
112 static void ibus_hangul_engine_property_activate
114 const gchar *prop_name,
117 static void ibus_hangul_engine_property_show
119 const gchar *prop_name);
120 static void ibus_hangul_engine_property_hide
122 const gchar *prop_name);
125 static void ibus_hangul_engine_candidate_clicked
131 static void ibus_hangul_engine_flush (IBusHangulEngine *hangul);
132 static void ibus_hangul_engine_clear_preedit_text
133 (IBusHangulEngine *hangul);
134 static void ibus_hangul_engine_update_preedit_text
135 (IBusHangulEngine *hangul);
137 static void ibus_hangul_engine_update_lookup_table
138 (IBusHangulEngine *hangul);
139 static gboolean ibus_hangul_engine_has_preedit
140 (IBusHangulEngine *hangul);
141 static bool ibus_hangul_engine_on_transition
142 (HangulInputContext *hic,
144 const ucschar *preedit,
147 static void ibus_config_value_changed (IBusConfig *config,
148 const gchar *section,
153 static void lookup_table_set_visible (IBusLookupTable *table,
155 static gboolean lookup_table_is_visible
156 (IBusLookupTable *table);
158 static gboolean key_event_list_match (GArray *list,
162 static void hanja_key_list_init (HanjaKeyList *list);
163 static void hanja_key_list_fini (HanjaKeyList *list);
164 static void hanja_key_list_set_from_string(HanjaKeyList *list,
166 static void hanja_key_list_append (HanjaKeyList *list,
169 static gboolean hanja_key_list_match (HanjaKeyList *list,
172 static gboolean hanja_key_list_has_modifier (HanjaKeyList *list,
175 static glong ucschar_strlen (const ucschar* str);
177 static IBusEngineClass *parent_class = NULL;
178 static HanjaTable *hanja_table = NULL;
179 static HanjaTable *symbol_table = NULL;
180 static IBusConfig *config = NULL;
181 static GString *hangul_keyboard = NULL;
182 static HanjaKeyList hanja_keys;
183 static int lookup_table_orientation = 0;
184 static IBusKeymap *keymap = NULL;
185 static gboolean word_commit = FALSE;
186 static gboolean auto_reorder = TRUE;
189 ucschar_strlen (const ucschar* str)
191 const ucschar* p = str;
198 ibus_hangul_engine_get_type (void)
200 static GType type = 0;
202 static const GTypeInfo type_info = {
203 sizeof (IBusHangulEngineClass),
204 (GBaseInitFunc) NULL,
205 (GBaseFinalizeFunc) NULL,
206 (GClassInitFunc) ibus_hangul_engine_class_init,
209 sizeof (IBusHangulEngine),
211 (GInstanceInitFunc) ibus_hangul_engine_init,
215 type = g_type_register_static (IBUS_TYPE_ENGINE,
225 ibus_hangul_init (IBusBus *bus)
229 hanja_table = hanja_table_load (NULL);
231 symbol_table = hanja_table_load (IBUSHANGUL_DATADIR "/data/symbol.txt");
233 config = ibus_bus_get_config (bus);
235 g_object_ref_sink (config);
237 hangul_keyboard = g_string_new_len ("2", 8);
238 value = ibus_config_get_value (config, "engine/Hangul",
241 const gchar* str = g_variant_get_string (value, NULL);
242 g_string_assign (hangul_keyboard, str);
243 g_variant_unref(value);
246 hanja_key_list_init(&hanja_keys);
248 value = ibus_config_get_value (config, "engine/Hangul",
251 const gchar* str = g_variant_get_string (value, NULL);
252 hanja_key_list_set_from_string(&hanja_keys, str);
253 g_variant_unref(value);
255 hanja_key_list_append(&hanja_keys, IBUS_Hangul_Hanja, 0);
256 hanja_key_list_append(&hanja_keys, IBUS_F9, 0);
259 value = ibus_config_get_value (config, "engine/Hangul",
262 word_commit = g_variant_get_boolean (value);
263 g_variant_unref(value);
266 value = ibus_config_get_value (config, "engine/Hangul", "AutoReorder");
268 auto_reorder = g_variant_get_boolean (value);
269 g_variant_unref (value);
272 keymap = ibus_keymap_get("us");
276 ibus_hangul_exit (void)
278 if (keymap != NULL) {
279 g_object_unref(keymap);
283 hanja_key_list_fini(&hanja_keys);
285 hanja_table_delete (hanja_table);
288 hanja_table_delete (symbol_table);
291 g_object_unref (config);
294 g_string_free (hangul_keyboard, TRUE);
295 hangul_keyboard = NULL;
299 ibus_hangul_engine_class_init (IBusHangulEngineClass *klass)
301 GObjectClass *object_class = G_OBJECT_CLASS (klass);
302 IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
303 IBusEngineClass *engine_class = IBUS_ENGINE_CLASS (klass);
305 parent_class = (IBusEngineClass *) g_type_class_peek_parent (klass);
307 object_class->constructor = ibus_hangul_engine_constructor;
308 ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_hangul_engine_destroy;
310 engine_class->process_key_event = ibus_hangul_engine_process_key_event;
312 engine_class->reset = ibus_hangul_engine_reset;
313 engine_class->enable = ibus_hangul_engine_enable;
314 engine_class->disable = ibus_hangul_engine_disable;
316 engine_class->focus_in = ibus_hangul_engine_focus_in;
317 engine_class->focus_out = ibus_hangul_engine_focus_out;
319 engine_class->page_up = ibus_hangul_engine_page_up;
320 engine_class->page_down = ibus_hangul_engine_page_down;
322 engine_class->cursor_up = ibus_hangul_engine_cursor_up;
323 engine_class->cursor_down = ibus_hangul_engine_cursor_down;
325 engine_class->property_activate = ibus_hangul_engine_property_activate;
327 engine_class->candidate_clicked = ibus_hangul_engine_candidate_clicked;
331 ibus_hangul_engine_init (IBusHangulEngine *hangul)
337 hangul->context = hangul_ic_new (hangul_keyboard->str);
338 hangul_ic_connect_callback (hangul->context, "transition",
339 ibus_hangul_engine_on_transition, hangul);
341 hangul->preedit = ustring_new();
342 hangul->hanja_list = NULL;
343 hangul->hangul_mode = TRUE;
344 hangul->hanja_mode = FALSE;
345 hangul->last_lookup_method = LOOKUP_METHOD_PREFIX;
347 hangul->prop_list = ibus_prop_list_new ();
348 g_object_ref_sink (hangul->prop_list);
350 label = ibus_text_new_from_string (_("Hanja lock"));
351 tooltip = ibus_text_new_from_string (_("Enable/Disable Hanja mode"));
352 prop = ibus_property_new ("hanja_mode",
357 TRUE, TRUE, PROP_STATE_UNCHECKED, NULL);
358 g_object_ref_sink (prop);
359 ibus_prop_list_append (hangul->prop_list, prop);
360 hangul->prop_hanja_mode = prop;
362 label = ibus_text_new_from_string (_("Setup"));
363 tooltip = ibus_text_new_from_string (_("Configure hangul engine"));
364 prop = ibus_property_new ("setup",
369 TRUE, TRUE, PROP_STATE_UNCHECKED, NULL);
370 ibus_prop_list_append (hangul->prop_list, prop);
372 hangul->table = ibus_lookup_table_new (9, 0, TRUE, FALSE);
373 g_object_ref_sink (hangul->table);
375 g_signal_connect (config, "value-changed",
376 G_CALLBACK(ibus_config_value_changed), hangul);
380 ibus_hangul_engine_constructor (GType type,
381 guint n_construct_params,
382 GObjectConstructParam *construct_params)
384 IBusHangulEngine *hangul;
386 hangul = (IBusHangulEngine *) G_OBJECT_CLASS (parent_class)->constructor (type,
390 return (GObject *)hangul;
395 ibus_hangul_engine_destroy (IBusHangulEngine *hangul)
397 if (hangul->prop_hanja_mode) {
398 g_object_unref (hangul->prop_hanja_mode);
399 hangul->prop_hanja_mode = NULL;
402 if (hangul->prop_list) {
403 g_object_unref (hangul->prop_list);
404 hangul->prop_list = NULL;
408 g_object_unref (hangul->table);
409 hangul->table = NULL;
412 if (hangul->context) {
413 hangul_ic_delete (hangul->context);
414 hangul->context = NULL;
417 IBUS_OBJECT_CLASS (parent_class)->destroy ((IBusObject *)hangul);
421 ibus_hangul_engine_clear_preedit_text (IBusHangulEngine *hangul)
425 text = ibus_text_new_from_static_string ("");
426 ibus_engine_update_preedit_text ((IBusEngine *)hangul, text, 0, FALSE);
430 ibus_hangul_engine_update_preedit_text (IBusHangulEngine *hangul)
432 const ucschar *hic_preedit;
437 // ibus-hangul's preedit string is made up of ibus context's
438 // internal preedit string and libhangul's preedit string.
439 // libhangul only supports one syllable preedit string.
440 // In order to make longer preedit string, ibus-hangul maintains
441 // internal preedit string.
442 hic_preedit = hangul_ic_get_preedit_string (hangul->context);
444 preedit = ustring_dup (hangul->preedit);
445 preedit_len = ustring_length(preedit);
446 ustring_append_ucs4 (preedit, hic_preedit, -1);
448 if (ustring_length(preedit) > 0) {
449 IBusPreeditFocusMode preedit_option = IBUS_ENGINE_PREEDIT_COMMIT;
451 if (hangul->hanja_list != NULL)
452 preedit_option = IBUS_ENGINE_PREEDIT_CLEAR;
454 text = ibus_text_new_from_ucs4 ((gunichar*)preedit->data);
455 // ibus-hangul's internal preedit string
456 ibus_text_append_attribute (text, IBUS_ATTR_TYPE_UNDERLINE,
457 IBUS_ATTR_UNDERLINE_SINGLE, 0, preedit_len);
458 // Preedit string from libhangul context.
459 // This is currently composing syllable.
460 ibus_text_append_attribute (text, IBUS_ATTR_TYPE_FOREGROUND,
461 0x00ffffff, preedit_len, -1);
462 ibus_text_append_attribute (text, IBUS_ATTR_TYPE_BACKGROUND,
463 0x00000000, preedit_len, -1);
464 ibus_engine_update_preedit_text_with_mode ((IBusEngine *)hangul,
466 ibus_text_get_length (text),
470 text = ibus_text_new_from_static_string ("");
471 ibus_engine_update_preedit_text ((IBusEngine *)hangul, text, 0, FALSE);
474 ustring_delete(preedit);
478 ibus_hangul_engine_update_lookup_table_ui (IBusHangulEngine *hangul)
485 cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
486 comment = hanja_list_get_nth_comment (hangul->hanja_list, cursor_pos);
488 text = ibus_text_new_from_string (comment);
489 ibus_engine_update_auxiliary_text ((IBusEngine *)hangul, text, TRUE);
491 // update lookup table
492 ibus_engine_update_lookup_table ((IBusEngine *)hangul, hangul->table, TRUE);
496 ibus_hangul_engine_commit_current_candidate (IBusHangulEngine *hangul)
501 const ucschar* hic_preedit;
503 glong hic_preedit_len;
508 cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
509 key = hanja_list_get_nth_key (hangul->hanja_list, cursor_pos);
510 value = hanja_list_get_nth_value (hangul->hanja_list, cursor_pos);
511 hic_preedit = hangul_ic_get_preedit_string (hangul->context);
513 key_len = g_utf8_strlen(key, -1);
514 preedit_len = ustring_length(hangul->preedit);
515 hic_preedit_len = ucschar_strlen (hic_preedit);
517 if (hangul->last_lookup_method == LOOKUP_METHOD_PREFIX) {
518 if (preedit_len == 0 && hic_preedit_len == 0) {
519 /* remove surrounding_text */
521 ibus_engine_delete_surrounding_text ((IBusEngine *)hangul,
525 /* remove ibus preedit text */
527 glong n = MIN(key_len, preedit_len);
528 ustring_erase (hangul->preedit, 0, n);
529 key_len -= preedit_len;
532 /* remove hic preedit text */
534 hangul_ic_reset (hangul->context);
535 key_len -= hic_preedit_len;
539 /* remove hic preedit text */
540 if (hic_preedit_len > 0) {
541 hangul_ic_reset (hangul->context);
542 key_len -= hic_preedit_len;
545 /* remove ibus preedit text */
546 if (key_len > preedit_len) {
547 ustring_erase (hangul->preedit, 0, preedit_len);
548 key_len -= preedit_len;
549 } else if (key_len > 0) {
550 ustring_erase (hangul->preedit, 0, key_len);
554 /* remove surrounding_text */
556 ibus_engine_delete_surrounding_text ((IBusEngine *)hangul,
561 /* clear preedit text before commit */
562 ibus_hangul_engine_clear_preedit_text (hangul);
564 text = ibus_text_new_from_string (value);
565 ibus_engine_commit_text ((IBusEngine *)hangul, text);
567 ibus_hangul_engine_update_preedit_text (hangul);
571 h_ibus_text_get_substring (IBusText* ibus_text, glong p1, glong p2)
581 text = ibus_text_get_text (ibus_text);
582 limit = ibus_text_get_length (ibus_text) + 1;
583 if (text == NULL || limit == 0)
595 begin = g_utf8_offset_to_pointer (text, pos);
596 end = g_utf8_offset_to_pointer (begin, n);
598 substring = g_strndup (begin, end - begin);
603 ibus_hangul_engine_lookup_hanja_table (const char* key, int method)
611 case LOOKUP_METHOD_EXACT:
612 if (symbol_table != NULL)
613 list = hanja_table_match_exact (symbol_table, key);
616 list = hanja_table_match_exact (hanja_table, key);
619 case LOOKUP_METHOD_PREFIX:
620 if (symbol_table != NULL)
621 list = hanja_table_match_prefix (symbol_table, key);
624 list = hanja_table_match_prefix (hanja_table, key);
627 case LOOKUP_METHOD_SUFFIX:
628 if (symbol_table != NULL)
629 list = hanja_table_match_suffix (symbol_table, key);
632 list = hanja_table_match_suffix (hanja_table, key);
641 ibus_hangul_engine_update_hanja_list (IBusHangulEngine *hangul)
645 const ucschar* hic_preedit;
648 IBusText* ibus_text = NULL;
649 guint cursor_pos = 0;
650 guint anchor_pos = 0;
652 if (hangul->hanja_list != NULL) {
653 hanja_list_delete (hangul->hanja_list);
654 hangul->hanja_list = NULL;
657 hic_preedit = hangul_ic_get_preedit_string (hangul->context);
660 lookup_method = LOOKUP_METHOD_PREFIX;
662 preedit = ustring_dup (hangul->preedit);
663 ustring_append_ucs4 (preedit, hic_preedit, -1);
665 if (ustring_length(preedit) > 0) {
666 preedit_utf8 = ustring_to_utf8 (preedit, -1);
667 if (word_commit || hangul->hanja_mode) {
668 hanja_key = preedit_utf8;
669 lookup_method = LOOKUP_METHOD_PREFIX;
672 ibus_engine_get_surrounding_text ((IBusEngine *)hangul, &ibus_text,
673 &cursor_pos, &anchor_pos);
675 substr = h_ibus_text_get_substring (ibus_text,
676 cursor_pos - 64, cursor_pos);
678 if (substr != NULL) {
679 hanja_key = g_strconcat (substr, preedit_utf8, NULL);
680 g_free (preedit_utf8);
682 hanja_key = preedit_utf8;
684 lookup_method = LOOKUP_METHOD_SUFFIX;
687 ibus_engine_get_surrounding_text ((IBusEngine *)hangul, &ibus_text,
688 &cursor_pos, &anchor_pos);
689 if (cursor_pos != anchor_pos) {
690 // If we have selection in surrounding text, we use that.
691 hanja_key = h_ibus_text_get_substring (ibus_text,
692 cursor_pos, anchor_pos);
693 lookup_method = LOOKUP_METHOD_EXACT;
695 hanja_key = h_ibus_text_get_substring (ibus_text,
696 cursor_pos - 64, cursor_pos);
697 lookup_method = LOOKUP_METHOD_SUFFIX;
701 if (hanja_key != NULL) {
702 hangul->hanja_list = ibus_hangul_engine_lookup_hanja_table (hanja_key,
704 hangul->last_lookup_method = lookup_method;
708 ustring_delete (preedit);
710 if (ibus_text != NULL)
711 g_object_unref (ibus_text);
715 ibus_hangul_engine_apply_hanja_list (IBusHangulEngine *hangul)
717 HanjaList* list = hangul->hanja_list;
720 n = hanja_list_get_size (list);
722 ibus_lookup_table_clear (hangul->table);
723 for (i = 0; i < n; i++) {
724 const char* value = hanja_list_get_nth_value (list, i);
725 IBusText* text = ibus_text_new_from_string (value);
726 ibus_lookup_table_append_candidate (hangul->table, text);
729 ibus_lookup_table_set_cursor_pos (hangul->table, 0);
730 ibus_hangul_engine_update_lookup_table_ui (hangul);
731 lookup_table_set_visible (hangul->table, TRUE);
736 ibus_hangul_engine_hide_lookup_table (IBusHangulEngine *hangul)
739 is_visible = lookup_table_is_visible (hangul->table);
741 // Sending hide lookup table message when the lookup table
742 // is not visible results wrong behavior. So I have to check
743 // whether the table is visible or not before to hide.
745 ibus_engine_hide_lookup_table ((IBusEngine *)hangul);
746 ibus_engine_hide_auxiliary_text ((IBusEngine *)hangul);
747 lookup_table_set_visible (hangul->table, FALSE);
750 if (hangul->hanja_list != NULL) {
751 hanja_list_delete (hangul->hanja_list);
752 hangul->hanja_list = NULL;
757 ibus_hangul_engine_update_lookup_table (IBusHangulEngine *hangul)
759 ibus_hangul_engine_update_hanja_list (hangul);
761 if (hangul->hanja_list != NULL) {
762 // We should redraw preedit text with IBUS_ENGINE_PREEDIT_CLEAR option
763 // here to prevent committing it on focus out event incidentally.
764 ibus_hangul_engine_update_preedit_text (hangul);
765 ibus_hangul_engine_apply_hanja_list (hangul);
767 ibus_hangul_engine_hide_lookup_table (hangul);
772 ibus_hangul_engine_process_candidate_key_event (IBusHangulEngine *hangul,
776 if (keyval == IBUS_Escape) {
777 ibus_hangul_engine_hide_lookup_table (hangul);
778 // When the lookup table is poped up, preedit string is
779 // updated with IBUS_ENGINE_PREEDIT_CLEAR option.
780 // So, when focus is out, the preedit text will not be committed.
781 // To prevent this problem, we have to update preedit text here
782 // with IBUS_ENGINE_PREEDIT_COMMIT option.
783 ibus_hangul_engine_update_preedit_text (hangul);
785 } else if (keyval == IBUS_Return) {
786 ibus_hangul_engine_commit_current_candidate (hangul);
788 if (hangul->hanja_mode && ibus_hangul_engine_has_preedit (hangul)) {
789 ibus_hangul_engine_update_lookup_table (hangul);
791 ibus_hangul_engine_hide_lookup_table (hangul);
794 } else if (keyval >= IBUS_1 && keyval <= IBUS_9) {
799 page_size = ibus_lookup_table_get_page_size (hangul->table);
800 cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
801 page_no = cursor_pos / page_size;
803 cursor_pos = page_no * page_size + (keyval - IBUS_1);
804 ibus_lookup_table_set_cursor_pos (hangul->table, cursor_pos);
806 ibus_hangul_engine_commit_current_candidate (hangul);
808 if (hangul->hanja_mode && ibus_hangul_engine_has_preedit (hangul)) {
809 ibus_hangul_engine_update_lookup_table (hangul);
811 ibus_hangul_engine_hide_lookup_table (hangul);
814 } else if (keyval == IBUS_Page_Up) {
815 ibus_lookup_table_page_up (hangul->table);
816 ibus_hangul_engine_update_lookup_table_ui (hangul);
818 } else if (keyval == IBUS_Page_Down) {
819 ibus_lookup_table_page_down (hangul->table);
820 ibus_hangul_engine_update_lookup_table_ui (hangul);
823 if (lookup_table_orientation == 0) {
825 if (keyval == IBUS_Left) {
826 ibus_lookup_table_cursor_up (hangul->table);
827 ibus_hangul_engine_update_lookup_table_ui (hangul);
829 } else if (keyval == IBUS_Right) {
830 ibus_lookup_table_cursor_down (hangul->table);
831 ibus_hangul_engine_update_lookup_table_ui (hangul);
833 } else if (keyval == IBUS_Up) {
834 ibus_lookup_table_page_up (hangul->table);
835 ibus_hangul_engine_update_lookup_table_ui (hangul);
837 } else if (keyval == IBUS_Down) {
838 ibus_lookup_table_page_down (hangul->table);
839 ibus_hangul_engine_update_lookup_table_ui (hangul);
844 if (keyval == IBUS_Left) {
845 ibus_lookup_table_page_up (hangul->table);
846 ibus_hangul_engine_update_lookup_table_ui (hangul);
848 } else if (keyval == IBUS_Right) {
849 ibus_lookup_table_page_down (hangul->table);
850 ibus_hangul_engine_update_lookup_table_ui (hangul);
852 } else if (keyval == IBUS_Up) {
853 ibus_lookup_table_cursor_up (hangul->table);
854 ibus_hangul_engine_update_lookup_table_ui (hangul);
856 } else if (keyval == IBUS_Down) {
857 ibus_lookup_table_cursor_down (hangul->table);
858 ibus_hangul_engine_update_lookup_table_ui (hangul);
864 if (!hangul->hanja_mode) {
865 if (lookup_table_orientation == 0) {
867 if (keyval == IBUS_h) {
868 ibus_lookup_table_cursor_up (hangul->table);
869 ibus_hangul_engine_update_lookup_table_ui (hangul);
871 } else if (keyval == IBUS_l) {
872 ibus_lookup_table_cursor_down (hangul->table);
873 ibus_hangul_engine_update_lookup_table_ui (hangul);
875 } else if (keyval == IBUS_k) {
876 ibus_lookup_table_page_up (hangul->table);
877 ibus_hangul_engine_update_lookup_table_ui (hangul);
879 } else if (keyval == IBUS_j) {
880 ibus_lookup_table_page_down (hangul->table);
881 ibus_hangul_engine_update_lookup_table_ui (hangul);
886 if (keyval == IBUS_h) {
887 ibus_lookup_table_page_up (hangul->table);
888 ibus_hangul_engine_update_lookup_table_ui (hangul);
890 } else if (keyval == IBUS_l) {
891 ibus_lookup_table_page_down (hangul->table);
892 ibus_hangul_engine_update_lookup_table_ui (hangul);
894 } else if (keyval == IBUS_k) {
895 ibus_lookup_table_cursor_up (hangul->table);
896 ibus_hangul_engine_update_lookup_table_ui (hangul);
898 } else if (keyval == IBUS_j) {
899 ibus_lookup_table_cursor_down (hangul->table);
900 ibus_hangul_engine_update_lookup_table_ui (hangul);
910 ibus_hangul_engine_process_key_event (IBusEngine *engine,
915 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
921 if (modifiers & IBUS_RELEASE_MASK)
924 // if we don't ignore shift keys, shift key will make flush the preedit
925 // string. So you cannot input shift+key.
926 // Let's think about these examples:
929 if (keyval == IBUS_Shift_L || keyval == IBUS_Shift_R)
932 // If hanja key has any modifiers, we ignore that modifier keyval,
933 // or we cannot make the hanja key work.
934 // Because when we get the modifier key alone, we commit the
935 // current preedit string. So after that, even if we get the
936 // right hanja key event, we don't have preedit string to be changed
938 // See this bug: http://code.google.com/p/ibus/issues/detail?id=1036
939 if (hanja_key_list_has_modifier(&hanja_keys, keyval))
942 if (hanja_key_list_match(&hanja_keys, keyval, modifiers)) {
943 if (hangul->hanja_list == NULL) {
944 ibus_hangul_engine_update_lookup_table (hangul);
946 ibus_hangul_engine_hide_lookup_table (hangul);
951 if (hangul->hanja_list != NULL) {
952 retval = ibus_hangul_engine_process_candidate_key_event (hangul,
954 if (hangul->hanja_mode) {
962 // If we've got a key event with some modifiers, commit current
963 // preedit string and ignore this key event.
964 // So, if you want to add some key event handler, put it
966 // Ignore key event with control, alt, super or mod5
967 mask = IBUS_CONTROL_MASK |
968 IBUS_MOD1_MASK | IBUS_MOD3_MASK | IBUS_MOD4_MASK | IBUS_MOD5_MASK;
969 if (modifiers & mask) {
970 ibus_hangul_engine_flush (hangul);
974 if (keyval == IBUS_BackSpace) {
975 retval = hangul_ic_backspace (hangul->context);
977 guint preedit_len = ustring_length (hangul->preedit);
978 if (preedit_len > 0) {
979 ustring_erase (hangul->preedit, preedit_len - 1, 1);
984 ibus_hangul_engine_update_preedit_text (hangul);
986 if (hangul->hanja_mode) {
987 if (ibus_hangul_engine_has_preedit (hangul)) {
988 ibus_hangul_engine_update_lookup_table (hangul);
990 ibus_hangul_engine_hide_lookup_table (hangul);
994 // We need to normalize the keyval to US qwerty keylayout,
995 // because the korean input method is depend on the position of
996 // each key, not the character. We make the keyval from keycode
997 // as if the keyboard is US qwerty layout. Then we can assume the
998 // keyval represent the position of the each key.
999 // But if the hic is in transliteration mode, then we should not
1000 // normalize the keyval.
1001 bool is_transliteration_mode =
1002 hangul_ic_is_transliteration(hangul->context);
1003 if (!is_transliteration_mode) {
1005 keyval = ibus_keymap_lookup_keysym(keymap, keycode, modifiers);
1009 if (modifiers & IBUS_LOCK_MASK) {
1010 if (keyval >= 'A' && keyval <= 'z') {
1011 if (isupper(keyval))
1012 keyval = tolower(keyval);
1014 keyval = toupper(keyval);
1017 retval = hangul_ic_process (hangul->context, keyval);
1019 str = hangul_ic_get_commit_string (hangul->context);
1020 if (word_commit || hangul->hanja_mode) {
1021 const ucschar* hic_preedit;
1023 hic_preedit = hangul_ic_get_preedit_string (hangul->context);
1024 if (hic_preedit != NULL && hic_preedit[0] != 0) {
1025 ustring_append_ucs4 (hangul->preedit, str, -1);
1028 const ucschar* preedit;
1030 ustring_append_ucs4 (hangul->preedit, str, -1);
1031 if (ustring_length (hangul->preedit) > 0) {
1032 /* clear preedit text before commit */
1033 ibus_hangul_engine_clear_preedit_text (hangul);
1035 preedit = ustring_begin (hangul->preedit);
1036 text = ibus_text_new_from_ucs4 ((gunichar*)preedit);
1037 ibus_engine_commit_text (engine, text);
1039 ustring_clear (hangul->preedit);
1042 if (str != NULL && str[0] != 0) {
1045 /* clear preedit text before commit */
1046 ibus_hangul_engine_clear_preedit_text (hangul);
1048 text = ibus_text_new_from_ucs4 (str);
1049 ibus_engine_commit_text (engine, text);
1053 ibus_hangul_engine_update_preedit_text (hangul);
1055 if (hangul->hanja_mode) {
1056 ibus_hangul_engine_update_lookup_table (hangul);
1060 ibus_hangul_engine_flush (hangul);
1067 ibus_hangul_engine_flush (IBusHangulEngine *hangul)
1069 const gunichar *str;
1072 ibus_hangul_engine_hide_lookup_table (hangul);
1074 str = hangul_ic_flush (hangul->context);
1076 ustring_append_ucs4 (hangul->preedit, str, -1);
1078 if (ustring_length (hangul->preedit) != 0) {
1079 /* clear preedit text before commit */
1080 ibus_hangul_engine_clear_preedit_text (hangul);
1082 str = ustring_begin (hangul->preedit);
1083 text = ibus_text_new_from_ucs4 (str);
1085 ibus_engine_commit_text ((IBusEngine *) hangul, text);
1087 ustring_clear(hangul->preedit);
1090 ibus_hangul_engine_update_preedit_text (hangul);
1094 ibus_hangul_engine_focus_in (IBusEngine *engine)
1096 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1098 if (hangul->hanja_mode) {
1099 ibus_property_set_state (hangul->prop_hanja_mode, PROP_STATE_CHECKED);
1101 ibus_property_set_state (hangul->prop_hanja_mode, PROP_STATE_UNCHECKED);
1104 ibus_engine_register_properties (engine, hangul->prop_list);
1106 ibus_hangul_engine_update_preedit_text (hangul);
1108 if (hangul->hanja_list != NULL) {
1109 ibus_hangul_engine_update_lookup_table_ui (hangul);
1112 parent_class->focus_in (engine);
1116 ibus_hangul_engine_focus_out (IBusEngine *engine)
1118 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1120 if (hangul->hanja_list == NULL) {
1122 // ibus_engine_update_preedit_text_with_mode() function which makes
1123 // the preedit string committed automatically when the focus is out.
1124 // So we don't need to commit the preedit here.
1125 hangul_ic_reset (hangul->context);
1127 ibus_engine_hide_lookup_table (engine);
1128 ibus_engine_hide_auxiliary_text (engine);
1131 parent_class->focus_out ((IBusEngine *) hangul);
1135 ibus_hangul_engine_reset (IBusEngine *engine)
1137 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1139 ibus_hangul_engine_flush (hangul);
1140 parent_class->reset (engine);
1144 ibus_hangul_engine_enable (IBusEngine *engine)
1146 parent_class->enable (engine);
1148 ibus_engine_get_surrounding_text (engine, NULL, NULL, NULL);
1152 ibus_hangul_engine_disable (IBusEngine *engine)
1154 ibus_hangul_engine_focus_out (engine);
1155 parent_class->disable (engine);
1159 ibus_hangul_engine_page_up (IBusEngine *engine)
1161 parent_class->page_up (engine);
1165 ibus_hangul_engine_page_down (IBusEngine *engine)
1167 parent_class->page_down (engine);
1171 ibus_hangul_engine_cursor_up (IBusEngine *engine)
1173 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1175 if (hangul->hanja_list != NULL) {
1176 ibus_lookup_table_cursor_up (hangul->table);
1177 ibus_hangul_engine_update_lookup_table_ui (hangul);
1180 parent_class->cursor_up (engine);
1184 ibus_hangul_engine_cursor_down (IBusEngine *engine)
1186 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1188 if (hangul->hanja_list != NULL) {
1189 ibus_lookup_table_cursor_down (hangul->table);
1190 ibus_hangul_engine_update_lookup_table_ui (hangul);
1193 parent_class->cursor_down (engine);
1197 ibus_hangul_engine_property_activate (IBusEngine *engine,
1198 const gchar *prop_name,
1201 if (strcmp(prop_name, "setup") == 0) {
1202 GError *error = NULL;
1203 gchar *argv[2] = { NULL, };
1205 argv[0] = "ibus-setup-hangul";
1207 g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error);
1208 } else if (strcmp(prop_name, "hanja_mode") == 0) {
1209 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1211 hangul->hanja_mode = !hangul->hanja_mode;
1212 if (hangul->hanja_mode) {
1213 ibus_property_set_state (hangul->prop_hanja_mode,
1214 PROP_STATE_CHECKED);
1216 ibus_property_set_state (hangul->prop_hanja_mode,
1217 PROP_STATE_UNCHECKED);
1220 ibus_engine_update_property (engine, hangul->prop_hanja_mode);
1221 ibus_hangul_engine_flush (hangul);
1226 ibus_hangul_engine_has_preedit (IBusHangulEngine *hangul)
1229 const ucschar *hic_preedit;
1231 hic_preedit = hangul_ic_get_preedit_string (hangul->context);
1232 if (hic_preedit[0] != 0)
1235 preedit_len = ustring_length (hangul->preedit);
1236 if (preedit_len > 0)
1243 ibus_hangul_engine_on_transition (HangulInputContext *hic,
1245 const ucschar *preedit,
1248 if (!auto_reorder) {
1249 if (hangul_is_choseong (c)) {
1250 if (hangul_ic_has_jungseong (hic) || hangul_ic_has_jongseong (hic))
1254 if (hangul_is_jungseong (c)) {
1255 if (hangul_ic_has_jongseong (hic))
1264 ibus_config_value_changed (IBusConfig *config,
1265 const gchar *section,
1270 IBusHangulEngine *hangul = (IBusHangulEngine *) user_data;
1272 if (strcmp(section, "engine/Hangul") == 0) {
1273 if (strcmp(name, "HangulKeyboard") == 0) {
1274 const gchar *str = g_variant_get_string(value, NULL);
1275 g_string_assign (hangul_keyboard, str);
1276 hangul_ic_select_keyboard (hangul->context, hangul_keyboard->str);
1277 } else if (strcmp(name, "HanjaKeys") == 0) {
1278 const gchar* str = g_variant_get_string(value, NULL);
1279 hanja_key_list_set_from_string(&hanja_keys, str);
1280 } else if (strcmp(name, "WordCommit") == 0) {
1281 word_commit = g_variant_get_boolean (value);
1282 } else if (strcmp (name, "AutoReorder") == 0) {
1283 auto_reorder = g_variant_get_boolean (value);
1285 } else if (strcmp(section, "panel") == 0) {
1286 if (strcmp(name, "lookup_table_orientation") == 0) {
1287 lookup_table_orientation = g_variant_get_int32(value);
1293 lookup_table_set_visible (IBusLookupTable *table, gboolean flag)
1295 g_object_set_data (G_OBJECT(table), "visible", GUINT_TO_POINTER(flag));
1299 lookup_table_is_visible (IBusLookupTable *table)
1301 gpointer res = g_object_get_data (G_OBJECT(table), "visible");
1302 return GPOINTER_TO_UINT(res);
1306 key_event_list_append(GArray* list, guint keyval, guint modifiers)
1308 struct KeyEvent ev = { keyval, modifiers};
1309 g_array_append_val(list, ev);
1313 key_event_list_match(GArray* list, guint keyval, guint modifiers)
1318 /* ignore capslock and numlock */
1319 mask = IBUS_SHIFT_MASK |
1327 for (i = 0; i < list->len; ++i) {
1328 struct KeyEvent* ev = &g_array_index(list, struct KeyEvent, i);
1329 if (ev->keyval == keyval && ev->modifiers == modifiers) {
1338 ibus_hangul_engine_candidate_clicked (IBusEngine *engine,
1343 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1347 if (hangul->table == NULL)
1350 ibus_lookup_table_set_cursor_pos (hangul->table, index);
1351 ibus_hangul_engine_commit_current_candidate (hangul);
1353 if (hangul->hanja_mode) {
1354 ibus_hangul_engine_update_lookup_table (hangul);
1356 ibus_hangul_engine_hide_lookup_table (hangul);
1361 hanja_key_list_init(HanjaKeyList* list)
1363 list->all_modifiers = 0;
1364 list->keys = g_array_sized_new(FALSE, TRUE, sizeof(struct KeyEvent), 4);
1368 hanja_key_list_fini(HanjaKeyList* list)
1370 g_array_free(list->keys, TRUE);
1374 hanja_key_list_append_from_string(HanjaKeyList *list, const char* str)
1377 guint modifiers = 0;
1380 res = ibus_key_event_from_string(str, &keyval, &modifiers);
1382 hanja_key_list_append(list, keyval, modifiers);
1387 hanja_key_list_append(HanjaKeyList *list, guint keyval, guint modifiers)
1389 list->all_modifiers |= modifiers;
1390 key_event_list_append(list->keys, keyval, modifiers);
1394 hanja_key_list_set_from_string(HanjaKeyList *list, const char* str)
1396 gchar** items = g_strsplit(str, ",", 0);
1398 list->all_modifiers = 0;
1399 g_array_set_size(list->keys, 0);
1401 if (items != NULL) {
1403 for (i = 0; items[i] != NULL; ++i) {
1404 hanja_key_list_append_from_string(list, items[i]);
1411 hanja_key_list_match(HanjaKeyList* list, guint keyval, guint modifiers)
1413 return key_event_list_match(list->keys, keyval, modifiers);
1417 hanja_key_list_has_modifier(HanjaKeyList* list, guint keyval)
1419 if (list->all_modifiers & IBUS_CONTROL_MASK) {
1420 if (keyval == IBUS_Control_L || keyval == IBUS_Control_R)
1424 if (list->all_modifiers & IBUS_MOD1_MASK) {
1425 if (keyval == IBUS_Alt_L || keyval == IBUS_Alt_R)
1429 if (list->all_modifiers & IBUS_SUPER_MASK) {
1430 if (keyval == IBUS_Super_L || keyval == IBUS_Super_R)
1434 if (list->all_modifiers & IBUS_HYPER_MASK) {
1435 if (keyval == IBUS_Hyper_L || keyval == IBUS_Hyper_R)
1439 if (list->all_modifiers & IBUS_META_MASK) {
1440 if (keyval == IBUS_Meta_L || keyval == IBUS_Meta_R)