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>
5 *
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.
10 *
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.
15 *
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.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <ibus.h>
26 #include <gio/gio.h>
27 #include <hangul.h>
28 #include <string.h>
29 #include <ctype.h>
30
31 #include "i18n.h"
32 #include "engine.h"
33 #include "ustring.h"
34
35
36 typedef struct _IBusHangulEngine IBusHangulEngine;
37 typedef struct _IBusHangulEngineClass IBusHangulEngineClass;
38
39 typedef struct _HotkeyList HotkeyList;
40
41 enum {
42 INPUT_MODE_HANGUL,
43 INPUT_MODE_LATIN,
44 INPUT_MODE_COUNT,
45 };
46
47 struct _IBusHangulEngine {
48 IBusEngineSimple parent;
49
50 /* members */
51 HangulInputContext *context;
52 UString* preedit;
53 int input_mode;
54 unsigned int input_purpose;
55 gboolean hanja_mode;
56 HanjaList* hanja_list;
57 int last_lookup_method;
58
59 IBusLookupTable *table;
60
61 IBusProperty *prop_hangul_mode;
62 IBusProperty *prop_hanja_mode;
63 IBusPropList *prop_list;
64
65 IBusText *input_mode_symbols[INPUT_MODE_COUNT];
66 };
67
68 struct _IBusHangulEngineClass {
69 IBusEngineSimpleClass parent;
70 };
71
72 struct KeyEvent {
73 guint keyval;
74 guint modifiers;
75 };
76
77 struct _HotkeyList {
78 guint all_modifiers;
79 GArray *keys;
80 };
81
82 enum {
83 LOOKUP_METHOD_EXACT,
84 LOOKUP_METHOD_PREFIX,
85 LOOKUP_METHOD_SUFFIX,
86 };
87
88 /* functions prototype */
89 static void ibus_hangul_engine_class_init
90 (IBusHangulEngineClass *klass);
91 static void ibus_hangul_engine_init (IBusHangulEngine *hangul);
92 static GObject*
93 ibus_hangul_engine_constructor
94 (GType type,
95 guint n_construct_params,
96 GObjectConstructParam *construct_params);
97 static void ibus_hangul_engine_destroy (IBusHangulEngine *hangul);
98 static gboolean
99 ibus_hangul_engine_process_key_event
100 (IBusEngine *engine,
101 guint keyval,
102 guint keycode,
103 guint modifiers);
104 static void ibus_hangul_engine_focus_in (IBusEngine *engine);
105 static void ibus_hangul_engine_focus_out (IBusEngine *engine);
106 static void ibus_hangul_engine_reset (IBusEngine *engine);
107 static void ibus_hangul_engine_enable (IBusEngine *engine);
108 static void ibus_hangul_engine_disable (IBusEngine *engine);
109 #if 0
110 static void ibus_engine_set_cursor_location (IBusEngine *engine,
111 gint x,
112 gint y,
113 gint w,
114 gint h);
115 static void ibus_hangul_engine_set_capabilities
116 (IBusEngine *engine,
117 guint caps);
118 #endif
119 static void ibus_hangul_engine_page_up (IBusEngine *engine);
120 static void ibus_hangul_engine_page_down (IBusEngine *engine);
121 static void ibus_hangul_engine_cursor_up (IBusEngine *engine);
122 static void ibus_hangul_engine_cursor_down (IBusEngine *engine);
123 static void ibus_hangul_engine_property_activate
124 (IBusEngine *engine,
125 const gchar *prop_name,
126 guint prop_state);
127 #if 0
128 static void ibus_hangul_engine_property_show
129 (IBusEngine *engine,
130 const gchar *prop_name);
131 static void ibus_hangul_engine_property_hide
132 (IBusEngine *engine,
133 const gchar *prop_name);
134 #endif
135
136 static void ibus_hangul_engine_candidate_clicked
137 (IBusEngine *engine,
138 guint index,
139 guint button,
140 guint state);
141 static void ibus_hangul_engine_set_content_type
142 (IBusEngine *engine,
143 guint purpose,
144 guint hints);
145
146 static void ibus_hangul_engine_flush (IBusHangulEngine *hangul);
147 static void ibus_hangul_engine_clear_preedit_text
148 (IBusHangulEngine *hangul);
149 static void ibus_hangul_engine_update_preedit_text
150 (IBusHangulEngine *hangul);
151
152 static void ibus_hangul_engine_update_lookup_table
153 (IBusHangulEngine *hangul);
154 static gboolean ibus_hangul_engine_has_preedit
155 (IBusHangulEngine *hangul);
156 static void ibus_hangul_engine_switch_input_mode
157 (IBusHangulEngine *hangul);
158 static void ibus_hangul_engine_set_input_mode
159 (IBusHangulEngine *hangul,
160 int input_mode);
161 static IBusText*
162 ibus_hangul_engine_get_input_mode_symbol
163 (IBusHangulEngine *hangul,
164 int input_mode);
165
166 static bool ibus_hangul_engine_on_transition
167 (HangulInputContext *hic,
168 ucschar c,
169 const ucschar *preedit,
170 void *data);
171
172 static void settings_changed (GSettings *settings,
173 const gchar *key,
174 gpointer user_data);
175
176 static void lookup_table_set_visible (IBusLookupTable *table,
177 gboolean flag);
178 static gboolean lookup_table_is_visible
179 (IBusLookupTable *table);
180
181 static gboolean key_event_list_match (GArray *list,
182 guint keyval,
183 guint modifiers);
184
185 static void hotkey_list_init (HotkeyList *list);
186 static void hotkey_list_fini (HotkeyList *list);
187 static void hotkey_list_set_from_string (HotkeyList *list,
188 const char *str);
189 static void hotkey_list_append (HotkeyList *list,
190 guint keyval,
191 guint modifiers);
192 static gboolean hotkey_list_match (HotkeyList *list,
193 guint keyval,
194 guint modifiers);
195 static gboolean hotkey_list_has_modifier (HotkeyList *list,
196 guint keyval);
197
198 static glong ucschar_strlen (const ucschar* str);
199
200 static IBusEngineSimpleClass *parent_class = NULL;
201 static HanjaTable *hanja_table = NULL;
202 static HanjaTable *symbol_table = NULL;
203 static GSettings *settings_hangul = NULL;
204 static GSettings *settings_panel = NULL;
205 static GString *hangul_keyboard = NULL;
206 static HotkeyList hanja_keys;
207 static HotkeyList switch_keys;
208 static HotkeyList on_keys;
209 static HotkeyList off_keys;
210 static int lookup_table_orientation = 0;
211 static IBusKeymap *keymap = NULL;
212 static gboolean word_commit = FALSE;
213 static gboolean auto_reorder = TRUE;
214 static gboolean disable_latin_mode = FALSE;
215 static int initial_input_mode = INPUT_MODE_LATIN;
216 /**
217 * whether to use event forwarding workaround
218 */
219 static gboolean use_event_forwarding = TRUE;
220
221 static glong
ucschar_strlen(const ucschar * str)222 ucschar_strlen (const ucschar* str)
223 {
224 const ucschar* p = str;
225 while (*p != 0)
226 p++;
227 return p - str;
228 }
229
230 GType
ibus_hangul_engine_get_type(void)231 ibus_hangul_engine_get_type (void)
232 {
233 static GType type = 0;
234
235 static const GTypeInfo type_info = {
236 sizeof (IBusHangulEngineClass),
237 (GBaseInitFunc) NULL,
238 (GBaseFinalizeFunc) NULL,
239 (GClassInitFunc) ibus_hangul_engine_class_init,
240 NULL,
241 NULL,
242 sizeof (IBusHangulEngine),
243 0,
244 (GInstanceInitFunc) ibus_hangul_engine_init,
245 };
246
247 if (type == 0) {
248 type = g_type_register_static (IBUS_TYPE_ENGINE_SIMPLE,
249 "IBusHangulEngine",
250 &type_info,
251 (GTypeFlags) 0);
252 }
253
254 return type;
255 }
256
257 void
ibus_hangul_init(IBusBus * bus)258 ibus_hangul_init (IBusBus *bus)
259 {
260 GVariant* value = NULL;
261
262 hanja_table = hanja_table_load (NULL);
263
264 symbol_table = hanja_table_load (IBUSHANGUL_DATADIR "/data/symbol.txt");
265
266 settings_hangul = g_settings_new ("org.freedesktop.ibus.engine.hangul");
267 settings_panel = g_settings_new ("org.freedesktop.ibus.panel");
268
269 hangul_keyboard = g_string_new_len (NULL, 8);
270 value = g_settings_get_value (settings_hangul, "hangul-keyboard");
271 if (value != NULL) {
272 const gchar* str = g_variant_get_string (value, NULL);
273 g_string_assign (hangul_keyboard, str);
274 g_clear_pointer (&value, g_variant_unref);
275 }
276
277 hotkey_list_init(&switch_keys);
278
279 value = g_settings_get_value (settings_hangul, "switch-keys");
280 if (value != NULL) {
281 const gchar* str = g_variant_get_string (value, NULL);
282 hotkey_list_set_from_string(&switch_keys, str);
283 g_clear_pointer (&value, g_variant_unref);
284 } else {
285 hotkey_list_append(&switch_keys, IBUS_Hangul, 0);
286 hotkey_list_append(&switch_keys, IBUS_space, IBUS_SHIFT_MASK);
287 }
288
289 hotkey_list_init(&hanja_keys);
290
291 value = g_settings_get_value (settings_hangul, "hanja-keys");
292 if (value != NULL) {
293 const gchar* str = g_variant_get_string (value, NULL);
294 hotkey_list_set_from_string(&hanja_keys, str);
295 g_clear_pointer (&value, g_variant_unref);
296 } else {
297 hotkey_list_append(&hanja_keys, IBUS_Hangul_Hanja, 0);
298 hotkey_list_append(&hanja_keys, IBUS_F9, 0);
299 }
300
301 hotkey_list_init (&on_keys);
302 value = g_settings_get_value (settings_hangul, "on-keys");
303 if (value != NULL) {
304 const gchar* str = g_variant_get_string (value, NULL);
305 hotkey_list_set_from_string (&on_keys, str);
306 g_clear_pointer (&value, g_variant_unref);
307 }
308
309 hotkey_list_init (&off_keys);
310 value = g_settings_get_value (settings_hangul, "off-keys");
311 if (value != NULL) {
312 const gchar* str = g_variant_get_string (value, NULL);
313 hotkey_list_set_from_string (&off_keys, str);
314 g_clear_pointer (&value, g_variant_unref);
315 }
316
317 value = g_settings_get_value (settings_hangul, "word-commit");
318 if (value != NULL) {
319 word_commit = g_variant_get_boolean (value);
320 g_clear_pointer (&value, g_variant_unref);
321 }
322
323 value = g_settings_get_value (settings_hangul, "auto-reorder");
324 if (value != NULL) {
325 auto_reorder = g_variant_get_boolean (value);
326 g_clear_pointer (&value, g_variant_unref);
327 }
328
329 value = g_settings_get_value (settings_hangul, "disable-latin-mode");
330 if (value != NULL) {
331 disable_latin_mode = g_variant_get_boolean (value);
332 g_clear_pointer (&value, g_variant_unref);
333 }
334
335 value = g_settings_get_value (settings_hangul, "initial-input-mode");
336 if (value != NULL) {
337 const gchar* str = g_variant_get_string (value, NULL);
338 if (strcmp(str, "latin") == 0) {
339 initial_input_mode = INPUT_MODE_LATIN;
340 } else if (strcmp(str, "hangul") == 0) {
341 initial_input_mode = INPUT_MODE_HANGUL;
342 }
343 g_clear_pointer (&value, g_variant_unref);
344 }
345
346 value = g_settings_get_value (settings_hangul, "use-event-forwarding");
347 if (value != NULL) {
348 use_event_forwarding = g_variant_get_boolean (value);
349 g_clear_pointer (&value, g_variant_unref);
350 }
351
352 value = g_settings_get_value (settings_panel, "lookup-table-orientation");
353 if (value != NULL) {
354 lookup_table_orientation = g_variant_get_int32(value);
355 g_clear_pointer (&value, g_variant_unref);
356 }
357
358 keymap = ibus_keymap_get("us");
359 }
360
361 void
ibus_hangul_exit(void)362 ibus_hangul_exit (void)
363 {
364 if (keymap != NULL) {
365 g_object_unref(keymap);
366 keymap = NULL;
367 }
368
369 hotkey_list_fini (&switch_keys);
370 hotkey_list_fini (&hanja_keys);
371 hotkey_list_fini (&on_keys);
372 hotkey_list_fini (&off_keys);
373
374 hanja_table_delete (hanja_table);
375 hanja_table = NULL;
376
377 hanja_table_delete (symbol_table);
378 symbol_table = NULL;
379
380 g_clear_object (&settings_hangul);
381 g_clear_object (&settings_panel);
382
383 g_string_free (hangul_keyboard, TRUE);
384 hangul_keyboard = NULL;
385 }
386
387 static void
ibus_hangul_engine_class_init(IBusHangulEngineClass * klass)388 ibus_hangul_engine_class_init (IBusHangulEngineClass *klass)
389 {
390 GObjectClass *object_class = G_OBJECT_CLASS (klass);
391 IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (klass);
392 IBusEngineClass *engine_class = IBUS_ENGINE_CLASS (klass);
393
394 parent_class = (IBusEngineSimpleClass *) g_type_class_peek_parent (klass);
395
396 object_class->constructor = ibus_hangul_engine_constructor;
397 ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_hangul_engine_destroy;
398
399 engine_class->process_key_event = ibus_hangul_engine_process_key_event;
400
401 engine_class->reset = ibus_hangul_engine_reset;
402 engine_class->enable = ibus_hangul_engine_enable;
403 engine_class->disable = ibus_hangul_engine_disable;
404
405 engine_class->focus_in = ibus_hangul_engine_focus_in;
406 engine_class->focus_out = ibus_hangul_engine_focus_out;
407
408 engine_class->page_up = ibus_hangul_engine_page_up;
409 engine_class->page_down = ibus_hangul_engine_page_down;
410
411 engine_class->cursor_up = ibus_hangul_engine_cursor_up;
412 engine_class->cursor_down = ibus_hangul_engine_cursor_down;
413
414 engine_class->property_activate = ibus_hangul_engine_property_activate;
415
416 engine_class->candidate_clicked = ibus_hangul_engine_candidate_clicked;
417 engine_class->set_content_type = ibus_hangul_engine_set_content_type;
418 }
419
420 static void
ibus_hangul_engine_init(IBusHangulEngine * hangul)421 ibus_hangul_engine_init (IBusHangulEngine *hangul)
422 {
423 IBusProperty* prop;
424 IBusText* label;
425 IBusText* tooltip;
426 IBusText* symbol;
427
428 hangul->context = hangul_ic_new (hangul_keyboard->str);
429 hangul_ic_connect_callback (hangul->context, "transition",
430 ibus_hangul_engine_on_transition, hangul);
431
432 hangul->preedit = ustring_new();
433 hangul->hanja_list = NULL;
434 hangul->input_mode = initial_input_mode;
435 hangul->input_purpose = IBUS_INPUT_PURPOSE_FREE_FORM;
436 hangul->hanja_mode = FALSE;
437 hangul->last_lookup_method = LOOKUP_METHOD_PREFIX;
438
439 if (disable_latin_mode) {
440 hangul->input_mode = INPUT_MODE_HANGUL;
441 }
442
443 hangul->prop_list = ibus_prop_list_new ();
444 g_object_ref_sink (hangul->prop_list);
445
446 label = ibus_text_new_from_string (_("Hangul mode"));
447 tooltip = ibus_text_new_from_string (_("Enable/Disable Hangul mode"));
448 prop = ibus_property_new ("InputMode",
449 PROP_TYPE_TOGGLE,
450 label,
451 NULL,
452 tooltip,
453 TRUE, TRUE, PROP_STATE_UNCHECKED, NULL);
454 symbol = ibus_hangul_engine_get_input_mode_symbol (hangul,
455 hangul->input_mode);
456 ibus_property_set_symbol(prop, symbol);
457 g_object_ref_sink (prop);
458 ibus_prop_list_append (hangul->prop_list, prop);
459 hangul->prop_hangul_mode = prop;
460
461 label = ibus_text_new_from_string (_("Hanja lock"));
462 tooltip = ibus_text_new_from_string (_("Enable/Disable Hanja mode"));
463 prop = ibus_property_new ("hanja_mode",
464 PROP_TYPE_TOGGLE,
465 label,
466 NULL,
467 tooltip,
468 TRUE, TRUE, PROP_STATE_UNCHECKED, NULL);
469 g_object_ref_sink (prop);
470 ibus_prop_list_append (hangul->prop_list, prop);
471 hangul->prop_hanja_mode = prop;
472
473 label = ibus_text_new_from_string (_("Setup"));
474 tooltip = ibus_text_new_from_string (_("Configure hangul engine"));
475 prop = ibus_property_new ("setup",
476 PROP_TYPE_NORMAL,
477 label,
478 "gtk-preferences",
479 tooltip,
480 TRUE, TRUE, PROP_STATE_UNCHECKED, NULL);
481 ibus_prop_list_append (hangul->prop_list, prop);
482
483 hangul->table = ibus_lookup_table_new (9, 0, TRUE, FALSE);
484 g_object_ref_sink (hangul->table);
485
486 g_signal_connect (settings_hangul, "changed",
487 G_CALLBACK (settings_changed), hangul);
488 g_signal_connect (settings_panel, "changed",
489 G_CALLBACK (settings_changed), hangul);
490 }
491
492 static GObject*
ibus_hangul_engine_constructor(GType type,guint n_construct_params,GObjectConstructParam * construct_params)493 ibus_hangul_engine_constructor (GType type,
494 guint n_construct_params,
495 GObjectConstructParam *construct_params)
496 {
497 IBusHangulEngine *hangul;
498
499 hangul = (IBusHangulEngine *) G_OBJECT_CLASS (parent_class)->constructor (type,
500 n_construct_params,
501 construct_params);
502
503 return (GObject *)hangul;
504 }
505
506
507 static void
ibus_hangul_engine_destroy(IBusHangulEngine * hangul)508 ibus_hangul_engine_destroy (IBusHangulEngine *hangul)
509 {
510 int i;
511 IBusText **symbols;
512
513 if (hangul->prop_hangul_mode) {
514 g_object_unref (hangul->prop_hangul_mode);
515 hangul->prop_hangul_mode = NULL;
516 }
517
518 if (hangul->prop_hanja_mode) {
519 g_object_unref (hangul->prop_hanja_mode);
520 hangul->prop_hanja_mode = NULL;
521 }
522
523 if (hangul->prop_list) {
524 g_object_unref (hangul->prop_list);
525 hangul->prop_list = NULL;
526 }
527
528 if (hangul->table) {
529 g_object_unref (hangul->table);
530 hangul->table = NULL;
531 }
532
533 if (hangul->context) {
534 hangul_ic_delete (hangul->context);
535 hangul->context = NULL;
536 }
537
538 symbols = hangul->input_mode_symbols;
539 for (i = 0; i < INPUT_MODE_COUNT; ++i) {
540 if (symbols[i] != NULL) {
541 g_object_unref(symbols[i]);
542 symbols[i] = NULL;
543 }
544 }
545
546 IBUS_OBJECT_CLASS (parent_class)->destroy ((IBusObject *)hangul);
547 }
548
549 static void
ibus_hangul_engine_clear_preedit_text(IBusHangulEngine * hangul)550 ibus_hangul_engine_clear_preedit_text (IBusHangulEngine *hangul)
551 {
552 IBusText *text;
553
554 text = ibus_text_new_from_static_string ("");
555 ibus_engine_update_preedit_text ((IBusEngine *)hangul, text, 0, FALSE);
556 }
557
558 static void
ibus_hangul_engine_update_preedit_text(IBusHangulEngine * hangul)559 ibus_hangul_engine_update_preedit_text (IBusHangulEngine *hangul)
560 {
561 const ucschar *hic_preedit;
562 IBusText *text;
563 UString *preedit;
564 gint preedit_len;
565
566 // ibus-hangul's preedit string is made up of ibus context's
567 // internal preedit string and libhangul's preedit string.
568 // libhangul only supports one syllable preedit string.
569 // In order to make longer preedit string, ibus-hangul maintains
570 // internal preedit string.
571 hic_preedit = hangul_ic_get_preedit_string (hangul->context);
572
573 preedit = ustring_dup (hangul->preedit);
574 preedit_len = ustring_length(preedit);
575 ustring_append_ucs4 (preedit, hic_preedit, -1);
576
577 if (ustring_length(preedit) > 0) {
578 IBusPreeditFocusMode preedit_option = IBUS_ENGINE_PREEDIT_COMMIT;
579
580 if (hangul->hanja_list != NULL)
581 preedit_option = IBUS_ENGINE_PREEDIT_CLEAR;
582
583 text = ibus_text_new_from_ucs4 ((gunichar*)preedit->data);
584 // ibus-hangul's internal preedit string
585 ibus_text_append_attribute (text, IBUS_ATTR_TYPE_UNDERLINE,
586 IBUS_ATTR_UNDERLINE_SINGLE, 0, preedit_len);
587 // Preedit string from libhangul context.
588 // This is currently composing syllable.
589 ibus_text_append_attribute (text, IBUS_ATTR_TYPE_FOREGROUND,
590 0x00ffffff, preedit_len, -1);
591 ibus_text_append_attribute (text, IBUS_ATTR_TYPE_BACKGROUND,
592 0x00000000, preedit_len, -1);
593 ibus_engine_update_preedit_text_with_mode ((IBusEngine *)hangul,
594 text,
595 ibus_text_get_length (text),
596 TRUE,
597 preedit_option);
598 } else {
599 text = ibus_text_new_from_static_string ("");
600 ibus_engine_update_preedit_text ((IBusEngine *)hangul, text, 0, FALSE);
601 }
602
603 ustring_delete(preedit);
604 }
605
606 static void
ibus_hangul_engine_update_lookup_table_ui(IBusHangulEngine * hangul)607 ibus_hangul_engine_update_lookup_table_ui (IBusHangulEngine *hangul)
608 {
609 guint cursor_pos;
610 const char* comment;
611 IBusText* text;
612
613 // update aux text
614 cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
615 comment = hanja_list_get_nth_comment (hangul->hanja_list, cursor_pos);
616
617 text = ibus_text_new_from_string (comment);
618 ibus_engine_update_auxiliary_text ((IBusEngine *)hangul, text, TRUE);
619
620 // update lookup table
621 ibus_engine_update_lookup_table ((IBusEngine *)hangul, hangul->table, TRUE);
622 }
623
624 static void
ibus_hangul_engine_commit_current_candidate(IBusHangulEngine * hangul)625 ibus_hangul_engine_commit_current_candidate (IBusHangulEngine *hangul)
626 {
627 guint cursor_pos;
628 const char* key;
629 const char* value;
630 const ucschar* hic_preedit;
631 glong key_len;
632 glong hic_preedit_len;
633 glong preedit_len;
634
635 IBusText* text;
636
637 cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
638 key = hanja_list_get_nth_key (hangul->hanja_list, cursor_pos);
639 value = hanja_list_get_nth_value (hangul->hanja_list, cursor_pos);
640 hic_preedit = hangul_ic_get_preedit_string (hangul->context);
641
642 key_len = g_utf8_strlen(key, -1);
643 preedit_len = ustring_length(hangul->preedit);
644 hic_preedit_len = ucschar_strlen (hic_preedit);
645
646 if (hangul->last_lookup_method == LOOKUP_METHOD_PREFIX) {
647 if (preedit_len == 0 && hic_preedit_len == 0) {
648 /* remove surrounding_text */
649 if (key_len > 0) {
650 ibus_engine_delete_surrounding_text ((IBusEngine *)hangul,
651 -key_len , key_len);
652 }
653 } else {
654 /* remove ibus preedit text */
655 if (key_len > 0) {
656 glong n = MIN(key_len, preedit_len);
657 ustring_erase (hangul->preedit, 0, n);
658 key_len -= preedit_len;
659 }
660
661 /* remove hic preedit text */
662 if (key_len > 0) {
663 hangul_ic_reset (hangul->context);
664 key_len -= hic_preedit_len;
665 }
666 }
667 } else {
668 /* remove hic preedit text */
669 if (hic_preedit_len > 0) {
670 hangul_ic_reset (hangul->context);
671 key_len -= hic_preedit_len;
672 }
673
674 /* remove ibus preedit text */
675 if (key_len > preedit_len) {
676 ustring_erase (hangul->preedit, 0, preedit_len);
677 key_len -= preedit_len;
678 } else if (key_len > 0) {
679 ustring_erase (hangul->preedit, 0, key_len);
680 key_len = 0;
681 }
682
683 /* remove surrounding_text */
684 if (key_len > 0) {
685 ibus_engine_delete_surrounding_text ((IBusEngine *)hangul,
686 -key_len , key_len);
687 }
688 }
689
690 /* clear preedit text before commit */
691 ibus_hangul_engine_clear_preedit_text (hangul);
692
693 text = ibus_text_new_from_string (value);
694 ibus_engine_commit_text ((IBusEngine *)hangul, text);
695
696 ibus_hangul_engine_update_preedit_text (hangul);
697 }
698
699 static gchar*
h_ibus_text_get_substring(IBusText * ibus_text,glong p1,glong p2)700 h_ibus_text_get_substring (IBusText* ibus_text, glong p1, glong p2)
701 {
702 const gchar* text;
703 const gchar* begin;
704 const gchar* end;
705 gchar* substring;
706 glong limit;
707 glong pos;
708 glong n;
709
710 text = ibus_text_get_text (ibus_text);
711 limit = ibus_text_get_length (ibus_text) + 1;
712 if (text == NULL || limit == 0)
713 return NULL;
714
715 p1 = MAX(0, p1);
716 p2 = MAX(0, p2);
717
718 pos = MIN(p1, p2);
719 n = ABS(p2 - p1);
720
721 if (pos + n > limit)
722 n = limit - pos;
723
724 begin = g_utf8_offset_to_pointer (text, pos);
725 end = g_utf8_offset_to_pointer (begin, n);
726
727 substring = g_strndup (begin, end - begin);
728 return substring;
729 }
730
731 static HanjaList*
ibus_hangul_engine_lookup_hanja_table(const char * key,int method)732 ibus_hangul_engine_lookup_hanja_table (const char* key, int method)
733 {
734 HanjaList* list = NULL;
735
736 if (key == NULL)
737 return NULL;
738
739 switch (method) {
740 case LOOKUP_METHOD_EXACT:
741 if (symbol_table != NULL)
742 list = hanja_table_match_exact (symbol_table, key);
743
744 if (list == NULL)
745 list = hanja_table_match_exact (hanja_table, key);
746
747 break;
748 case LOOKUP_METHOD_PREFIX:
749 if (symbol_table != NULL)
750 list = hanja_table_match_prefix (symbol_table, key);
751
752 if (list == NULL)
753 list = hanja_table_match_prefix (hanja_table, key);
754
755 break;
756 case LOOKUP_METHOD_SUFFIX:
757 if (symbol_table != NULL)
758 list = hanja_table_match_suffix (symbol_table, key);
759
760 if (list == NULL)
761 list = hanja_table_match_suffix (hanja_table, key);
762
763 break;
764 }
765
766 return list;
767 }
768
769 static void
ibus_hangul_engine_update_hanja_list(IBusHangulEngine * hangul)770 ibus_hangul_engine_update_hanja_list (IBusHangulEngine *hangul)
771 {
772 gchar* hanja_key;
773 gchar* preedit_utf8;
774 const ucschar* hic_preedit;
775 UString* preedit;
776 int lookup_method;
777 IBusText* ibus_text = NULL;
778 guint cursor_pos = 0;
779 guint anchor_pos = 0;
780
781 if (hangul->hanja_list != NULL) {
782 hanja_list_delete (hangul->hanja_list);
783 hangul->hanja_list = NULL;
784 }
785
786 hic_preedit = hangul_ic_get_preedit_string (hangul->context);
787
788 hanja_key = NULL;
789 lookup_method = LOOKUP_METHOD_PREFIX;
790
791 preedit = ustring_dup (hangul->preedit);
792 ustring_append_ucs4 (preedit, hic_preedit, -1);
793
794 if (ustring_length(preedit) > 0) {
795 preedit_utf8 = ustring_to_utf8 (preedit, -1);
796 if (word_commit || hangul->hanja_mode) {
797 hanja_key = preedit_utf8;
798 lookup_method = LOOKUP_METHOD_PREFIX;
799 } else {
800 gchar* substr;
801 ibus_engine_get_surrounding_text ((IBusEngine *)hangul, &ibus_text,
802 &cursor_pos, &anchor_pos);
803
804 substr = h_ibus_text_get_substring (ibus_text,
805 cursor_pos - 64, cursor_pos);
806
807 if (substr != NULL) {
808 hanja_key = g_strconcat (substr, preedit_utf8, NULL);
809 g_free (preedit_utf8);
810 } else {
811 hanja_key = preedit_utf8;
812 }
813 lookup_method = LOOKUP_METHOD_SUFFIX;
814 }
815 } else {
816 ibus_engine_get_surrounding_text ((IBusEngine *)hangul, &ibus_text,
817 &cursor_pos, &anchor_pos);
818 if (cursor_pos != anchor_pos) {
819 // If we have selection in surrounding text, we use that.
820 hanja_key = h_ibus_text_get_substring (ibus_text,
821 cursor_pos, anchor_pos);
822 lookup_method = LOOKUP_METHOD_EXACT;
823 } else {
824 hanja_key = h_ibus_text_get_substring (ibus_text,
825 cursor_pos - 64, cursor_pos);
826 lookup_method = LOOKUP_METHOD_SUFFIX;
827 }
828 }
829
830 if (hanja_key != NULL) {
831 hangul->hanja_list = ibus_hangul_engine_lookup_hanja_table (hanja_key,
832 lookup_method);
833 hangul->last_lookup_method = lookup_method;
834 g_free (hanja_key);
835 }
836
837 ustring_delete (preedit);
838
839 if (ibus_text != NULL)
840 g_object_unref (ibus_text);
841 }
842
843 static void
ibus_hangul_engine_apply_hanja_list(IBusHangulEngine * hangul)844 ibus_hangul_engine_apply_hanja_list (IBusHangulEngine *hangul)
845 {
846 HanjaList* list = hangul->hanja_list;
847 if (list != NULL) {
848 int i, n;
849 n = hanja_list_get_size (list);
850
851 ibus_lookup_table_clear (hangul->table);
852 for (i = 0; i < n; i++) {
853 const char* value = hanja_list_get_nth_value (list, i);
854 IBusText* text = ibus_text_new_from_string (value);
855 ibus_lookup_table_append_candidate (hangul->table, text);
856 }
857
858 ibus_lookup_table_set_cursor_pos (hangul->table, 0);
859 ibus_hangul_engine_update_lookup_table_ui (hangul);
860 lookup_table_set_visible (hangul->table, TRUE);
861 }
862 }
863
864 static void
ibus_hangul_engine_hide_lookup_table(IBusHangulEngine * hangul)865 ibus_hangul_engine_hide_lookup_table (IBusHangulEngine *hangul)
866 {
867 gboolean is_visible;
868 is_visible = lookup_table_is_visible (hangul->table);
869
870 // Sending hide lookup table message when the lookup table
871 // is not visible results wrong behavior. So I have to check
872 // whether the table is visible or not before to hide.
873 if (is_visible) {
874 ibus_engine_hide_lookup_table ((IBusEngine *)hangul);
875 ibus_engine_hide_auxiliary_text ((IBusEngine *)hangul);
876 lookup_table_set_visible (hangul->table, FALSE);
877 }
878
879 if (hangul->hanja_list != NULL) {
880 hanja_list_delete (hangul->hanja_list);
881 hangul->hanja_list = NULL;
882 }
883 }
884
885 static void
ibus_hangul_engine_update_lookup_table(IBusHangulEngine * hangul)886 ibus_hangul_engine_update_lookup_table (IBusHangulEngine *hangul)
887 {
888 ibus_hangul_engine_update_hanja_list (hangul);
889
890 if (hangul->hanja_list != NULL) {
891 // We should redraw preedit text with IBUS_ENGINE_PREEDIT_CLEAR option
892 // here to prevent committing it on focus out event incidentally.
893 ibus_hangul_engine_update_preedit_text (hangul);
894 ibus_hangul_engine_apply_hanja_list (hangul);
895 } else {
896 ibus_hangul_engine_hide_lookup_table (hangul);
897 }
898 }
899
900 static gboolean
ibus_hangul_engine_process_candidate_key_event(IBusHangulEngine * hangul,guint keyval,guint modifiers)901 ibus_hangul_engine_process_candidate_key_event (IBusHangulEngine *hangul,
902 guint keyval,
903 guint modifiers)
904 {
905 if (keyval == IBUS_Escape) {
906 ibus_hangul_engine_hide_lookup_table (hangul);
907 // When the lookup table is poped up, preedit string is
908 // updated with IBUS_ENGINE_PREEDIT_CLEAR option.
909 // So, when focus is out, the preedit text will not be committed.
910 // To prevent this problem, we have to update preedit text here
911 // with IBUS_ENGINE_PREEDIT_COMMIT option.
912 ibus_hangul_engine_update_preedit_text (hangul);
913 return TRUE;
914 } else if (keyval == IBUS_Return) {
915 ibus_hangul_engine_commit_current_candidate (hangul);
916
917 if (hangul->hanja_mode && ibus_hangul_engine_has_preedit (hangul)) {
918 ibus_hangul_engine_update_lookup_table (hangul);
919 } else {
920 ibus_hangul_engine_hide_lookup_table (hangul);
921 }
922 return TRUE;
923 } else if (keyval >= IBUS_1 && keyval <= IBUS_9) {
924 guint page_no;
925 guint page_size;
926 guint cursor_pos;
927
928 page_size = ibus_lookup_table_get_page_size (hangul->table);
929 cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
930 page_no = cursor_pos / page_size;
931
932 cursor_pos = page_no * page_size + (keyval - IBUS_1);
933 ibus_lookup_table_set_cursor_pos (hangul->table, cursor_pos);
934
935 ibus_hangul_engine_commit_current_candidate (hangul);
936
937 if (hangul->hanja_mode && ibus_hangul_engine_has_preedit (hangul)) {
938 ibus_hangul_engine_update_lookup_table (hangul);
939 } else {
940 ibus_hangul_engine_hide_lookup_table (hangul);
941 }
942 return TRUE;
943 } else if (keyval == IBUS_Page_Up) {
944 ibus_lookup_table_page_up (hangul->table);
945 ibus_hangul_engine_update_lookup_table_ui (hangul);
946 return TRUE;
947 } else if (keyval == IBUS_Page_Down) {
948 ibus_lookup_table_page_down (hangul->table);
949 ibus_hangul_engine_update_lookup_table_ui (hangul);
950 return TRUE;
951 } else {
952 if (lookup_table_orientation == 0) {
953 // horizontal
954 if (keyval == IBUS_Left) {
955 ibus_lookup_table_cursor_up (hangul->table);
956 ibus_hangul_engine_update_lookup_table_ui (hangul);
957 return TRUE;
958 } else if (keyval == IBUS_Right) {
959 ibus_lookup_table_cursor_down (hangul->table);
960 ibus_hangul_engine_update_lookup_table_ui (hangul);
961 return TRUE;
962 } else if (keyval == IBUS_Up) {
963 ibus_lookup_table_page_up (hangul->table);
964 ibus_hangul_engine_update_lookup_table_ui (hangul);
965 return TRUE;
966 } else if (keyval == IBUS_Down) {
967 ibus_lookup_table_page_down (hangul->table);
968 ibus_hangul_engine_update_lookup_table_ui (hangul);
969 return TRUE;
970 }
971 } else {
972 // vertical
973 if (keyval == IBUS_Left) {
974 ibus_lookup_table_page_up (hangul->table);
975 ibus_hangul_engine_update_lookup_table_ui (hangul);
976 return TRUE;
977 } else if (keyval == IBUS_Right) {
978 ibus_lookup_table_page_down (hangul->table);
979 ibus_hangul_engine_update_lookup_table_ui (hangul);
980 return TRUE;
981 } else if (keyval == IBUS_Up) {
982 ibus_lookup_table_cursor_up (hangul->table);
983 ibus_hangul_engine_update_lookup_table_ui (hangul);
984 return TRUE;
985 } else if (keyval == IBUS_Down) {
986 ibus_lookup_table_cursor_down (hangul->table);
987 ibus_hangul_engine_update_lookup_table_ui (hangul);
988 return TRUE;
989 }
990 }
991 }
992
993 if (!hangul->hanja_mode) {
994 if (lookup_table_orientation == 0) {
995 // horizontal
996 if (keyval == IBUS_h) {
997 ibus_lookup_table_cursor_up (hangul->table);
998 ibus_hangul_engine_update_lookup_table_ui (hangul);
999 return TRUE;
1000 } else if (keyval == IBUS_l) {
1001 ibus_lookup_table_cursor_down (hangul->table);
1002 ibus_hangul_engine_update_lookup_table_ui (hangul);
1003 return TRUE;
1004 } else if (keyval == IBUS_k) {
1005 ibus_lookup_table_page_up (hangul->table);
1006 ibus_hangul_engine_update_lookup_table_ui (hangul);
1007 return TRUE;
1008 } else if (keyval == IBUS_j) {
1009 ibus_lookup_table_page_down (hangul->table);
1010 ibus_hangul_engine_update_lookup_table_ui (hangul);
1011 return TRUE;
1012 }
1013 } else {
1014 // vertical
1015 if (keyval == IBUS_h) {
1016 ibus_lookup_table_page_up (hangul->table);
1017 ibus_hangul_engine_update_lookup_table_ui (hangul);
1018 return TRUE;
1019 } else if (keyval == IBUS_l) {
1020 ibus_lookup_table_page_down (hangul->table);
1021 ibus_hangul_engine_update_lookup_table_ui (hangul);
1022 return TRUE;
1023 } else if (keyval == IBUS_k) {
1024 ibus_lookup_table_cursor_up (hangul->table);
1025 ibus_hangul_engine_update_lookup_table_ui (hangul);
1026 return TRUE;
1027 } else if (keyval == IBUS_j) {
1028 ibus_lookup_table_cursor_down (hangul->table);
1029 ibus_hangul_engine_update_lookup_table_ui (hangul);
1030 return TRUE;
1031 }
1032 }
1033 }
1034
1035 return FALSE;
1036 }
1037
1038 static gboolean
ibus_hangul_engine_process_key_event(IBusEngine * engine,guint keyval,guint keycode,guint modifiers)1039 ibus_hangul_engine_process_key_event (IBusEngine *engine,
1040 guint keyval,
1041 guint keycode,
1042 guint modifiers)
1043 {
1044 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1045
1046 guint mask;
1047 gboolean retval;
1048 const ucschar *str;
1049
1050 if (modifiers & IBUS_RELEASE_MASK)
1051 return FALSE;
1052
1053 // if we don't ignore shift keys, shift key will make flush the preedit
1054 // string. So you cannot input shift+key.
1055 // Let's think about these examples:
1056 // dlTek (2 set)
1057 // qhRdmaqkq (2 set)
1058 if (keyval == IBUS_Shift_L || keyval == IBUS_Shift_R)
1059 return FALSE;
1060
1061 // On password mode, we ignore hotkeys
1062 if (hangul->input_purpose == IBUS_INPUT_PURPOSE_PASSWORD)
1063 return IBUS_ENGINE_CLASS (parent_class)->process_key_event (engine, keyval, keycode, modifiers);
1064
1065 // If a hotkey has any modifiers, we ignore that modifier
1066 // keyval, or we cannot make the hanja key work.
1067 // Because when we get the modifier key alone, we commit the
1068 // current preedit string. So after that, even if we get the
1069 // right hanja key event, we don't have preedit string to be changed
1070 // to hanja word.
1071 // See this bug: http://code.google.com/p/ibus/issues/detail?id=1036
1072 if (hotkey_list_has_modifier(&switch_keys, keyval))
1073 return FALSE;
1074
1075 if (hotkey_list_match(&switch_keys, keyval, modifiers)) {
1076 ibus_hangul_engine_switch_input_mode (hangul);
1077 return TRUE;
1078 }
1079
1080 if (hotkey_list_match (&on_keys, keyval, modifiers)) {
1081 ibus_hangul_engine_set_input_mode (hangul, INPUT_MODE_HANGUL);
1082 return FALSE;
1083 }
1084
1085 if (hangul->input_mode == INPUT_MODE_LATIN)
1086 return IBUS_ENGINE_CLASS (parent_class)->process_key_event (engine, keyval, keycode, modifiers);
1087
1088 /* This feature is for vi* users.
1089 * On Esc, the input mode is changed to latin */
1090 if (hotkey_list_match (&off_keys, keyval, modifiers)) {
1091 ibus_hangul_engine_set_input_mode (hangul, INPUT_MODE_LATIN);
1092 /* If we return TRUE, then vi will not receive "ESC" key event. */
1093 return FALSE;
1094 }
1095
1096 if (hotkey_list_has_modifier(&hanja_keys, keyval))
1097 return FALSE;
1098
1099 if (hotkey_list_match(&hanja_keys, keyval, modifiers)) {
1100 if (hangul->hanja_list == NULL) {
1101 ibus_hangul_engine_update_lookup_table (hangul);
1102 } else {
1103 ibus_hangul_engine_hide_lookup_table (hangul);
1104 }
1105 return TRUE;
1106 }
1107
1108 if (hangul->hanja_list != NULL) {
1109 retval = ibus_hangul_engine_process_candidate_key_event (hangul,
1110 keyval, modifiers);
1111 if (hangul->hanja_mode) {
1112 if (retval)
1113 return TRUE;
1114 } else {
1115 return TRUE;
1116 }
1117 }
1118
1119 // If we've got a key event with some modifiers, commit current
1120 // preedit string and ignore this key event.
1121 // So, if you want to add some key event handler, put it
1122 // before this code.
1123 // Ignore key event with control, alt, super or mod5
1124 mask = IBUS_CONTROL_MASK |
1125 IBUS_MOD1_MASK | IBUS_MOD3_MASK | IBUS_MOD4_MASK | IBUS_MOD5_MASK;
1126 if (modifiers & mask) {
1127 ibus_hangul_engine_flush (hangul);
1128 return FALSE;
1129 }
1130
1131 if (keyval == IBUS_BackSpace) {
1132 retval = hangul_ic_backspace (hangul->context);
1133 if (!retval) {
1134 guint preedit_len = ustring_length (hangul->preedit);
1135 if (preedit_len > 0) {
1136 ustring_erase (hangul->preedit, preedit_len - 1, 1);
1137 retval = TRUE;
1138 }
1139 }
1140
1141 ibus_hangul_engine_update_preedit_text (hangul);
1142
1143 if (hangul->hanja_mode) {
1144 if (ibus_hangul_engine_has_preedit (hangul)) {
1145 ibus_hangul_engine_update_lookup_table (hangul);
1146 } else {
1147 ibus_hangul_engine_hide_lookup_table (hangul);
1148 }
1149 }
1150 } else {
1151 // We need to normalize the keyval to US qwerty keylayout,
1152 // because the korean input method is depend on the position of
1153 // each key, not the character. We make the keyval from keycode
1154 // as if the keyboard is US qwerty layout. Then we can assume the
1155 // keyval represent the position of the each key.
1156 // But if the hic is in transliteration mode, then we should not
1157 // normalize the keyval.
1158 bool is_transliteration_mode =
1159 hangul_ic_is_transliteration(hangul->context);
1160 if (!is_transliteration_mode) {
1161 if (keymap != NULL)
1162 keyval = ibus_keymap_lookup_keysym(keymap, keycode, modifiers);
1163 }
1164
1165 // ignore capslock
1166 if (modifiers & IBUS_LOCK_MASK) {
1167 if (keyval >= 'A' && keyval <= 'z') {
1168 if (isupper(keyval))
1169 keyval = tolower(keyval);
1170 else
1171 keyval = toupper(keyval);
1172 }
1173 }
1174 retval = hangul_ic_process (hangul->context, keyval);
1175
1176 str = hangul_ic_get_commit_string (hangul->context);
1177 if (word_commit || hangul->hanja_mode) {
1178 const ucschar* hic_preedit;
1179
1180 hic_preedit = hangul_ic_get_preedit_string (hangul->context);
1181 if (hic_preedit != NULL && hic_preedit[0] != 0) {
1182 ustring_append_ucs4 (hangul->preedit, str, -1);
1183 } else {
1184 IBusText *text;
1185 const ucschar* preedit;
1186
1187 ustring_append_ucs4 (hangul->preedit, str, -1);
1188 if (ustring_length (hangul->preedit) > 0) {
1189 /* clear preedit text before commit */
1190 ibus_hangul_engine_clear_preedit_text (hangul);
1191
1192 preedit = ustring_begin (hangul->preedit);
1193 text = ibus_text_new_from_ucs4 ((gunichar*)preedit);
1194 ibus_engine_commit_text (engine, text);
1195 }
1196 ustring_clear (hangul->preedit);
1197 }
1198 } else {
1199 if (str != NULL && str[0] != 0) {
1200 IBusText *text;
1201
1202 /* clear preedit text before commit */
1203 ibus_hangul_engine_clear_preedit_text (hangul);
1204
1205 text = ibus_text_new_from_ucs4 (str);
1206 ibus_engine_commit_text (engine, text);
1207 }
1208 }
1209
1210 ibus_hangul_engine_update_preedit_text (hangul);
1211
1212 if (hangul->hanja_mode) {
1213 ibus_hangul_engine_update_lookup_table (hangul);
1214 }
1215
1216 if (!retval)
1217 ibus_hangul_engine_flush (hangul);
1218 }
1219
1220 /* We always return TRUE here even if we didn't use this event.
1221 * Instead, we forward the event to clients.
1222 *
1223 * Because IBus has a problem with sync mode.
1224 * I think it's limitations of IBus implementation.
1225 * We call several engine functions(updating preedit text and committing
1226 * text) inside this function.
1227 * But clients cannot receive the results of other calls until this
1228 * function ends. Clients only get one result from a remote call at a time
1229 * because clients may run on event loop.
1230 * Clients may process this event first and then get the results which
1231 * may change the preedit text or commit text.
1232 * So the event order is broken.
1233 * Call order:
1234 * engine client
1235 * call process_key_event
1236 * begin process_key_event
1237 * call commit_text
1238 * call update_preedit_text
1239 * return the event as unused
1240 * receive the result of process_key_event
1241 * receive the result of commit_text
1242 * receive the result of update_preedit_text
1243 *
1244 * To solve this problem, we return TRUE as if we consumed this event.
1245 * After that, we forward this event to clients.
1246 * Then clients may get the events in correct order.
1247 * This approach is a kind of async processing.
1248 * Call order:
1249 * engine client
1250 * call process_key_event
1251 * begin process_key_event
1252 * call commit_text
1253 * call update_preedit_text
1254 * call forward_key_event
1255 * return the event as used
1256 * receive the result of process_key_event
1257 * receive the result of commit_text
1258 * receive the result of update_preedit_text
1259 * receive the forwarded key event
1260 *
1261 * See: https://github.com/choehwanjin/ibus-hangul/issues/40
1262 */
1263 if (use_event_forwarding) {
1264 if (!retval) {
1265 ibus_engine_forward_key_event (engine, keyval, keycode, modifiers);
1266 }
1267
1268 return TRUE;
1269 }
1270
1271 return retval;
1272 }
1273
1274 static void
ibus_hangul_engine_flush(IBusHangulEngine * hangul)1275 ibus_hangul_engine_flush (IBusHangulEngine *hangul)
1276 {
1277 const gunichar *str;
1278 IBusText *text;
1279
1280 ibus_hangul_engine_hide_lookup_table (hangul);
1281
1282 str = hangul_ic_flush (hangul->context);
1283
1284 ustring_append_ucs4 (hangul->preedit, str, -1);
1285
1286 if (ustring_length (hangul->preedit) != 0) {
1287 /* clear preedit text before commit */
1288 ibus_hangul_engine_clear_preedit_text (hangul);
1289
1290 str = ustring_begin (hangul->preedit);
1291 text = ibus_text_new_from_ucs4 (str);
1292
1293 g_debug("flush: %s", text->text);
1294 ibus_engine_commit_text ((IBusEngine *) hangul, text);
1295
1296 ustring_clear(hangul->preedit);
1297 }
1298
1299 ibus_hangul_engine_update_preedit_text (hangul);
1300 }
1301
1302 static void
ibus_hangul_engine_focus_in(IBusEngine * engine)1303 ibus_hangul_engine_focus_in (IBusEngine *engine)
1304 {
1305 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1306
1307 if (hangul->input_mode == INPUT_MODE_HANGUL) {
1308 ibus_property_set_state (hangul->prop_hangul_mode, PROP_STATE_CHECKED);
1309 } else {
1310 ibus_property_set_state (hangul->prop_hangul_mode, PROP_STATE_UNCHECKED);
1311 }
1312
1313 if (hangul->hanja_mode) {
1314 ibus_property_set_state (hangul->prop_hanja_mode, PROP_STATE_CHECKED);
1315 } else {
1316 ibus_property_set_state (hangul->prop_hanja_mode, PROP_STATE_UNCHECKED);
1317 }
1318
1319 ibus_engine_register_properties (engine, hangul->prop_list);
1320
1321 ibus_hangul_engine_update_preedit_text (hangul);
1322
1323 if (hangul->hanja_list != NULL) {
1324 ibus_hangul_engine_update_lookup_table_ui (hangul);
1325 }
1326
1327 IBUS_ENGINE_CLASS (parent_class)->focus_in (engine);
1328 }
1329
1330 static void
ibus_hangul_engine_focus_out(IBusEngine * engine)1331 ibus_hangul_engine_focus_out (IBusEngine *engine)
1332 {
1333 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1334
1335 if (hangul->hanja_list == NULL) {
1336 // ibus-hangul uses
1337 // ibus_engine_update_preedit_text_with_mode() function which makes
1338 // the preedit string committed automatically when the focus is out.
1339 // So we don't need to commit the preedit here.
1340 hangul_ic_reset (hangul->context);
1341 } else {
1342 ibus_engine_hide_lookup_table (engine);
1343 ibus_engine_hide_auxiliary_text (engine);
1344 }
1345
1346 IBUS_ENGINE_CLASS (parent_class)->focus_out ((IBusEngine *) hangul);
1347 }
1348
1349 static void
ibus_hangul_engine_reset(IBusEngine * engine)1350 ibus_hangul_engine_reset (IBusEngine *engine)
1351 {
1352 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1353
1354 ibus_hangul_engine_flush (hangul);
1355 IBUS_ENGINE_CLASS (parent_class)->reset (engine);
1356 }
1357
1358 static void
ibus_hangul_engine_enable(IBusEngine * engine)1359 ibus_hangul_engine_enable (IBusEngine *engine)
1360 {
1361 IBUS_ENGINE_CLASS (parent_class)->enable (engine);
1362
1363 ibus_engine_get_surrounding_text (engine, NULL, NULL, NULL);
1364 }
1365
1366 static void
ibus_hangul_engine_disable(IBusEngine * engine)1367 ibus_hangul_engine_disable (IBusEngine *engine)
1368 {
1369 ibus_hangul_engine_focus_out (engine);
1370 IBUS_ENGINE_CLASS (parent_class)->disable (engine);
1371 }
1372
1373 static void
ibus_hangul_engine_page_up(IBusEngine * engine)1374 ibus_hangul_engine_page_up (IBusEngine *engine)
1375 {
1376 IBUS_ENGINE_CLASS (parent_class)->page_up (engine);
1377 }
1378
1379 static void
ibus_hangul_engine_page_down(IBusEngine * engine)1380 ibus_hangul_engine_page_down (IBusEngine *engine)
1381 {
1382 IBUS_ENGINE_CLASS (parent_class)->page_down (engine);
1383 }
1384
1385 static void
ibus_hangul_engine_cursor_up(IBusEngine * engine)1386 ibus_hangul_engine_cursor_up (IBusEngine *engine)
1387 {
1388 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1389
1390 if (hangul->hanja_list != NULL) {
1391 ibus_lookup_table_cursor_up (hangul->table);
1392 ibus_hangul_engine_update_lookup_table_ui (hangul);
1393 }
1394
1395 IBUS_ENGINE_CLASS (parent_class)->cursor_up (engine);
1396 }
1397
1398 static void
ibus_hangul_engine_cursor_down(IBusEngine * engine)1399 ibus_hangul_engine_cursor_down (IBusEngine *engine)
1400 {
1401 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1402
1403 if (hangul->hanja_list != NULL) {
1404 ibus_lookup_table_cursor_down (hangul->table);
1405 ibus_hangul_engine_update_lookup_table_ui (hangul);
1406 }
1407
1408 IBUS_ENGINE_CLASS (parent_class)->cursor_down (engine);
1409 }
1410
1411 static void
ibus_hangul_engine_property_activate(IBusEngine * engine,const gchar * prop_name,guint prop_state)1412 ibus_hangul_engine_property_activate (IBusEngine *engine,
1413 const gchar *prop_name,
1414 guint prop_state)
1415 {
1416 if (strcmp(prop_name, "setup") == 0) {
1417 GError *error = NULL;
1418 gchar *argv[2] = { NULL, };
1419
1420 argv[0] = "ibus-setup-hangul";
1421 argv[1] = NULL;
1422 g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error);
1423 } else if (strcmp(prop_name, "InputMode") == 0) {
1424 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1425
1426 ibus_hangul_engine_switch_input_mode (hangul);
1427 } else if (strcmp(prop_name, "hanja_mode") == 0) {
1428 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1429
1430 hangul->hanja_mode = !hangul->hanja_mode;
1431 if (hangul->hanja_mode) {
1432 ibus_property_set_state (hangul->prop_hanja_mode,
1433 PROP_STATE_CHECKED);
1434 } else {
1435 ibus_property_set_state (hangul->prop_hanja_mode,
1436 PROP_STATE_UNCHECKED);
1437 }
1438
1439 ibus_engine_update_property (engine, hangul->prop_hanja_mode);
1440 ibus_hangul_engine_flush (hangul);
1441 }
1442 }
1443
1444 static gboolean
ibus_hangul_engine_has_preedit(IBusHangulEngine * hangul)1445 ibus_hangul_engine_has_preedit (IBusHangulEngine *hangul)
1446 {
1447 guint preedit_len;
1448 const ucschar *hic_preedit;
1449
1450 hic_preedit = hangul_ic_get_preedit_string (hangul->context);
1451 if (hic_preedit[0] != 0)
1452 return TRUE;
1453
1454 preedit_len = ustring_length (hangul->preedit);
1455 if (preedit_len > 0)
1456 return TRUE;
1457
1458 return FALSE;
1459 }
1460
1461 static void
ibus_hangul_engine_switch_input_mode(IBusHangulEngine * hangul)1462 ibus_hangul_engine_switch_input_mode (IBusHangulEngine *hangul)
1463 {
1464 int input_mode = hangul->input_mode + 1;
1465
1466 if (input_mode >= INPUT_MODE_COUNT) {
1467 input_mode = INPUT_MODE_HANGUL;
1468 }
1469
1470 ibus_hangul_engine_set_input_mode (hangul, input_mode);
1471 }
1472
1473 static IBusText *
ibus_hangul_engine_get_input_mode_symbol(IBusHangulEngine * hangul,int input_mode)1474 ibus_hangul_engine_get_input_mode_symbol (IBusHangulEngine *hangul,
1475 int input_mode)
1476 {
1477 IBusText **symbols = hangul->input_mode_symbols;
1478
1479 if (symbols[0] == NULL) {
1480 symbols[INPUT_MODE_HANGUL] = ibus_text_new_from_string ("한");
1481 g_object_ref_sink(symbols[INPUT_MODE_HANGUL]);
1482 symbols[INPUT_MODE_LATIN] = ibus_text_new_from_string ("EN");
1483 g_object_ref_sink(symbols[INPUT_MODE_LATIN]);
1484 }
1485
1486 if (input_mode >= INPUT_MODE_COUNT)
1487 return symbols[INPUT_MODE_HANGUL];
1488
1489 return symbols[input_mode];
1490 }
1491
1492 static void
ibus_hangul_engine_set_input_mode(IBusHangulEngine * hangul,int input_mode)1493 ibus_hangul_engine_set_input_mode (IBusHangulEngine *hangul, int input_mode)
1494 {
1495 IBusText* symbol;
1496 IBusProperty* prop;
1497
1498 ibus_hangul_engine_flush (hangul);
1499
1500 if (disable_latin_mode) {
1501 return;
1502 }
1503
1504 prop = hangul->prop_hangul_mode;
1505
1506 hangul->input_mode = input_mode;
1507 g_debug("input_mode: %s", (input_mode == INPUT_MODE_HANGUL) ? "hangul" : "latin");
1508
1509 symbol = ibus_hangul_engine_get_input_mode_symbol (hangul, input_mode);
1510 ibus_property_set_symbol(prop, symbol);
1511
1512 if (hangul->input_mode == INPUT_MODE_HANGUL) {
1513 ibus_property_set_state (prop, PROP_STATE_CHECKED);
1514 } else {
1515 ibus_property_set_state (prop, PROP_STATE_UNCHECKED);
1516 }
1517
1518 ibus_engine_update_property (IBUS_ENGINE (hangul), prop);
1519 }
1520
1521 static bool
ibus_hangul_engine_on_transition(HangulInputContext * hic,ucschar c,const ucschar * preedit,void * data)1522 ibus_hangul_engine_on_transition (HangulInputContext *hic,
1523 ucschar c,
1524 const ucschar *preedit,
1525 void *data)
1526 {
1527 if (!auto_reorder) {
1528 if (hangul_is_choseong (c)) {
1529 if (hangul_ic_has_jungseong (hic) || hangul_ic_has_jongseong (hic))
1530 return false;
1531 }
1532
1533 if (hangul_is_jungseong (c)) {
1534 if (hangul_ic_has_jongseong (hic))
1535 return false;
1536 }
1537 }
1538
1539 return true;
1540 }
1541
1542 static void
settings_changed(GSettings * settings,const gchar * key,gpointer user_data)1543 settings_changed (GSettings *settings,
1544 const gchar *key,
1545 gpointer user_data)
1546 {
1547 IBusHangulEngine *hangul = (IBusHangulEngine *) user_data;
1548 GValue schema_value = G_VALUE_INIT;
1549 const gchar *schema_id;
1550 GVariant *value;
1551
1552 g_return_if_fail (G_IS_SETTINGS (settings));
1553
1554 g_value_init (&schema_value, G_TYPE_STRING);
1555 g_object_get_property (G_OBJECT (settings), "schema-id", &schema_value);
1556 schema_id = g_value_get_string (&schema_value);
1557 value = g_settings_get_value (settings, key);
1558 if (strcmp (schema_id, "org.freedesktop.ibus.engine.hangul") == 0) {
1559 if (strcmp(key, "hangul-keyboard") == 0) {
1560 const gchar *str = g_variant_get_string(value, NULL);
1561 g_string_assign (hangul_keyboard, str);
1562 hangul_ic_select_keyboard (hangul->context, hangul_keyboard->str);
1563 } else if (strcmp (key, "hanja-keys") == 0) {
1564 const gchar* str = g_variant_get_string(value, NULL);
1565 hotkey_list_set_from_string(&hanja_keys, str);
1566 } else if (strcmp (key, "word-commit") == 0) {
1567 word_commit = g_variant_get_boolean (value);
1568 } else if (strcmp (key, "auto-reorder") == 0) {
1569 auto_reorder = g_variant_get_boolean (value);
1570 } else if (strcmp (key, "switch-keys") == 0) {
1571 const gchar* str = g_variant_get_string(value, NULL);
1572 hotkey_list_set_from_string(&switch_keys, str);
1573 } else if (strcmp (key, "on-keys") == 0) {
1574 const gchar* str = g_variant_get_string(value, NULL);
1575 hotkey_list_set_from_string(&on_keys, str);
1576 } else if (strcmp (key, "off-keys") == 0) {
1577 const gchar* str = g_variant_get_string(value, NULL);
1578 hotkey_list_set_from_string(&off_keys, str);
1579 } else if (strcmp (key, "initial-input-mode") == 0) {
1580 const gchar* str = g_variant_get_string (value, NULL);
1581 if (strcmp(str, "latin") == 0) {
1582 initial_input_mode = INPUT_MODE_LATIN;
1583 } else if (strcmp(str, "hangul") == 0) {
1584 initial_input_mode = INPUT_MODE_HANGUL;
1585 }
1586 }
1587 } else if (strcmp (schema_id, "org.freedesktop.ibus.panel") == 0) {
1588 if (strcmp (key, "lookup-table-orientation") == 0) {
1589 lookup_table_orientation = g_variant_get_int32(value);
1590 }
1591 }
1592 g_variant_unref (value);
1593 g_value_unset (&schema_value);
1594 }
1595
1596 static void
lookup_table_set_visible(IBusLookupTable * table,gboolean flag)1597 lookup_table_set_visible (IBusLookupTable *table, gboolean flag)
1598 {
1599 g_object_set_data (G_OBJECT(table), "visible", GUINT_TO_POINTER(flag));
1600 }
1601
1602 static gboolean
lookup_table_is_visible(IBusLookupTable * table)1603 lookup_table_is_visible (IBusLookupTable *table)
1604 {
1605 gpointer res = g_object_get_data (G_OBJECT(table), "visible");
1606 return GPOINTER_TO_UINT(res);
1607 }
1608
1609 static void
key_event_list_append(GArray * list,guint keyval,guint modifiers)1610 key_event_list_append(GArray* list, guint keyval, guint modifiers)
1611 {
1612 struct KeyEvent ev = { keyval, modifiers};
1613 g_array_append_val(list, ev);
1614 }
1615
1616 static gboolean
key_event_list_match(GArray * list,guint keyval,guint modifiers)1617 key_event_list_match(GArray* list, guint keyval, guint modifiers)
1618 {
1619 guint i;
1620 guint mask;
1621
1622 /* ignore capslock and numlock */
1623 mask = IBUS_SHIFT_MASK |
1624 IBUS_CONTROL_MASK |
1625 IBUS_MOD1_MASK |
1626 IBUS_MOD3_MASK |
1627 IBUS_MOD4_MASK |
1628 IBUS_MOD5_MASK;
1629
1630 modifiers &= mask;
1631 for (i = 0; i < list->len; ++i) {
1632 struct KeyEvent* ev = &g_array_index(list, struct KeyEvent, i);
1633 if (ev->keyval == keyval && ev->modifiers == modifiers) {
1634 return TRUE;
1635 }
1636 }
1637
1638 return FALSE;
1639 }
1640
1641 static void
ibus_hangul_engine_candidate_clicked(IBusEngine * engine,guint index,guint button,guint state)1642 ibus_hangul_engine_candidate_clicked (IBusEngine *engine,
1643 guint index,
1644 guint button,
1645 guint state)
1646 {
1647 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1648 if (hangul == NULL)
1649 return;
1650
1651 if (hangul->table == NULL)
1652 return;
1653
1654 ibus_lookup_table_set_cursor_pos (hangul->table, index);
1655 ibus_hangul_engine_commit_current_candidate (hangul);
1656
1657 if (hangul->hanja_mode) {
1658 ibus_hangul_engine_update_lookup_table (hangul);
1659 } else {
1660 ibus_hangul_engine_hide_lookup_table (hangul);
1661 }
1662 }
1663
1664 static void
ibus_hangul_engine_set_content_type(IBusEngine * engine,guint purpose,guint hints)1665 ibus_hangul_engine_set_content_type (IBusEngine *engine,
1666 guint purpose,
1667 guint hints)
1668 {
1669 IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
1670 if (hangul == NULL)
1671 return;
1672
1673 hangul->input_purpose = purpose;
1674 }
1675
1676 static void
hotkey_list_init(HotkeyList * list)1677 hotkey_list_init(HotkeyList* list)
1678 {
1679 list->all_modifiers = 0;
1680 list->keys = g_array_sized_new(FALSE, TRUE, sizeof(struct KeyEvent), 4);
1681 }
1682
1683 static void
hotkey_list_fini(HotkeyList * list)1684 hotkey_list_fini(HotkeyList* list)
1685 {
1686 g_array_free(list->keys, TRUE);
1687 list->keys = NULL;
1688 }
1689
1690 static void
hotkey_list_append_from_string(HotkeyList * list,const char * str)1691 hotkey_list_append_from_string(HotkeyList *list, const char* str)
1692 {
1693 guint keyval = 0;
1694 guint modifiers = 0;
1695 gboolean res;
1696
1697 res = ibus_key_event_from_string(str, &keyval, &modifiers);
1698 if (res) {
1699 hotkey_list_append(list, keyval, modifiers);
1700 }
1701 }
1702
1703 static void
hotkey_list_append(HotkeyList * list,guint keyval,guint modifiers)1704 hotkey_list_append(HotkeyList *list, guint keyval, guint modifiers)
1705 {
1706 list->all_modifiers |= modifiers;
1707 key_event_list_append(list->keys, keyval, modifiers);
1708 }
1709
1710 static void
hotkey_list_set_from_string(HotkeyList * list,const char * str)1711 hotkey_list_set_from_string(HotkeyList *list, const char* str)
1712 {
1713 gchar** items = g_strsplit(str, ",", 0);
1714
1715 list->all_modifiers = 0;
1716 g_array_set_size(list->keys, 0);
1717
1718 if (items != NULL) {
1719 int i;
1720 for (i = 0; items[i] != NULL; ++i) {
1721 hotkey_list_append_from_string(list, items[i]);
1722 }
1723 g_strfreev(items);
1724 }
1725 }
1726
1727 static gboolean
hotkey_list_match(HotkeyList * list,guint keyval,guint modifiers)1728 hotkey_list_match(HotkeyList* list, guint keyval, guint modifiers)
1729 {
1730 return key_event_list_match(list->keys, keyval, modifiers);
1731 }
1732
1733 static gboolean
hotkey_list_has_modifier(HotkeyList * list,guint keyval)1734 hotkey_list_has_modifier(HotkeyList* list, guint keyval)
1735 {
1736 if (list->all_modifiers & IBUS_CONTROL_MASK) {
1737 if (keyval == IBUS_Control_L || keyval == IBUS_Control_R)
1738 return TRUE;
1739 }
1740
1741 if (list->all_modifiers & IBUS_MOD1_MASK) {
1742 if (keyval == IBUS_Alt_L || keyval == IBUS_Alt_R)
1743 return TRUE;
1744 }
1745
1746 if (list->all_modifiers & IBUS_SUPER_MASK) {
1747 if (keyval == IBUS_Super_L || keyval == IBUS_Super_R)
1748 return TRUE;
1749 }
1750
1751 if (list->all_modifiers & IBUS_HYPER_MASK) {
1752 if (keyval == IBUS_Hyper_L || keyval == IBUS_Hyper_R)
1753 return TRUE;
1754 }
1755
1756 if (list->all_modifiers & IBUS_META_MASK) {
1757 if (keyval == IBUS_Meta_L || keyval == IBUS_Meta_R)
1758 return TRUE;
1759 }
1760
1761 return FALSE;
1762 }
1763