/* vim:set et sts=4: */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <ibus.h>
#include <hangul.h>
#include <string.h>
+
+#include "i18n.h"
#include "engine.h"
+#include "ustring.h"
+
typedef struct _IBusHangulEngine IBusHangulEngine;
typedef struct _IBusHangulEngineClass IBusHangulEngineClass;
/* members */
HangulInputContext *context;
+ UString* preedit;
gboolean hangul_mode;
+ gboolean hanja_mode;
+ HanjaList* hanja_list;
IBusLookupTable *table;
+
IBusProperty *hangul_mode_prop;
+ IBusProperty *prop_hanja_mode;
IBusPropList *prop_list;
};
static gboolean
ibus_hangul_engine_process_key_event
(IBusEngine *engine,
- guint keyval,
- guint modifiers);
+ guint keyval,
+ guint keycode,
+ guint modifiers);
static void ibus_hangul_engine_focus_in (IBusEngine *engine);
static void ibus_hangul_engine_focus_out (IBusEngine *engine);
static void ibus_hangul_engine_reset (IBusEngine *engine);
static void ibus_hangul_engine_page_down (IBusEngine *engine);
static void ibus_hangul_engine_cursor_up (IBusEngine *engine);
static void ibus_hangul_engine_cursor_down (IBusEngine *engine);
-static void ibus_hangul_engine_toggle_hangul_mode
- (IBusHangulEngine *hangul);
-#if 0
-static void ibus_hangul_property_activate (IBusEngine *engine,
+static void ibus_hangul_engine_property_activate
+ (IBusEngine *engine,
const gchar *prop_name,
- gint prop_state);
+ guint prop_state);
+#if 0
static void ibus_hangul_engine_property_show
(IBusEngine *engine,
const gchar *prop_name);
static void ibus_hangul_engine_update_preedit_text
(IBusHangulEngine *hangul);
+static void ibus_hangul_engine_update_lookup_table
+ (IBusHangulEngine *hangul);
+static void ibus_config_value_changed (IBusConfig *config,
+ const gchar *section,
+ const gchar *name,
+ GValue *value,
+ gpointer user_data);
+
+static void lookup_table_set_visible (IBusLookupTable *table,
+ gboolean flag);
+static gboolean lookup_table_is_visible (IBusLookupTable *table);
+
static IBusEngineClass *parent_class = NULL;
+static HanjaTable *hanja_table = NULL;
+static HanjaTable *symbol_table = NULL;
+static IBusConfig *config = NULL;
+static GString *hangul_keyboard;
GType
ibus_hangul_engine_get_type (void)
return type;
}
+void
+ibus_hangul_init (IBusBus *bus)
+{
+ gboolean res;
+ GValue value = { 0, };
+
+ hanja_table = hanja_table_load (NULL);
+
+ symbol_table = hanja_table_load (IBUSHANGUL_DATADIR "/data/symbol.txt");
+
+ config = ibus_bus_get_config (bus);
+
+ hangul_keyboard = g_string_new_len ("2", 8);
+ res = ibus_config_get_value (config, "engine/Hangul",
+ "HangulKeyboard", &value);
+ if (res) {
+ const gchar* str = g_value_get_string (&value);
+ g_string_assign (hangul_keyboard, str);
+ }
+}
+
+void
+ibus_hangul_exit (void)
+{
+ hanja_table_delete (hanja_table);
+ hanja_table = NULL;
+
+ hanja_table_delete (symbol_table);
+ symbol_table = NULL;
+
+ g_object_unref (config);
+ config = NULL;
+
+ g_string_free (hangul_keyboard, TRUE);
+ hangul_keyboard = NULL;
+}
+
static void
ibus_hangul_engine_class_init (IBusHangulEngineClass *klass)
{
engine_class->cursor_up = ibus_hangul_engine_cursor_up;
engine_class->cursor_down = ibus_hangul_engine_cursor_down;
+
+ engine_class->property_activate = ibus_hangul_engine_property_activate;
}
static void
ibus_hangul_engine_init (IBusHangulEngine *hangul)
{
- hangul->context = hangul_ic_new ("2");
+ IBusProperty* prop;
+ IBusText* label;
+ IBusText* tooltip;
+
+ hangul->context = hangul_ic_new (hangul_keyboard->str);
+ hangul->preedit = ustring_new();
+ hangul->hanja_list = NULL;
hangul->hangul_mode = TRUE;
- hangul->hangul_mode_prop = ibus_property_new ("hangul_mode_prop",
- PROP_TYPE_NORMAL,
- NULL,
- NULL,
- NULL,
- TRUE,
- FALSE,
- 0,
- NULL);
+ hangul->hanja_mode = FALSE;
hangul->prop_list = ibus_prop_list_new ();
- ibus_prop_list_append (hangul->prop_list, hangul->hangul_mode_prop);
- hangul->table = ibus_lookup_table_new (9, 0, TRUE, TRUE);
+ label = ibus_text_new_from_string (_("Hanja lock"));
+ tooltip = ibus_text_new_from_string (_("Enable/Disable Hanja mode"));
+ prop = ibus_property_new ("hanja_mode",
+ PROP_TYPE_TOGGLE,
+ label,
+ NULL,
+ tooltip,
+ TRUE, TRUE, PROP_STATE_UNCHECKED, NULL);
+ g_object_unref (label);
+ g_object_unref (tooltip);
+ ibus_prop_list_append (hangul->prop_list, prop);
+ hangul->prop_hanja_mode = prop;
+
+ label = ibus_text_new_from_string (_("Setup"));
+ tooltip = ibus_text_new_from_string (_("Configure hangul engine"));
+ prop = ibus_property_new ("setup",
+ PROP_TYPE_NORMAL,
+ label,
+ "gtk-preferences",
+ tooltip,
+ TRUE, TRUE, PROP_STATE_UNCHECKED, NULL);
+ g_object_unref (label);
+ g_object_unref (tooltip);
+ ibus_prop_list_append (hangul->prop_list, prop);
+
+ hangul->table = ibus_lookup_table_new (9, 0, TRUE, FALSE);
+
+ g_signal_connect (config, "value-changed",
+ G_CALLBACK(ibus_config_value_changed), hangul);
}
static GObject*
static void
ibus_hangul_engine_update_preedit_text (IBusHangulEngine *hangul)
{
- const gunichar *str;
+ const ucschar *hic_preedit;
IBusText *text;
-
- str = hangul_ic_get_preedit_string (hangul->context);
-
- if (str != NULL && str[0] != 0) {
- text = ibus_text_new_from_ucs4 (str);
- ibus_text_append_attribute (text, IBUS_ATTR_TYPE_FOREGROUND, 0x00ffffff, 0, -1);
- ibus_text_append_attribute (text, IBUS_ATTR_TYPE_BACKGROUND, 0x00000000, 0, -1);
+ UString *preedit;
+ gint preedit_len;
+
+ // ibus-hangul's preedit string is made up of ibus context's
+ // internal preedit string and libhangul's preedit string.
+ // libhangul only supports one syllable preedit string.
+ // In order to make longer preedit string, ibus-hangul maintains
+ // internal preedit string.
+ hic_preedit = hangul_ic_get_preedit_string (hangul->context);
+
+ preedit = ustring_dup (hangul->preedit);
+ preedit_len = ustring_length(preedit);
+ ustring_append_ucs4 (preedit, hic_preedit, -1);
+
+ if (ustring_length(preedit) > 0) {
+ text = ibus_text_new_from_ucs4 ((gunichar*)preedit->data);
+ // ibus-hangul's internal preedit string
+ ibus_text_append_attribute (text, IBUS_ATTR_TYPE_UNDERLINE,
+ IBUS_ATTR_UNDERLINE_SINGLE, 0, preedit_len);
+ // Preedit string from libhangul context.
+ // This is currently composing syllable.
+ ibus_text_append_attribute (text, IBUS_ATTR_TYPE_FOREGROUND,
+ 0x00ffffff, preedit_len, -1);
+ ibus_text_append_attribute (text, IBUS_ATTR_TYPE_BACKGROUND,
+ 0x00000000, preedit_len, -1);
ibus_engine_update_preedit_text ((IBusEngine *)hangul,
text,
ibus_text_get_length (text),
TRUE);
g_object_unref (text);
- }
- else {
+ } else {
text = ibus_text_new_from_static_string ("");
ibus_engine_update_preedit_text ((IBusEngine *)hangul, text, 0, FALSE);
g_object_unref (text);
}
+
+ ustring_delete(preedit);
+}
+
+static void
+ibus_hangul_engine_update_lookup_table_ui (IBusHangulEngine *hangul)
+{
+ guint cursor_pos;
+ const char* comment;
+ IBusText* text;
+
+ // update aux text
+ cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
+ comment = hanja_list_get_nth_comment (hangul->hanja_list, cursor_pos);
+
+ text = ibus_text_new_from_string (comment);
+ ibus_engine_update_auxiliary_text ((IBusEngine *)hangul, text, TRUE);
+ g_object_unref (text);
+
+ // update lookup table
+ ibus_engine_update_lookup_table ((IBusEngine *)hangul, hangul->table, TRUE);
+}
+
+static void
+ibus_hangul_engine_commit_current_candidate (IBusHangulEngine *hangul)
+{
+ guint cursor_pos;
+ const char* key;
+ const char* value;
+ int key_len;
+ int preedit_len;
+ int len;
+
+ IBusText* text;
+
+ cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
+ key = hanja_list_get_nth_key (hangul->hanja_list, cursor_pos);
+ value = hanja_list_get_nth_value (hangul->hanja_list, cursor_pos);
+
+ key_len = g_utf8_strlen(key, -1);
+ preedit_len = ustring_length(hangul->preedit);
+
+ len = MIN(key_len, preedit_len);
+ ustring_erase (hangul->preedit, 0, len);
+ if (key_len > preedit_len)
+ hangul_ic_reset (hangul->context);
+
+ ibus_hangul_engine_update_preedit_text (hangul);
+
+ text = ibus_text_new_from_string (value);
+ ibus_engine_commit_text ((IBusEngine *)hangul, text);
+ g_object_unref (text);
+}
+
+static void
+ibus_hangul_engine_update_hanja_list (IBusHangulEngine *hangul)
+{
+ char* utf8;
+ const ucschar* hic_preedit;
+ UString* preedit;
+
+ if (hangul->hanja_list != NULL) {
+ hanja_list_delete (hangul->hanja_list);
+ hangul->hanja_list = NULL;
+ }
+
+ hic_preedit = hangul_ic_get_preedit_string (hangul->context);
+
+ preedit = ustring_dup (hangul->preedit);
+ ustring_append_ucs4 (preedit, hic_preedit, -1);
+ if (ustring_length(preedit) > 0) {
+ utf8 = ustring_to_utf8 (preedit, -1);
+ if (utf8 != NULL) {
+ if (symbol_table != NULL)
+ hangul->hanja_list = hanja_table_match_prefix (symbol_table, utf8);
+ if (hangul->hanja_list == NULL)
+ hangul->hanja_list = hanja_table_match_prefix (hanja_table, utf8);
+ g_free (utf8);
+ }
+ }
+
+ ustring_delete (preedit);
+}
+
+
+static void
+ibus_hangul_engine_apply_hanja_list (IBusHangulEngine *hangul)
+{
+ HanjaList* list = hangul->hanja_list;
+ if (list != NULL) {
+ int i, n;
+ n = hanja_list_get_size (list);
+
+ ibus_lookup_table_clear (hangul->table);
+ for (i = 0; i < n; i++) {
+ const char* value = hanja_list_get_nth_value (list, i);
+ IBusText* text = ibus_text_new_from_string (value);
+ ibus_lookup_table_append_candidate (hangul->table, text);
+ g_object_unref (text);
+ }
+
+ ibus_lookup_table_set_cursor_pos (hangul->table, 0);
+ ibus_hangul_engine_update_lookup_table_ui (hangul);
+ lookup_table_set_visible (hangul->table, TRUE);
+ }
+}
+
+static void
+ibus_hangul_engine_hide_lookup_table (IBusHangulEngine *hangul)
+{
+ gboolean is_visible;
+ is_visible = lookup_table_is_visible (hangul->table);
+
+ // Sending hide lookup table message when the lookup table
+ // is not visible results wrong behavior. So I have to check
+ // whether the table is visible or not before to hide.
+ if (is_visible) {
+ ibus_engine_hide_lookup_table ((IBusEngine *)hangul);
+ ibus_engine_hide_auxiliary_text ((IBusEngine *)hangul);
+ lookup_table_set_visible (hangul->table, FALSE);
+ }
+
+ if (hangul->hanja_list != NULL) {
+ hanja_list_delete (hangul->hanja_list);
+ hangul->hanja_list = NULL;
+ }
+}
+
+static void
+ibus_hangul_engine_update_lookup_table (IBusHangulEngine *hangul)
+{
+ ibus_hangul_engine_update_hanja_list (hangul);
+
+ if (hangul->hanja_list != NULL) {
+ ibus_hangul_engine_apply_hanja_list (hangul);
+ } else {
+ ibus_hangul_engine_hide_lookup_table (hangul);
+ }
+}
+
+static gboolean
+ibus_hangul_engine_process_candidate_key_event (IBusHangulEngine *hangul,
+ guint keyval,
+ guint modifiers)
+{
+ if (keyval == IBUS_Escape) {
+ ibus_hangul_engine_hide_lookup_table (hangul);
+ return TRUE;
+ } else if (keyval == IBUS_Return) {
+ ibus_hangul_engine_commit_current_candidate (hangul);
+
+ if (hangul->hanja_mode) {
+ ibus_hangul_engine_update_lookup_table (hangul);
+ } else {
+ ibus_hangul_engine_hide_lookup_table (hangul);
+ }
+ return TRUE;
+ } else if (keyval >= IBUS_1 && keyval <= IBUS_9) {
+ guint page_no;
+ guint page_size;
+ guint cursor_pos;
+
+ page_size = ibus_lookup_table_get_page_size (hangul->table);
+ cursor_pos = ibus_lookup_table_get_cursor_pos (hangul->table);
+ page_no = cursor_pos / page_size;
+
+ cursor_pos = page_no * page_size + (keyval - IBUS_1);
+ ibus_lookup_table_set_cursor_pos (hangul->table, cursor_pos);
+
+ ibus_hangul_engine_commit_current_candidate (hangul);
+
+ if (hangul->hanja_mode) {
+ ibus_hangul_engine_update_lookup_table (hangul);
+ } else {
+ ibus_hangul_engine_hide_lookup_table (hangul);
+ }
+ return TRUE;
+ } else if (keyval == IBUS_Left) {
+ ibus_lookup_table_cursor_up (hangul->table);
+ ibus_hangul_engine_update_lookup_table_ui (hangul);
+ return TRUE;
+ } else if (keyval == IBUS_Right) {
+ ibus_lookup_table_cursor_down (hangul->table);
+ ibus_hangul_engine_update_lookup_table_ui (hangul);
+ return TRUE;
+ } else if (keyval == IBUS_Up) {
+ ibus_lookup_table_page_up (hangul->table);
+ ibus_hangul_engine_update_lookup_table_ui (hangul);
+ return TRUE;
+ } else if (keyval == IBUS_Down) {
+ ibus_lookup_table_page_down (hangul->table);
+ ibus_hangul_engine_update_lookup_table_ui (hangul);
+ return TRUE;
+ } else if (keyval == IBUS_Page_Up) {
+ ibus_lookup_table_page_up (hangul->table);
+ ibus_hangul_engine_update_lookup_table_ui (hangul);
+ return TRUE;
+ } else if (keyval == IBUS_Page_Down) {
+ ibus_lookup_table_page_down (hangul->table);
+ ibus_hangul_engine_update_lookup_table_ui (hangul);
+ return TRUE;
+ }
+
+ if (!hangul->hanja_mode) {
+ if (keyval == IBUS_h) {
+ ibus_lookup_table_cursor_up (hangul->table);
+ ibus_hangul_engine_update_lookup_table_ui (hangul);
+ return TRUE;
+ } else if (keyval == IBUS_l) {
+ ibus_lookup_table_cursor_down (hangul->table);
+ ibus_hangul_engine_update_lookup_table_ui (hangul);
+ return TRUE;
+ } else if (keyval == IBUS_k) {
+ ibus_lookup_table_page_up (hangul->table);
+ ibus_hangul_engine_update_lookup_table_ui (hangul);
+ return TRUE;
+ } else if (keyval == IBUS_j) {
+ ibus_lookup_table_page_down (hangul->table);
+ ibus_hangul_engine_update_lookup_table_ui (hangul);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
}
static gboolean
ibus_hangul_engine_process_key_event (IBusEngine *engine,
guint keyval,
+ guint keycode,
guint modifiers)
{
IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
gboolean retval;
- const gunichar *str;
+ const ucschar *str;
if (modifiers & IBUS_RELEASE_MASK)
return FALSE;
+ // if we don't ignore shift keys, shift key will make flush the preedit
+ // string. So you cannot input shift+key.
+ // Let's think about these examples:
+ // dlTek (2 set)
+ // qhRdmaqkq (2 set)
+ if (keyval == IBUS_Shift_L || keyval == IBUS_Shift_R)
+ return FALSE;
+
+ if (keyval == IBUS_F9 || keyval == IBUS_Hangul_Hanja) {
+ if (hangul->hanja_list == NULL) {
+ ibus_hangul_engine_update_lookup_table (hangul);
+ } else {
+ ibus_hangul_engine_hide_lookup_table (hangul);
+ }
+ return TRUE;
+ }
+
if (modifiers & (IBUS_CONTROL_MASK | IBUS_MOD1_MASK))
return FALSE;
+ if (hangul->hanja_list != NULL) {
+ retval = ibus_hangul_engine_process_candidate_key_event (hangul,
+ keyval, modifiers);
+ if (hangul->hanja_mode) {
+ if (retval)
+ return TRUE;
+ } else {
+ return TRUE;
+ }
+ }
+
if (keyval == IBUS_BackSpace) {
retval = hangul_ic_backspace (hangul->context);
} else {
}
str = hangul_ic_get_commit_string (hangul->context);
- if (str != NULL && str[0] != 0) {
- IBusText *text = ibus_text_new_from_ucs4 (str);
- ibus_engine_commit_text ((IBusEngine *)hangul, text);
- g_object_unref (text);
+ if (hangul->hanja_mode) {
+ const ucschar* hic_preedit;
+
+ hic_preedit = hangul_ic_get_preedit_string (hangul->context);
+ if (hic_preedit != NULL && hic_preedit[0] != 0) {
+ ustring_append_ucs4 (hangul->preedit, str, -1);
+ } else {
+ IBusText *text;
+ const ucschar* preedit;
+
+ ustring_append_ucs4 (hangul->preedit, str, -1);
+ if (ustring_length (hangul->preedit) > 0) {
+ preedit = ustring_begin (hangul->preedit);
+ text = ibus_text_new_from_ucs4 ((gunichar*)preedit);
+ ibus_engine_commit_text (engine, text);
+ g_object_unref (text);
+ }
+ ustring_clear (hangul->preedit);
+ }
+ } else {
+ if (str != NULL && str[0] != 0) {
+ IBusText *text = ibus_text_new_from_ucs4 (str);
+ ibus_engine_commit_text (engine, text);
+ g_object_unref (text);
+ }
}
ibus_hangul_engine_update_preedit_text (hangul);
+ if (hangul->hanja_mode) {
+ ibus_hangul_engine_update_lookup_table (hangul);
+ }
+
if (!retval)
ibus_hangul_engine_flush (hangul);
}
static void
-ibus_hangul_engine_toggle_hangul_mode (IBusHangulEngine *hangul)
-{
- IBusText *text;
- hangul->hangul_mode = ! hangul->hangul_mode;
-
- ibus_hangul_engine_flush (hangul);
-
- if (hangul->hangul_mode) {
- text = ibus_text_new_from_static_string ("한");
- }
- else {
- text = ibus_text_new_from_static_string ("A");
- }
-
- ibus_property_set_label (hangul->hangul_mode_prop, text);
- ibus_engine_update_property ((IBusEngine *)hangul, hangul->hangul_mode_prop);
- g_object_unref (text);
-}
-
-static void
ibus_hangul_engine_focus_in (IBusEngine *engine)
{
IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
+ if (hangul->hanja_mode) {
+ hangul->prop_hanja_mode->state = PROP_STATE_CHECKED;
+ } else {
+ hangul->prop_hanja_mode->state = PROP_STATE_UNCHECKED;
+ }
+
ibus_engine_register_properties (engine, hangul->prop_list);
+ if (hangul->hanja_list != NULL) {
+ ibus_hangul_engine_update_lookup_table_ui (hangul);
+ }
+
parent_class->focus_in (engine);
}
{
IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
- ibus_hangul_engine_flush (hangul);
+ if (hangul->hanja_list == NULL) {
+ ibus_hangul_engine_flush (hangul);
+ } else {
+ ibus_engine_hide_lookup_table (engine);
+ ibus_engine_hide_auxiliary_text (engine);
+ }
parent_class->focus_out ((IBusEngine *) hangul);
}
IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
ibus_hangul_engine_flush (hangul);
+ if (hangul->hanja_list != NULL) {
+ ibus_hangul_engine_hide_lookup_table (hangul);
+ }
parent_class->reset (engine);
}
static void
ibus_hangul_engine_cursor_up (IBusEngine *engine)
{
+ IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
+
+ if (hangul->hanja_list != NULL) {
+ ibus_lookup_table_cursor_up (hangul->table);
+ ibus_hangul_engine_update_lookup_table_ui (hangul);
+ }
+
parent_class->cursor_up (engine);
}
static void
ibus_hangul_engine_cursor_down (IBusEngine *engine)
{
+ IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
+
+ if (hangul->hanja_list != NULL) {
+ ibus_lookup_table_cursor_down (hangul->table);
+ ibus_hangul_engine_update_lookup_table_ui (hangul);
+ }
+
parent_class->cursor_down (engine);
}
+static void
+ibus_hangul_engine_property_activate (IBusEngine *engine,
+ const gchar *prop_name,
+ guint prop_state)
+{
+ if (strcmp(prop_name, "setup") == 0) {
+ GError *error = NULL;
+ gchar *argv[2] = { NULL, };
+ gchar *path;
+ const char* libexecdir;
+
+ libexecdir = g_getenv("LIBEXECDIR");
+ if (libexecdir == NULL)
+ libexecdir = LIBEXECDIR;
+
+ path = g_build_filename(libexecdir, "ibus-setup-hangul", NULL);
+ argv[0] = path;
+ argv[1] = NULL;
+ g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, NULL, &error);
+
+ g_free(path);
+ } else if (strcmp(prop_name, "hanja_mode") == 0) {
+ IBusHangulEngine *hangul = (IBusHangulEngine *) engine;
+
+ hangul->hanja_mode = !hangul->hanja_mode;
+ if (hangul->hanja_mode) {
+ hangul->prop_hanja_mode->state = PROP_STATE_CHECKED;
+ } else {
+ hangul->prop_hanja_mode->state = PROP_STATE_UNCHECKED;
+ }
+
+ ibus_engine_update_property (engine, hangul->prop_hanja_mode);
+ }
+}
+
+static void
+ibus_config_value_changed (IBusConfig *config,
+ const gchar *section,
+ const gchar *name,
+ GValue *value,
+ gpointer user_data)
+{
+ IBusHangulEngine *hangul = (IBusHangulEngine *) user_data;
+
+ if (strcmp(section, "engine/Hangul") == 0) {
+ if (strcmp(name, "HangulKeyboard") == 0) {
+ const gchar *str = g_value_get_string (value);
+ g_string_assign (hangul_keyboard, str);
+ hangul_ic_select_keyboard (hangul->context, hangul_keyboard->str);
+ }
+ }
+}
+
+static void
+lookup_table_set_visible (IBusLookupTable *table, gboolean flag)
+{
+ g_object_set_data (G_OBJECT(table), "visible", GUINT_TO_POINTER(flag));
+}
+
+static gboolean
+lookup_table_is_visible (IBusLookupTable *table)
+{
+ gpointer res = g_object_get_data (G_OBJECT(table), "visible");
+ return GPOINTER_TO_UINT(res);
+}