textinput/ptihangulcore/src/hangulinputcontext.c
branchRCL_3
changeset 3 f5a1e66df979
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/textinput/ptihangulcore/src/hangulinputcontext.c	Fri Feb 19 23:09:27 2010 +0200
@@ -0,0 +1,1232 @@
+/* libhangul
+ * Copyright (c) 2005,2006 Choe Hwanjin
+ * All rights reserved.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <limits.h>
+
+#include "hangul.h"
+#include "hangulinternals.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define HANGUL_KEYBOARD_TABLE_SIZE 0x80
+
+typedef void   (*HangulOnTranslate)  (HangulInputContext*,
+				      int,
+				      ucschar*,
+				      void*);
+typedef bool   (*HangulOnTransition) (HangulInputContext*,
+				      ucschar,
+				      const ucschar*,
+				      void*);
+
+typedef struct _HangulCombinationItem HangulCombinationItem;
+
+struct _HangulKeyboard {
+    int type;
+    ucschar* table;
+};
+
+struct _HangulCombinationItem {
+    uint32_t key;
+    ucschar code;
+};
+
+struct _HangulCombination {
+    int size;
+    HangulCombinationItem *table;
+};
+
+struct _HangulBuffer {
+    ucschar choseong;
+    ucschar jungseong;
+    ucschar jongseong;
+
+    ucschar stack[12];
+    int     index;
+};
+
+struct _HangulInputContext {
+    int type;
+
+    const HangulKeyboard*    keyboard;
+    const HangulCombination* combination;
+
+    HangulBuffer buffer;
+    int output_mode;
+
+    ucschar preedit_string[64];
+    ucschar commit_string[64];
+    ucschar flushed_string[64];
+
+    HangulOnTranslate   on_translate;
+    void*               on_translate_data;
+
+    HangulOnTransition  on_transition;
+    void*               on_transition_data;
+
+    HangulICFilter filter;
+    void *filter_data;
+
+    unsigned int use_jamo_mode_only : 1;
+};
+
+#include "hangulkeyboard.h"
+
+static const HangulKeyboard hangul_keyboard_2 = {
+    HANGUL_KEYBOARD_TYPE_JAMO,
+    (ucschar*)hangul_keyboard_table_2
+};
+
+static const HangulKeyboard hangul_keyboard_32 = {
+    HANGUL_KEYBOARD_TYPE_JASO,
+    (ucschar*)hangul_keyboard_table_32
+};
+
+static const HangulKeyboard hangul_keyboard_390 = {
+    HANGUL_KEYBOARD_TYPE_JASO,
+    (ucschar*)hangul_keyboard_table_390
+};
+
+static const HangulKeyboard hangul_keyboard_3final = {
+    HANGUL_KEYBOARD_TYPE_JASO,
+    (ucschar*)hangul_keyboard_table_3final
+};
+
+static const HangulKeyboard hangul_keyboard_3sun = {
+    HANGUL_KEYBOARD_TYPE_JASO,
+    (ucschar*)hangul_keyboard_table_3sun
+};
+
+static const HangulKeyboard hangul_keyboard_3yet = {
+    HANGUL_KEYBOARD_TYPE_JASO,
+    (ucschar*)hangul_keyboard_table_3yet
+};
+
+static const HangulCombination hangul_combination_default = {
+    N_ELEMENTS(hangul_combination_table_default),
+    (HangulCombinationItem*)hangul_combination_table_default
+};
+
+static const HangulCombination hangul_combination_full = {
+    N_ELEMENTS(hangul_combination_table_full),
+    (HangulCombinationItem*)hangul_combination_table_full
+};
+
+static void    hangul_buffer_push(HangulBuffer *buffer, ucschar ch);
+static ucschar hangul_buffer_pop (HangulBuffer *buffer);
+static ucschar hangul_buffer_peek(HangulBuffer *buffer);
+
+static void    hangul_buffer_clear(HangulBuffer *buffer);
+static int     hangul_buffer_get_string(HangulBuffer *buffer, ucschar*buf, int buflen);
+static int     hangul_buffer_get_jamo_string(HangulBuffer *buffer, ucschar *buf, int buflen);
+
+static void    hangul_ic_flush_internal(HangulInputContext *hic);
+
+HangulKeyboard*
+hangul_keyboard_new()
+{
+    HangulKeyboard *keyboard = malloc(sizeof(HangulKeyboard));
+    if (keyboard != NULL) {
+	keyboard->table = malloc(sizeof(ucschar) * HANGUL_KEYBOARD_TABLE_SIZE);
+	if (keyboard->table != NULL) {
+	    int i;
+	    for (i = 0; i < HANGUL_KEYBOARD_TABLE_SIZE; i++)
+		keyboard->table[i] = 0;
+
+	    return keyboard;
+	}
+	free(keyboard);
+    }
+
+    return NULL;
+}
+
+static ucschar
+hangul_keyboard_get_value(const HangulKeyboard *keyboard, int key)
+{
+    if (keyboard != NULL) {
+	if (key >= 0 && key < HANGUL_KEYBOARD_TABLE_SIZE)
+	    return keyboard->table[key];
+    }
+
+    return 0;
+}
+
+void
+hangul_keyboard_set_value(HangulKeyboard *keyboard, int key, ucschar value)
+{
+    if (keyboard != NULL) {
+	if (key >= 0 && key < N_ELEMENTS(keyboard->table))
+	    keyboard->table[key] = value;
+    }
+}
+
+static int
+hangul_keyboard_get_type(const HangulKeyboard *keyboard)
+{
+    int type = 0;
+    if (keyboard != NULL) {
+	type = keyboard->type;
+    }
+    return type;
+}
+
+void
+hangul_keyboard_set_type(HangulKeyboard *keyboard, int type)
+{
+    if (keyboard != NULL) {
+	keyboard->type = type;
+    }
+}
+
+void
+hangul_keyboard_delete(HangulKeyboard *keyboard)
+{
+    if (keyboard != NULL)
+	free(keyboard);
+}
+
+HangulCombination*
+hangul_combination_new()
+{
+    HangulCombination *combination = malloc(sizeof(HangulCombination));
+    if (combination != NULL) {
+	combination->size = 0;
+	combination->table = NULL;
+	return combination;
+    }
+
+    return NULL;
+}
+
+void
+hangul_combination_delete(HangulCombination *combination)
+{
+    if (combination != NULL) {
+	if (combination->table != NULL)
+	    free(combination->table);
+	free(combination);
+    }
+}
+
+static uint32_t
+hangul_combination_make_key(ucschar first, ucschar second)
+{
+    return first << 16 | second;
+}
+
+bool
+hangul_combination_set_data(HangulCombination* combination, 
+			    ucschar* first, ucschar* second, ucschar* result,
+			    unsigned int n)
+{
+    if (combination == NULL)
+	return false;
+
+    if (n == 0 || n > ULONG_MAX / sizeof(HangulCombinationItem))
+	return false;
+
+    combination->table = malloc(sizeof(HangulCombinationItem) * n);
+    if (combination->table != NULL) {
+	int i;
+
+	combination->size = n;
+	for (i = 0; i < n; i++) {
+	    combination->table[i].key = hangul_combination_make_key(first[i], second[i]);
+	    combination->table[i].code = result[i];
+	}
+	return true;
+    }
+
+    return false;
+}
+
+static int 
+hangul_combination_cmp(const void* p1, const void* p2)
+{
+    const HangulCombinationItem *item1 = p1;
+    const HangulCombinationItem *item2 = p2;
+    return item1->key - item2->key;
+}
+
+ucschar
+hangul_combination_combine(const HangulCombination* combination,
+			   ucschar first, ucschar second)
+{
+    HangulCombinationItem *res;
+    HangulCombinationItem key;
+
+    if (combination == NULL)
+	return 0;
+
+    key.key = hangul_combination_make_key(first, second);
+    res = bsearch(&key, combination->table, combination->size,
+	          sizeof(combination->table[0]), hangul_combination_cmp);
+    if (res != NULL)
+	return res->code;
+
+    return 0;
+}
+
+static bool
+hangul_buffer_is_empty(HangulBuffer *buffer)
+{
+    return buffer->choseong == 0 && buffer->jungseong == 0 &&
+	   buffer->jongseong == 0;
+}
+
+static bool
+hangul_buffer_has_choseong(HangulBuffer *buffer)
+{
+    return buffer->choseong != 0;
+}
+
+static bool
+hangul_buffer_has_jungseong(HangulBuffer *buffer)
+{
+    return buffer->jungseong != 0;
+}
+
+static bool
+hangul_buffer_has_jongseong(HangulBuffer *buffer)
+{
+    return buffer->jongseong != 0;
+}
+
+static void
+hangul_buffer_push(HangulBuffer *buffer, ucschar ch)
+{
+    if (hangul_is_choseong(ch)) {
+	buffer->choseong = ch;
+    } else if (hangul_is_jungseong(ch)) {
+	buffer->jungseong = ch;
+    } else if (hangul_is_jongseong(ch)) {
+	buffer->jongseong = ch;
+    } else {
+    }
+
+    buffer->stack[++buffer->index] = ch;
+}
+
+static ucschar
+hangul_buffer_pop(HangulBuffer *buffer)
+{
+    return buffer->stack[buffer->index--];
+}
+
+static ucschar
+hangul_buffer_peek(HangulBuffer *buffer)
+{
+    if (buffer->index < 0)
+	return 0;
+
+    return buffer->stack[buffer->index];
+}
+
+static void
+hangul_buffer_clear(HangulBuffer *buffer)
+{
+    buffer->choseong = 0;
+    buffer->jungseong = 0;
+    buffer->jongseong = 0;
+
+    buffer->index = -1;
+    buffer->stack[0]  = 0;
+    buffer->stack[1]  = 0;
+    buffer->stack[2]  = 0;
+    buffer->stack[3]  = 0;
+    buffer->stack[4]  = 0;
+    buffer->stack[5]  = 0;
+    buffer->stack[6]  = 0;
+    buffer->stack[7]  = 0;
+    buffer->stack[8]  = 0;
+    buffer->stack[9]  = 0;
+    buffer->stack[10] = 0;
+    buffer->stack[11] = 0;
+}
+
+static int
+hangul_buffer_get_jamo_string(HangulBuffer *buffer, ucschar *buf, int buflen)
+{
+    int n = 0;
+
+    if (buffer->choseong || buffer->jungseong || buffer->jongseong) {
+	if (buffer->choseong) {
+	    buf[n++] = buffer->choseong;
+	} else {
+	    buf[n++] = HANGUL_CHOSEONG_FILLER;
+	}
+	if (buffer->jungseong) {
+	    buf[n++] = buffer->jungseong;
+	} else {
+	    buf[n++] = HANGUL_JUNGSEONG_FILLER;
+	}
+	if (buffer->jongseong) {
+	    buf[n++] = buffer->jongseong;
+	}
+    }
+
+    buf[n] = 0;
+
+    return n;
+}
+
+static int
+hangul_jaso_to_string(ucschar cho, ucschar jung, ucschar jong,
+		      ucschar *buf, int len)
+{
+    ucschar ch = 0;
+    int n = 0;
+
+    if (cho) {
+	if (jung) {
+	    /* have cho, jung, jong or no jong */
+	    ch = hangul_jaso_to_syllable(cho, jung, jong);
+	    buf[n++] = ch;
+	} else {
+	    if (jong) {
+		/* have cho, jong */
+		ch = hangul_jaso_to_jamo(cho);
+		buf[n++] = ch;
+		ch = hangul_jaso_to_jamo(jong);
+		buf[n++] = ch;
+	    } else {
+		/* have cho */
+		ch = hangul_jaso_to_jamo(cho);
+		buf[n++] = ch;
+	    }
+	}
+    } else {
+	if (jung) {
+	    if (jong) {
+		/* have jung, jong */
+		ch = hangul_jaso_to_jamo(jung);
+		buf[n++] = ch;
+		ch = hangul_jaso_to_jamo(jong);
+		buf[n++] = ch;
+	    } else {
+		/* have jung */
+		ch = hangul_jaso_to_jamo(jung);
+		buf[n++] = ch;
+	    }
+	} else {
+	    if (jong) { 
+		/* have jong */
+		ch = hangul_jaso_to_jamo(jong);
+		buf[n++] = ch;
+	    } else {
+		/* have nothing */
+		buf[n] = 0;
+	    }
+	}
+    }
+    buf[n] = 0;
+
+    return n;
+}
+
+static int
+hangul_buffer_get_string(HangulBuffer *buffer, ucschar *buf, int buflen)
+{
+    return hangul_jaso_to_string(buffer->choseong,
+				 buffer->jungseong,
+				 buffer->jongseong,
+				 buf, buflen);
+}
+
+static bool
+hangul_buffer_backspace(HangulBuffer *buffer)
+{
+    if (buffer->index >= 0) {
+	ucschar ch = hangul_buffer_pop(buffer);
+	if (ch == 0)
+	    return false;
+
+	if (hangul_is_choseong(ch)) {
+	    ch = hangul_buffer_peek(buffer);
+	    buffer->choseong = hangul_is_choseong(ch) ? ch : 0;
+	    return true;
+	} else if (hangul_is_jungseong(ch)) {
+	    ch = hangul_buffer_peek(buffer);
+	    buffer->jungseong = hangul_is_jungseong(ch) ? ch : 0;
+	    return true;
+	} else if (hangul_is_jongseong(ch)) {
+	    ch = hangul_buffer_peek(buffer);
+	    buffer->jongseong = hangul_is_jongseong(ch) ? ch : 0;
+	    return true;
+	}
+    }
+    return false;
+}
+
+static 
+#ifndef __SYMBIAN32__
+inline 
+#endif
+bool
+hangul_ic_push(HangulInputContext *hic, ucschar c)
+{
+    ucschar buf[64] = { 0, };
+    if (hic->on_transition != NULL) {
+	ucschar cho, jung, jong;
+	if (hangul_is_choseong(c)) {
+	    cho  = c;
+	    jung = hic->buffer.jungseong;
+	    jong = hic->buffer.jongseong;
+	} else if (hangul_is_jungseong(c)) {
+	    cho  = hic->buffer.choseong;
+	    jung = c;
+	    jong = hic->buffer.jongseong;
+	} else if (hangul_is_jongseong(c)) {
+	    cho  = hic->buffer.choseong;
+	    jung = hic->buffer.jungseong;
+	    jong = c;
+	} else {
+	    hangul_ic_flush_internal(hic);
+	    return false;
+	}
+
+	hangul_jaso_to_string(cho, jung, jong, buf, N_ELEMENTS(buf));
+	if (!hic->on_transition(hic, c, buf, hic->on_transition_data)) {
+	    hangul_ic_flush_internal(hic);
+	    return false;
+	}
+    } else {
+	if (!hangul_is_jaso(c)) {
+	    hangul_ic_flush_internal(hic);
+	    return false;
+	}
+    }
+
+    hangul_buffer_push(&hic->buffer, c);
+    return true;
+}
+
+static 
+#ifndef __SYMBIAN32__
+inline 
+#endif
+ucschar
+hangul_ic_pop(HangulInputContext *hic)
+{
+    return hangul_buffer_pop(&hic->buffer);
+}
+
+static 
+#ifndef __SYMBIAN32__
+inline 
+#endif
+ucschar
+hangul_ic_peek(HangulInputContext *hic)
+{
+    return hangul_buffer_peek(&hic->buffer);
+}
+
+static 
+#ifndef __SYMBIAN32__
+inline 
+#endif
+void
+hangul_ic_save_preedit_string(HangulInputContext *hic)
+{
+    if (hic->output_mode == HANGUL_OUTPUT_JAMO) {
+	hangul_buffer_get_jamo_string(&hic->buffer,
+				      hic->preedit_string,
+				      N_ELEMENTS(hic->preedit_string));
+    } else {
+	hangul_buffer_get_string(&hic->buffer,
+				 hic->preedit_string,
+				 N_ELEMENTS(hic->preedit_string));
+    }
+}
+
+static 
+#ifndef __SYMBIAN32__
+inline 
+#endif
+void
+hangul_ic_append_commit_string(HangulInputContext *hic, ucschar ch)
+{
+    int i;
+
+    for (i = 0; i < N_ELEMENTS(hic->commit_string); i++) {
+	if (hic->commit_string[i] == 0)
+	    break;
+    }
+
+    if (i + 1 < N_ELEMENTS(hic->commit_string)) {
+	hic->commit_string[i++] = ch;
+	hic->commit_string[i] = 0;
+    }
+}
+
+static 
+#ifndef __SYMBIAN32__
+inline 
+#endif
+void
+hangul_ic_save_commit_string(HangulInputContext *hic)
+{
+    ucschar *string = hic->commit_string;
+    int len = N_ELEMENTS(hic->commit_string);
+
+    while (len > 0) {
+	if (*string == 0)
+	    break;
+	len--;
+	string++;
+    }
+
+    if (hic->output_mode == HANGUL_OUTPUT_JAMO) {
+	hangul_buffer_get_jamo_string(&hic->buffer, string, len);
+    } else {
+	hangul_buffer_get_string(&hic->buffer, string, len);
+    }
+
+    hangul_buffer_clear(&hic->buffer);
+}
+
+static bool
+hangul_ic_process_jamo(HangulInputContext *hic, ucschar ch)
+{
+    ucschar jong;
+    ucschar combined;
+
+    if (!hangul_is_jaso(ch) && ch > 0) {
+	hangul_ic_save_commit_string(hic);
+	hangul_ic_append_commit_string(hic, ch);
+	return true;
+    }
+
+    if (hic->buffer.jongseong) {
+	if (hangul_is_choseong(ch)) {
+	    jong = hangul_choseong_to_jongseong(ch);
+	    combined = hangul_combination_combine(hic->combination,
+					      hic->buffer.jongseong, jong);
+	    if (hangul_is_jongseong(combined)) {
+		if (!hangul_ic_push(hic, combined)) {
+		    if (!hangul_ic_push(hic, ch)) {
+			return false;
+		    }
+		}
+	    } else {
+		hangul_ic_save_commit_string(hic);
+		if (!hangul_ic_push(hic, ch)) {
+		    return false;
+		}
+	    }
+	} else if (hangul_is_jungseong(ch)) {
+	    ucschar pop, peek;
+	    pop = hangul_ic_pop(hic);
+	    peek = hangul_ic_peek(hic);
+
+	    if (hangul_is_jungseong(peek)) {
+		hic->buffer.jongseong = 0;
+		hangul_ic_save_commit_string(hic);
+		hangul_ic_push(hic, hangul_jongseong_to_choseong(pop));
+		if (!hangul_ic_push(hic, ch)) {
+		    return false;
+		}
+	    } else {
+		ucschar choseong = 0, jongseong = 0; 
+		hangul_jongseong_dicompose(hic->buffer.jongseong,
+					   &jongseong, &choseong);
+		hic->buffer.jongseong = jongseong;
+		hangul_ic_save_commit_string(hic);
+		hangul_ic_push(hic, choseong);
+		if (!hangul_ic_push(hic, ch)) {
+		    return false;
+		}
+	    }
+	} else {
+	    goto flush;
+	}
+    } else if (hic->buffer.jungseong) {
+	if (hangul_is_choseong(ch)) {
+	    if (hic->buffer.choseong) {
+		jong = hangul_choseong_to_jongseong(ch);
+		if (hangul_is_jongseong(jong)) {
+		    if (!hangul_ic_push(hic, jong)) {
+			if (!hangul_ic_push(hic, ch)) {
+			    return false;
+			}
+		    }
+		} else {
+		    hangul_ic_save_commit_string(hic);
+		    if (!hangul_ic_push(hic, ch)) {
+			return false;
+		    }
+		}
+	    } else {
+#if 1
+        hangul_ic_save_commit_string(hic);
+        if (!hangul_ic_push(hic, ch)) {
+            return false;
+        }
+#else
+		if (!hangul_ic_push(hic, ch)) {
+		    if (!hangul_ic_push(hic, ch)) {
+			return false;
+		    }
+		}
+#endif
+	    }
+	} else if (hangul_is_jungseong(ch)) {
+	    combined = hangul_combination_combine(hic->combination,
+						  hic->buffer.jungseong, ch);
+	    if (hangul_is_jungseong(combined)) {
+		if (!hangul_ic_push(hic, combined)) {
+		    return false;
+		}
+	    } else {
+		hangul_ic_save_commit_string(hic);
+		if (!hangul_ic_push(hic, ch)) {
+		    return false;
+		}
+	    }
+	} else {
+	    goto flush;
+	}
+    } else if (hic->buffer.choseong) {
+	if (hangul_is_choseong(ch)) {
+	    combined = hangul_combination_combine(hic->combination,
+						  hic->buffer.choseong, ch);
+	    if (!hangul_ic_push(hic, combined)) {
+		if (!hangul_ic_push(hic, ch)) {
+		    return false;
+		}
+	    }
+	} else {
+	    if (!hangul_ic_push(hic, ch)) {
+		if (!hangul_ic_push(hic, ch)) {
+		    return false;
+		}
+	    }
+	}
+    } else {
+	if (!hangul_ic_push(hic, ch)) {
+	    return false;
+	}
+    }
+
+    hangul_ic_save_preedit_string(hic);
+    return true;
+
+flush:
+    hangul_ic_flush_internal(hic);
+    return false;
+}
+
+static bool
+hangul_ic_process_jaso(HangulInputContext *hic, ucschar ch)
+{
+    if (hangul_is_choseong(ch)) {
+	if (hic->buffer.choseong == 0) {
+	    if (!hangul_ic_push(hic, ch)) {
+		if (!hangul_ic_push(hic, ch)) {
+		    return false;
+		}
+	    }
+	} else {
+	    ucschar choseong = 0;
+	    if (hangul_is_choseong(hangul_ic_peek(hic))) {
+		choseong = hangul_combination_combine(hic->combination,
+						  hic->buffer.choseong, ch);
+	    }
+	    if (choseong) {
+		if (!hangul_ic_push(hic, choseong)) {
+		    if (!hangul_ic_push(hic, choseong)) {
+			return false;
+		    }
+		}
+	    } else {
+		hangul_ic_save_commit_string(hic);
+		if (!hangul_ic_push(hic, ch)) {
+		    return false;
+		}
+	    }
+	}
+    } else if (hangul_is_jungseong(ch)) {
+	if (hic->buffer.jungseong == 0) {
+	    if (!hangul_ic_push(hic, ch)) {
+		if (!hangul_ic_push(hic, ch)) {
+		    return false;
+		}
+	    }
+	} else {
+	    ucschar jungseong = 0;
+	    if (hangul_is_jungseong(hangul_ic_peek(hic))) {
+		jungseong = hangul_combination_combine(hic->combination,
+						 hic->buffer.jungseong, ch);
+	    }
+	    if (jungseong) {
+		if (!hangul_ic_push(hic, jungseong)) {
+		    if (!hangul_ic_push(hic, jungseong)) {
+			return false;
+		    }
+		}
+	    } else {
+		hangul_ic_save_commit_string(hic);
+		if (!hangul_ic_push(hic, ch)) {
+		    if (!hangul_ic_push(hic, ch)) {
+			return false;
+		    }
+		}
+	    }
+	}
+    } else if (hangul_is_jongseong(ch)) {
+	if (hic->buffer.jongseong == 0) {
+	    if (!hangul_ic_push(hic, ch)) {
+		if (!hangul_ic_push(hic, ch)) {
+		    return false;
+		}
+	    }
+	} else {
+	    ucschar jongseong = 0;
+	    if (hangul_is_jongseong(hangul_ic_peek(hic))) {
+		jongseong = hangul_combination_combine(hic->combination,
+						   hic->buffer.jongseong, ch);
+	    }
+	    if (jongseong) {
+		if (!hangul_ic_push(hic, jongseong)) {
+		    if (!hangul_ic_push(hic, jongseong)) {
+			return false;
+		    }
+		}
+	    } else {
+		hangul_ic_save_commit_string(hic);
+		if (!hangul_ic_push(hic, ch)) {
+		    if (!hangul_ic_push(hic, ch)) {
+			return false;
+		    }
+		}
+	    }
+	}
+    } else if (ch > 0) {
+	hangul_ic_save_commit_string(hic);
+	hangul_ic_append_commit_string(hic, ch);
+    } else {
+	hangul_ic_save_commit_string(hic);
+	return false;
+    }
+
+    hangul_ic_save_preedit_string(hic);
+    return true;
+}
+
+bool
+hangul_ic_process(HangulInputContext *hic, int ascii)
+{
+    ucschar c;
+
+    if (hic == NULL)
+	return false;
+
+    hic->preedit_string[0] = 0;
+    hic->commit_string[0] = 0;
+
+    c = hangul_keyboard_get_value(hic->keyboard, ascii);
+    if (hic->on_translate != NULL)
+	hic->on_translate(hic, ascii, &c, hic->on_translate_data);
+
+    if (hangul_keyboard_get_type(hic->keyboard) == HANGUL_KEYBOARD_TYPE_JAMO)
+	return hangul_ic_process_jamo(hic, c);
+    else
+	return hangul_ic_process_jaso(hic, c);
+}
+
+const ucschar*
+hangul_ic_get_preedit_string(HangulInputContext *hic)
+{
+    if (hic == NULL)
+	return NULL;
+
+    return hic->preedit_string;
+}
+
+const ucschar*
+hangul_ic_get_commit_string(HangulInputContext *hic)
+{
+    if (hic == NULL)
+	return NULL;
+
+    return hic->commit_string;
+}
+
+void
+hangul_ic_reset(HangulInputContext *hic)
+{
+    if (hic == NULL)
+	return;
+
+    hic->preedit_string[0] = 0;
+    hic->commit_string[0] = 0;
+    hic->flushed_string[0] = 0;
+
+    hangul_buffer_clear(&hic->buffer);
+}
+
+/* append current preedit to the commit buffer.
+ * this function does not clear previously made commit string. */
+static void
+hangul_ic_flush_internal(HangulInputContext *hic)
+{
+    hic->preedit_string[0] = 0;
+
+    hangul_ic_save_commit_string(hic);
+    hangul_buffer_clear(&hic->buffer);
+}
+
+const ucschar*
+hangul_ic_flush(HangulInputContext *hic)
+{
+    if (hic == NULL)
+	return NULL;
+
+    // get the remaining string and clear the buffer
+    hic->preedit_string[0] = 0;
+    hic->commit_string[0] = 0;
+    hic->flushed_string[0] = 0;
+
+    if (hic->output_mode == HANGUL_OUTPUT_JAMO) {
+	hangul_buffer_get_jamo_string(&hic->buffer, hic->flushed_string,
+				 N_ELEMENTS(hic->flushed_string));
+    } else {
+	hangul_buffer_get_string(&hic->buffer, hic->flushed_string,
+				 N_ELEMENTS(hic->flushed_string));
+    }
+
+    hangul_buffer_clear(&hic->buffer);
+
+    return hic->flushed_string;
+}
+
+bool
+hangul_ic_backspace(HangulInputContext *hic)
+{
+    int ret;
+
+    if (hic == NULL)
+	return false;
+
+    hic->preedit_string[0] = 0;
+    hic->commit_string[0] = 0;
+
+    ret = hangul_buffer_backspace(&hic->buffer);
+    if (ret)
+	hangul_ic_save_preedit_string(hic);
+    return ret;
+}
+
+int
+hangul_ic_dvorak_to_qwerty(int qwerty)
+{
+    static const int table[] = {
+	'!',	/* ! */
+	'Q',	/* " */
+	'#',	/* # */
+	'$',	/* $ */
+	'%',	/* % */
+	'&',	/* & */
+	'q',	/* ' */
+	'(',	/* ( */
+	')',	/* ) */
+	'*',	/* * */
+	'}',	/* + */
+	'w',	/* , */
+	'\'',	/* - */
+	'e',	/* . */
+	'[',	/* / */
+	'0',	/* 0 */
+	'1',	/* 1 */
+	'2',	/* 2 */
+	'3',	/* 3 */
+	'4',	/* 4 */
+	'5',	/* 5 */
+	'6',	/* 6 */
+	'7',	/* 7 */
+	'8',	/* 8 */
+	'9',	/* 9 */
+	'Z',	/* : */
+	'z',	/* ; */
+	'W',	/* < */
+	']',	/* = */
+	'E',	/* > */
+	'{',	/* ? */
+	'@',	/* @ */
+	'A',	/* A */
+	'N',	/* B */
+	'I',	/* C */
+	'H',	/* D */
+	'D',	/* E */
+	'Y',	/* F */
+	'U',	/* G */
+	'J',	/* H */
+	'G',	/* I */
+	'C',	/* J */
+	'V',	/* K */
+	'P',	/* L */
+	'M',	/* M */
+	'L',	/* N */
+	'S',	/* O */
+	'R',	/* P */
+	'X',	/* Q */
+	'O',	/* R */
+	':',	/* S */
+	'K',	/* T */
+	'F',	/* U */
+	'>',	/* V */
+	'<',	/* W */
+	'B',	/* X */
+	'T',	/* Y */
+	'?',	/* Z */
+	'-',	/* [ */
+	'\\',	/* \ */
+	'=',	/* ] */
+	'^',	/* ^ */
+	'"',	/* _ */
+	'`',	/* ` */
+	'a',	/* a */
+	'n',	/* b */
+	'i',	/* c */
+	'h',	/* d */
+	'd',	/* e */
+	'y',	/* f */
+	'u',	/* g */
+	'j',	/* h */
+	'g',	/* i */
+	'c',	/* j */
+	'v',	/* k */
+	'p',	/* l */
+	'm',	/* m */
+	'l',	/* n */
+	's',	/* o */
+	'r',	/* p */
+	'x',	/* q */
+	'o',	/* r */
+	';',	/* s */
+	'k',	/* t */
+	'f',	/* u */
+	'.',	/* v */
+	',',	/* w */
+	'b',	/* x */
+	't',	/* y */
+	'/',	/* z */
+	'_',	/* { */
+	'|',	/* | */
+	'+',	/* } */
+	'~'	/* ~ */
+    };
+
+    if (qwerty >= '!' && qwerty <= '~')
+	return table[qwerty - '!'];
+
+    return qwerty;
+}
+
+bool
+hangul_ic_is_empty(HangulInputContext *hic)
+{
+    return hangul_buffer_is_empty(&hic->buffer);
+}
+
+bool
+hangul_ic_has_choseong(HangulInputContext *hic)
+{
+    return hangul_buffer_has_choseong(&hic->buffer);
+}
+
+bool
+hangul_ic_has_jungseong(HangulInputContext *hic)
+{
+    return hangul_buffer_has_jungseong(&hic->buffer);
+}
+
+bool
+hangul_ic_has_jongseong(HangulInputContext *hic)
+{
+    return hangul_buffer_has_jongseong(&hic->buffer);
+}
+
+void
+hangul_ic_set_output_mode(HangulInputContext *hic, int mode)
+{
+    if (hic == NULL)
+	return;
+
+    if (!hic->use_jamo_mode_only)
+	hic->output_mode = mode;
+}
+
+void
+hangul_ic_connect_translate (HangulInputContext* hic,
+                             HangulOnTranslate callback,
+                             void* user_data)
+{
+    if (hic != NULL) {
+	hic->on_translate      = callback;
+	hic->on_translate_data = user_data;
+    }
+}
+
+void
+hangul_ic_connect_transition(HangulInputContext* hic,
+                             HangulOnTransition callback,
+                             void* user_data)
+{
+    if (hic != NULL) {
+	hic->on_transition      = callback;
+	hic->on_transition_data = user_data;
+    }
+}
+
+void hangul_ic_connect_callback(HangulInputContext* hic, const char* event,
+				void* callback, void* user_data)
+{
+    if (hic == NULL || event == NULL)
+	return;
+
+    if (strcasecmp(event, "translate") == 0) {
+	hic->on_translate      = (HangulOnTranslate)callback;
+	hic->on_translate_data = user_data;
+    } else if (strcasecmp(event, "transition") == 0) {
+	hic->on_transition      = (HangulOnTransition)callback;
+	hic->on_transition_data = user_data;
+    }
+}
+
+void hangul_ic_set_filter(HangulInputContext *hic,
+			  HangulICFilter func, void *user_data)
+{
+    return;
+}
+
+void
+hangul_ic_set_keyboard(HangulInputContext *hic, const HangulKeyboard* keyboard)
+{
+    if (hic == NULL || keyboard == NULL)
+	return;
+
+    hic->keyboard = keyboard;
+}
+
+void
+hangul_ic_select_keyboard(HangulInputContext *hic, const char* id)
+{
+    if (hic == NULL)
+	return;
+
+    if (id == NULL)
+	id = "2";
+
+    if (strcmp(id, "32") == 0) {
+	hic->keyboard = &hangul_keyboard_32;
+	hic->combination = &hangul_combination_default;
+	hic->output_mode = HANGUL_OUTPUT_SYLLABLE;
+	hic->use_jamo_mode_only = FALSE;
+    } else if (strcmp(id, "39") == 0) {
+	hic->keyboard = &hangul_keyboard_390;
+	hic->combination = &hangul_combination_default;
+	hic->output_mode = HANGUL_OUTPUT_SYLLABLE;
+	hic->use_jamo_mode_only = FALSE;
+    } else if (strcmp(id, "3f") == 0) {
+	hic->keyboard = &hangul_keyboard_3final;
+	hic->combination = &hangul_combination_default;
+	hic->output_mode = HANGUL_OUTPUT_SYLLABLE;
+	hic->use_jamo_mode_only = FALSE;
+    } else if (strcmp(id, "3s") == 0) {
+	hic->keyboard = &hangul_keyboard_3sun;
+	hic->combination = &hangul_combination_default;
+	hic->output_mode = HANGUL_OUTPUT_SYLLABLE;
+	hic->use_jamo_mode_only = FALSE;
+    } else if (strcmp(id, "3y") == 0) {
+	hic->keyboard = &hangul_keyboard_3yet;
+	hic->combination = &hangul_combination_full;
+	hic->output_mode = HANGUL_OUTPUT_JAMO;
+	hic->use_jamo_mode_only = TRUE;
+    } else {
+	hic->keyboard = &hangul_keyboard_2;
+	hic->combination = &hangul_combination_default;
+	hic->output_mode = HANGUL_OUTPUT_SYLLABLE;
+	hic->use_jamo_mode_only = FALSE;
+    }
+}
+
+void
+hangul_ic_set_combination(HangulInputContext *hic,
+			  const HangulCombination* combination)
+{
+    if (hic == NULL || combination == NULL)
+	return;
+
+    hic->combination = combination;
+}
+
+HangulInputContext*
+hangul_ic_new(const char* keyboard)
+{
+    HangulInputContext *hic;
+    
+    int size=sizeof(HangulInputContext);
+
+    hic = malloc(size);
+    if (hic == NULL)
+	return NULL;
+
+    hic->preedit_string[0] = 0;
+    hic->commit_string[0] = 0;
+    hic->flushed_string[0] = 0;
+
+    hic->on_translate      = NULL;
+    hic->on_translate_data = NULL;
+
+    hic->on_transition      = NULL;
+    hic->on_transition_data = NULL;
+
+    hic->use_jamo_mode_only = FALSE;
+
+    hangul_ic_set_output_mode(hic, HANGUL_OUTPUT_SYLLABLE);
+    hangul_ic_select_keyboard(hic, keyboard);
+
+    hangul_buffer_clear(&hic->buffer);
+
+    return hic;
+}
+
+void
+hangul_ic_delete(HangulInputContext *hic)
+{
+    if (hic == NULL)
+	return;
+
+    free(hic);
+}