0.2 snapshot
[himkr] / src / him-vkb-korean.c
diff --git a/src/him-vkb-korean.c b/src/him-vkb-korean.c
new file mode 100644 (file)
index 0000000..a7c3aa7
--- /dev/null
@@ -0,0 +1,1359 @@
+/*
+ * This file is part of hildon-input-method-plugins-example
+ *
+ * Copyright (C) 2006-2007 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Mohammad Anwari <Mohammad.Anwari@nokia.com>
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+ *
+    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    * Neither the name of Nokia Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+* THE POSSIBILITY OF SUCH DAMAGE.
+* 
+*/
+
+#include "hildon-im-plugin.h"
+#include "hildon-im-ui.h"
+
+#include <string.h>
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtkwidget.h>
+
+#define KOREAN_VKB_TYPE korean_vkb_get_type()
+#define KOREAN_VKB(obj) GTK_CHECK_CAST(obj, korean_vkb_get_type(), KoreanVKB)
+#define KOREAN_VKB_CLASS(klass) \
+        GTK_CHECK_CLASS_CAST(klass, korean_vkb_get_type, \
+                             KoreanVKBClass)
+#define IS_KOREAN_VKB(obj) \
+        GTK_CHECK_TYPE(obj, korean_vkb_get_type())
+#define KOREAN_VKB_GET_PRIVATE(obj) \
+        (G_TYPE_INSTANCE_GET_PRIVATE ((obj), KOREAN_VKB_TYPE,\
+                                      KoreanVKBPrivate))
+
+#define KEYBOARD_ROWS 4
+#define MAX_ROW 13
+#define NUM_KEYS 48
+
+
+enum {
+  CASE_EN_LOWER,
+  CASE_EN_UPPER,
+  CASE_KO_LOWER,
+  CASE_KO_UPPER,
+  CASE_MAX
+};
+
+enum {
+  STATE_0, /* Doubles as OTHER state */
+  STATE_CHO,
+  STATE_JUNG,
+  STATE_iJUNG,
+  STATE_JONG
+};
+
+enum {
+  KEY_OTHER,
+  KEY_KO_JA,
+  KEY_KO_MO
+};
+
+
+
+/*static const*/ gchar *keyboard_layout [CASE_MAX] = {
+  "`1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./  ",
+  "~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:\"ZXCVBNM<>?  ",
+  "`1234567890-=ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔ[]\\ㅁㄴㅇㄹㅎㅗㅓㅏㅣ;'ㅋㅌㅊㅍㅠㅜㅡ,./  ",
+  "~!@#$%^&*()_+ㅃㅉㄸㄲㅆㅛㅕㅑㅒㅖ{}|ㅁㄴㅇㄹㅎㅗㅓㅏㅣ:\"ㅋㅌㅊㅍㅠㅜㅡ<>?  "
+};
+
+static const gchar *key_type [CASE_MAX] = {
+  "oooooooooooooooooooooooooooooooooooooooooooooooo",
+  "oooooooooooooooooooooooooooooooooooooooooooooooo",
+  "ooooooooooooojjjjjmmmmmooojjjjjmmmmoojjjjmmmoooo",
+  "ooooooooooooojjjjjmmmmmooojjjjjmmmmoojjjjmmmoooo"
+};
+
+static const gint key_code [CASE_MAX][NUM_KEYS] = {
+  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+   -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},
+  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+   -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},
+  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+    717,1222, 307,   1, 919,120000, 60000, 20000, 10000, 50000,-1,-1,-1,
+    616, 204,1121, 508,1827, 80000, 40000,     0,200000,-1,-1,
+   1524,1625,1423,1726,170000,130000,180000,-1,-1,-1,-1},
+  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+    828,1328, 428, 102,1020,120000, 60000, 20000, 30000, 70000,-1,-1,-1,
+    616, 204,1121, 508,1827, 80000, 40000,     0,200000,-1,-1,
+   1524,1625,1423,1726,170000,130000,180000,-1,-1,-1,-1}
+};
+
+static const gint row_num_keys [KEYBOARD_ROWS] = {
+  13, 13, 11, 11
+};
+
+static const gint row_offset [KEYBOARD_ROWS] = {
+  0, 0, 15, 30
+};
+
+
+typedef struct _vkbkey VKBKey;
+
+static GType korean_vkb_type = 0;
+static GtkWidgetClass *parent_class = NULL;
+
+typedef struct {
+  GtkWidgetClass parent;
+} KoreanVKBClass;
+
+typedef struct {
+  GtkWidget parent;
+} KoreanVKB;
+
+typedef struct {
+  HildonIMUI        *ui;
+  GtkStyle          *style;
+  VKBKey            *keys,
+                    *last_key;
+  PangoLayout       *layout;
+
+  gint               case_mode;
+  gint               total_keys;
+  gint               state;
+
+  gint               ko_cho;
+  gint               ko_jung;
+  gint               ko_jong;
+} KoreanVKBPrivate;
+
+struct _vkbkey {
+/*
+  gchar             *upper;
+  gint               upper_length;
+  gchar             *lower;
+  gint               x, y, width, height;
+*/
+  gchar             *ch[CASE_MAX];
+  gint               ch_length[CASE_MAX];
+  gint               code[CASE_MAX];
+  gint               type[CASE_MAX];
+
+  gint               row, col, len;
+};
+
+GType korean_vkb_get_type (void);
+GtkWidget *korean_vkb_new (HildonIMUI *kbd);
+
+/* Prototype declarations */
+static void korean_vkb_iface_init (HildonIMPluginIface *iface);
+static void korean_vkb_class_init (KoreanVKBClass *klass);
+static void finalize(GObject *obj);
+static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+static void set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+static void korean_vkb_init (KoreanVKB *vkb);
+static void enable (HildonIMPlugin *plugin, gboolean init);
+static void disable(HildonIMPlugin *plugin);
+static void input_mode_changed (HildonIMPlugin *plugin);
+static void clear(HildonIMPlugin *plugin);
+static void client_widget_changed (HildonIMPlugin *plugin);
+static void save_data(HildonIMPlugin *plugin);
+static void mode_a(HildonIMPlugin *plugin); 
+static void mode_b(HildonIMPlugin *plugin);
+static void language (HildonIMPlugin *plugin);
+static void language_settings_changed (HildonIMPlugin *plugin, gint index);
+static void backspace (HildonIMPlugin *plugin);
+static void enter (HildonIMPlugin *plugin);
+static void tab (HildonIMPlugin *plugin);
+static void settings_changed (HildonIMPlugin *plugin, const gchar *key, const GConfValue *value);
+
+static void build_keys (KoreanVKB *);
+
+static void draw_keyboard (KoreanVKB *);
+static void draw_ko_keyboard (KoreanVKB *);
+static void update_layout (KoreanVKB *);
+static void send_key (KoreanVKB *, VKBKey *key);
+/* static void send_ko_key (KoreanVKB *, gint code); */
+static void send_ko_key (KoreanVKBPrivate *priv);
+
+
+static gboolean expose_cb (GtkWidget *, GdkEventExpose *);
+static gboolean destroy_cb (GtkWidget *, GdkEventAny *);
+static void realize_cb (GtkWidget *);
+static void size_allocate_cb (GtkWidget *, GtkAllocation *);
+static gboolean button_press_cb (GtkWidget *, GdkEventButton *);
+static gboolean button_release_cb (GtkWidget *, GdkEventButton *);
+
+/**
+ * Module functions
+ *
+ **/
+
+HildonIMPlugin* 
+module_create (HildonIMUI *keyboard)
+{
+  return HILDON_IM_PLUGIN (korean_vkb_new (keyboard));
+}
+
+void
+module_exit(void)
+{
+  /* empty */
+}
+
+void
+module_init(GTypeModule *module)
+{
+  static const GTypeInfo type_info = {
+    sizeof(KoreanVKBClass),
+    NULL, /* base_init */
+    NULL, /* base_finalize */
+    (GClassInitFunc) korean_vkb_class_init,
+    NULL, /* class_finalize */
+    NULL, /* class_data */
+    sizeof(KoreanVKB),
+    0, /* n_preallocs */
+    (GInstanceInitFunc) korean_vkb_init,
+  };
+  
+  static const GInterfaceInfo plugin_info = {
+    (GInterfaceInitFunc) korean_vkb_iface_init,
+    NULL, /* interface_finalize */
+    NULL, /* interface_data */
+  };
+
+  korean_vkb_type =
+          g_type_module_register_type(module,
+                                      GTK_TYPE_WIDGET, "KoreanVKB",
+                                      &type_info,
+                                      0);
+  
+  g_type_module_add_interface(module,
+                              KOREAN_VKB_TYPE,
+                              HILDON_IM_TYPE_PLUGIN,
+                              &plugin_info);
+}
+
+GType
+korean_vkb_get_type (void)
+{
+  return korean_vkb_type;
+}
+
+/* Standard GTK stuff */
+static void
+korean_vkb_iface_init (HildonIMPluginIface *iface)
+{
+  iface->enable = enable;
+  iface->disable = disable;
+  iface->enter = enter;
+  iface->tab = tab;
+  iface->backspace = backspace;
+  iface->clear = clear;
+  iface->input_mode_changed = input_mode_changed;
+  iface->client_widget_changed = client_widget_changed;
+  iface->save_data = save_data;
+  iface->language = language;
+  iface->mode_a = mode_a;
+  iface->mode_b = mode_b;
+  iface->language_settings_changed = language_settings_changed;
+  iface->settings_changed = settings_changed;
+
+  return;
+}
+
+static void
+korean_vkb_class_init (KoreanVKBClass *klass)
+{
+  GObjectClass *object_class;
+  GtkObjectClass *gtk_object_class;
+  GtkWidgetClass *widget_class;
+
+  parent_class = g_type_class_peek_parent(klass);
+  g_type_class_add_private(klass, sizeof(KoreanVKBPrivate));
+
+  object_class = G_OBJECT_CLASS(klass);
+  gtk_object_class = GTK_OBJECT_CLASS(klass);
+  widget_class = GTK_WIDGET_CLASS(klass);
+
+  object_class->set_property  = set_property;
+  object_class->get_property  = get_property;
+  object_class->finalize      = finalize;
+
+  widget_class->realize       = realize_cb;
+  widget_class->destroy_event = destroy_cb;
+  widget_class->expose_event  = expose_cb;
+  widget_class->size_allocate = size_allocate_cb;
+
+  widget_class->button_press_event    = button_press_cb;
+  widget_class->button_release_event  = button_release_cb;
+
+  g_object_class_install_property(object_class, HILDON_IM_PROP_UI,
+                                  g_param_spec_object(HILDON_IM_PROP_UI_DESCRIPTION, 
+                                  HILDON_IM_PROP_UI_DESCRIPTION,
+                                  "Keyboard that uses plugin",
+                                  HILDON_IM_TYPE_UI,
+                                  G_PARAM_READWRITE
+                                  | G_PARAM_CONSTRUCT_ONLY));  
+}
+
+/* Input Method plugin information.
+ * This structure tells the main UI about this plugin */
+const HildonIMPluginInfo *
+hildon_im_plugin_get_info(void)
+{
+  static const HildonIMPluginInfo info =
+  {
+    "HIM VKB Korean",                   /* description */
+    "korean_vkb",                       /* name */
+    "Keyboard (ko)",                    /* menu title */
+    NULL,                               /* gettext domain */
+    TRUE,                               /* visible in menu */
+    FALSE,                              /* cached */
+    HILDON_IM_TYPE_DEFAULT,             /* UI type */
+    HILDON_IM_GROUP_LATIN,              /* group */
+    HILDON_IM_DEFAULT_PLUGIN_PRIORITY,  /* priority */
+    NULL,                               /* special character plugin */
+    "",                                 /* help page */
+    FALSE,                              /* disable common UI buttons */
+    HILDON_IM_DEFAULT_HEIGHT,           /* plugin height */
+    HILDON_IM_TRIGGER_STYLUS            /* trigger */
+  };
+
+  return &info;
+}
+
+/*
+ * This function returns the list of available languages supported
+ * by the plugin.
+ * */
+gchar ** 
+hildon_im_plugin_get_available_languages (gboolean *free)
+{
+  static gchar *list[] = { "en_US", "ko_KR", "en_GB", NULL };
+  *free = FALSE;
+
+  return list;
+}
+
+static void 
+finalize(GObject *obj)
+{
+  if (G_OBJECT_CLASS(parent_class)->finalize)
+  {
+    G_OBJECT_CLASS(parent_class)->finalize(obj);
+  }
+}
+
+static void
+get_property (GObject *object,
+              guint prop_id,
+              GValue *value,
+              GParamSpec *pspec)
+{
+  KoreanVKBPrivate *priv;
+
+  g_return_if_fail (IS_KOREAN_VKB(object));
+  priv = KOREAN_VKB_GET_PRIVATE(object);
+
+  switch (prop_id)
+  {
+    case HILDON_IM_PROP_UI:
+      g_value_set_object(value, priv->ui);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+set_property(GObject *object,
+             guint prop_id,
+             const GValue *value,
+             GParamSpec *pspec)
+{
+  KoreanVKBPrivate *priv;
+
+  g_return_if_fail (IS_KOREAN_VKB (object));
+  priv = KOREAN_VKB_GET_PRIVATE(object);
+
+  switch (prop_id)
+  {
+    case HILDON_IM_PROP_UI:
+      priv->ui = g_value_get_object(value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+      break;
+  }
+}
+
+/* Implementation of plugin interface starts here */
+static void
+korean_vkb_init (KoreanVKB *vkb)
+{
+  KoreanVKBPrivate *priv;
+
+  g_return_if_fail (IS_KOREAN_VKB (vkb));
+
+  priv = KOREAN_VKB_GET_PRIVATE (vkb);
+
+  priv->keys          = NULL;
+  priv->case_mode     = CASE_EN_LOWER;
+  priv->state         = STATE_0;
+
+  build_keys(vkb);
+}
+
+GtkWidget *
+korean_vkb_new (HildonIMUI *kbd)
+{
+  return g_object_new (KOREAN_VKB_TYPE, HILDON_IM_PROP_UI_DESCRIPTION, kbd, NULL);
+}
+
+/* Called when the plugin is available to the user */
+static void
+enable (HildonIMPlugin *plugin, gboolean init)
+{
+  KoreanVKBPrivate *priv;
+  KoreanVKB *vkb;
+  gint i;
+
+  vkb = KOREAN_VKB (plugin);
+  priv = KOREAN_VKB_GET_PRIVATE (vkb);
+  if (init == TRUE) 
+  {
+    priv->case_mode = CASE_EN_LOWER;
+
+    hildon_im_ui_button_set_toggle (priv->ui, 
+      HILDON_IM_BUTTON_MODE_A, FALSE);
+    hildon_im_ui_button_set_toggle (priv->ui, 
+      HILDON_IM_BUTTON_MODE_B, FALSE);
+    hildon_im_ui_button_set_label (priv->ui, 
+      HILDON_IM_BUTTON_MODE_A, "en");
+    hildon_im_ui_button_set_label (priv->ui, 
+      HILDON_IM_BUTTON_MODE_B, "Shift");
+  }
+
+  hildon_im_ui_send_communication_message(priv->ui,
+    HILDON_IM_CONTEXT_DIRECT_MODE);
+}
+
+/* Called when the plugin is disabled */
+static void
+disable(HildonIMPlugin *plugin)
+{
+  /* not implemented */
+}
+
+/* Called when input mode changed */
+static void
+input_mode_changed (HildonIMPlugin *plugin)
+{
+  /* not implemented */
+}
+
+/* Called when the plugin is requested to 'clear'/refresh its UI */
+static void
+clear(HildonIMPlugin *plugin)
+{
+  /* not implemented */
+}
+
+/* Called when the client widget changed */
+static void
+client_widget_changed (HildonIMPlugin *plugin)
+{
+  /* not implemented */
+}
+
+/* Called when the plugin is requested to save its data */
+static void
+save_data(HildonIMPlugin *plugin)
+{
+  /* not implemented */
+}
+
+/* Called when the MODE_A button is pressed */
+static void
+mode_a(HildonIMPlugin *plugin)
+{
+  KoreanVKBPrivate *priv;
+  KoreanVKB *vkb;
+
+  vkb = KOREAN_VKB (plugin);
+
+  priv = KOREAN_VKB_GET_PRIVATE (vkb);
+
+  switch (priv->case_mode) {
+    case CASE_EN_LOWER: priv->case_mode = CASE_KO_LOWER; break;
+    case CASE_EN_UPPER: priv->case_mode = CASE_KO_UPPER; break;
+    case CASE_KO_LOWER: priv->case_mode = CASE_EN_LOWER; break;
+    case CASE_KO_UPPER: priv->case_mode = CASE_EN_UPPER; break;
+    default: priv->case_mode = CASE_EN_LOWER;
+  }
+
+  update_layout (vkb);
+}
+
+/* Called when the MODE_B button is pressed */
+static void
+mode_b(HildonIMPlugin *plugin)
+{
+  KoreanVKBPrivate *priv;
+  KoreanVKB *vkb;
+
+  vkb = KOREAN_VKB (plugin);
+  priv = KOREAN_VKB_GET_PRIVATE (vkb);
+
+  switch (priv->case_mode) {
+    case CASE_EN_LOWER: priv->case_mode = CASE_EN_UPPER; break;
+    case CASE_EN_UPPER: priv->case_mode = CASE_EN_LOWER; break;
+    case CASE_KO_LOWER: priv->case_mode = CASE_KO_UPPER; break;
+    case CASE_KO_UPPER: priv->case_mode = CASE_KO_LOWER; break;
+    default: priv->case_mode = CASE_EN_LOWER;
+  }
+
+  update_layout (vkb);
+}
+
+/* Called when the language has been changed */
+static void
+language (HildonIMPlugin *plugin)
+{
+  /* not implemented */
+}
+
+/* Called when the language settings has been changed */
+static void
+language_settings_changed (HildonIMPlugin *plugin,
+    gint index)
+{
+  /* not implemented */
+}
+
+/* Called when the backspace button is pressed */
+static void
+backspace (HildonIMPlugin *plugin)
+{
+  KoreanVKBPrivate *priv;
+
+  priv = KOREAN_VKB_GET_PRIVATE (KOREAN_VKB (plugin));
+
+  priv->state = STATE_0;
+
+  hildon_im_ui_send_communication_message (priv->ui, 
+      HILDON_IM_CONTEXT_HANDLE_BACKSPACE);
+}
+
+/* Called when the enter button is pressed */
+static void
+enter (HildonIMPlugin *plugin)
+{
+  KoreanVKBPrivate *priv;
+
+  priv = KOREAN_VKB_GET_PRIVATE (KOREAN_VKB (plugin));
+
+  priv->state = STATE_0;
+
+  hildon_im_ui_send_communication_message (priv->ui, 
+      HILDON_IM_CONTEXT_HANDLE_ENTER);
+}
+
+/* Called when the tab button is pressed */
+static void
+tab (HildonIMPlugin *plugin)
+{
+  KoreanVKBPrivate *priv;
+
+  priv = KOREAN_VKB_GET_PRIVATE (KOREAN_VKB (plugin));
+
+  priv->state = STATE_0;
+
+  hildon_im_ui_send_communication_message (priv->ui, 
+      HILDON_IM_CONTEXT_HANDLE_TAB);
+}
+
+/* Called when the standard input method settings 
+ * has been changed */
+static void
+settings_changed (HildonIMPlugin *plugin,
+    const gchar *key, const GConfValue *value)
+{
+  /* not implemented */
+}
+
+
+
+
+
+
+
+
+/* Supporting functions starts here */
+
+static gint
+char_type (gchar ch)
+{
+  switch (ch) {
+    case 'o':
+      return KEY_OTHER;
+    case 'j':
+      return KEY_KO_JA;
+    case 'm':
+      return KEY_KO_MO;
+  }
+
+  return KEY_OTHER;
+}
+
+static void
+build_keys (KoreanVKB *vkb)
+{
+  KoreanVKBPrivate *priv;
+  VKBKey           *keys;
+  VKBKey           *key;
+  gchar            *tmp_en_upper,
+                   *tmp_en_lower,
+                   *tmp_ko_lower,
+                   *tmp_ko_upper;
+
+  gint i, j, k;
+
+  g_return_if_fail (IS_KOREAN_VKB (vkb));
+  priv = KOREAN_VKB_GET_PRIVATE (vkb);
+
+  /* build priv->total_keys */
+  priv->total_keys = 0;
+  for ( i=0 ; i<KEYBOARD_ROWS ; i++) {
+    priv->total_keys += row_num_keys[i];
+  }
+
+  if (priv->keys == NULL)
+  {
+    priv->keys = g_malloc0 (sizeof(VKBKey) * priv->total_keys);
+  }
+
+  keys = priv->keys;
+  tmp_en_lower = keyboard_layout[CASE_EN_LOWER];
+  tmp_en_upper = keyboard_layout[CASE_EN_UPPER];
+  tmp_ko_lower = keyboard_layout[CASE_KO_LOWER];
+  tmp_ko_upper = keyboard_layout[CASE_KO_UPPER];
+
+  k = 0;
+  for ( j=0 ; j<KEYBOARD_ROWS ; j++ ) {
+    for ( i=0 ; i<row_num_keys[j] ; i++ ) {
+      key = &(keys[k]);
+
+      key->row = j;
+      key->col = i;
+      key->len = 1;
+
+      key->ch[CASE_EN_LOWER] = tmp_en_lower;
+      key->ch[CASE_EN_UPPER] = tmp_en_upper;
+      key->ch[CASE_KO_LOWER] = tmp_ko_lower;
+      key->ch[CASE_KO_UPPER] = tmp_ko_upper;
+
+      tmp_en_lower = g_utf8_find_next_char (tmp_en_lower, NULL);
+      tmp_en_upper = g_utf8_find_next_char (tmp_en_upper, NULL);
+      tmp_ko_lower = g_utf8_find_next_char (tmp_ko_lower, NULL);
+      tmp_ko_upper = g_utf8_find_next_char (tmp_ko_upper, NULL);
+
+      key->ch_length[CASE_EN_LOWER] = tmp_en_lower - key->ch[CASE_EN_LOWER];
+      key->ch_length[CASE_EN_UPPER] = tmp_en_upper - key->ch[CASE_EN_UPPER];
+      key->ch_length[CASE_KO_LOWER] = tmp_ko_lower - key->ch[CASE_KO_LOWER];
+      key->ch_length[CASE_KO_UPPER] = tmp_ko_upper - key->ch[CASE_KO_UPPER];
+
+      key->code[CASE_EN_LOWER] = key_code[CASE_EN_LOWER][k];
+      key->code[CASE_EN_UPPER] = key_code[CASE_EN_UPPER][k];
+      key->code[CASE_KO_LOWER] = key_code[CASE_KO_LOWER][k];
+      key->code[CASE_KO_UPPER] = key_code[CASE_KO_UPPER][k];
+
+      key->type[CASE_EN_LOWER] = char_type(key_type[CASE_EN_LOWER][k]);
+      key->type[CASE_EN_UPPER] = char_type(key_type[CASE_EN_UPPER][k]);
+      key->type[CASE_KO_LOWER] = char_type(key_type[CASE_KO_LOWER][k]);
+      key->type[CASE_KO_UPPER] = char_type(key_type[CASE_KO_UPPER][k]);
+
+      k++;
+    }
+  }
+
+  keys[k-1].len = 2; /* overwrites key length for space bar */
+}
+
+
+
+
+static void 
+draw_key (KoreanVKB *vkb, VKBKey *key, GtkStateType state, gint case_mode)
+{
+  KoreanVKBPrivate *priv;
+  PangoRectangle extents;
+  gint x1, y1, x2, y2, w, h, key_w, key_h, r, c, l, offset;
+
+  g_return_if_fail (IS_KOREAN_VKB (vkb));
+  g_return_if_fail (key != NULL);
+  priv = KOREAN_VKB_GET_PRIVATE (vkb);
+
+
+  pango_layout_set_text (priv->layout,
+                         key->ch[priv->case_mode],
+                         key->ch_length[priv->case_mode]);
+
+  priv->style->depth = gdk_drawable_get_depth (((GtkWidget*)vkb)->window);
+
+  pango_layout_get_pixel_extents(priv->layout, &extents, NULL);
+
+
+  w = GTK_WIDGET(vkb)->allocation.width / MAX_ROW;
+  h = GTK_WIDGET(vkb)->allocation.height / KEYBOARD_ROWS;
+
+  r = key->row;
+  c = key->col;
+  l = key->len;
+  offset = row_offset[r];
+
+  x1 = c * w + offset;
+  x2 = (c+l) * w + offset;
+  y1 = r * h;
+  y2 = (r+1) * h;
+
+  key_w = x2 - x1;
+  key_h = y2 - y1;
+
+  gtk_paint_box (priv->style, 
+      GTK_WIDGET(vkb)->window, 
+      state, 
+      GTK_SHADOW_IN, NULL, GTK_WIDGET(vkb), NULL, 
+      x1, y1, key_w, key_h);
+
+  gtk_paint_layout (GTK_WIDGET(vkb)->style, 
+      GTK_WIDGET(vkb)->window, 
+      state, 
+      TRUE, NULL, GTK_WIDGET(vkb), NULL, 
+      x1 + ((key_w - extents.width) / 2),
+      /* y1 + ((key_h - extents.height) / 2), better to have it above */
+      y1,
+      priv->layout);
+}
+
+static void 
+draw_keyboard (KoreanVKB *vkb)
+{
+  KoreanVKBPrivate *priv;
+  VKBKey *key;
+  gint i;
+
+  g_return_if_fail (IS_KOREAN_VKB (vkb));
+  priv = KOREAN_VKB_GET_PRIVATE (vkb);
+
+  for ( i=0 ; i<priv->total_keys ; i++) {
+    key = &(priv->keys[i]);
+    draw_key (vkb, key, GTK_STATE_NORMAL, priv->case_mode);
+  }
+}
+
+static void 
+update_layout (KoreanVKB *vkb)
+{
+  KoreanVKBPrivate *priv;
+  priv = KOREAN_VKB_GET_PRIVATE (vkb);
+
+  draw_keyboard (vkb);
+
+  switch (priv->case_mode) {
+    case CASE_KO_UPPER:
+    case CASE_KO_LOWER:
+      hildon_im_ui_button_set_label (priv->ui, 
+        HILDON_IM_BUTTON_MODE_A, "ko");
+      break;
+    case CASE_EN_UPPER:
+    case CASE_EN_LOWER:
+    default:
+      hildon_im_ui_button_set_label (priv->ui, 
+        HILDON_IM_BUTTON_MODE_A, "en");
+      break;
+  }
+
+/*
+  Don't really need active buttons...
+
+      hildon_im_ui_button_set_active (priv->ui,
+        HILDON_IM_BUTTON_MODE_A, FALSE);
+      hildon_im_ui_button_set_active (priv->ui,
+        HILDON_IM_BUTTON_MODE_B, TRUE);
+*/
+
+  hildon_im_ui_button_set_sensitive (priv->ui,
+    HILDON_IM_BUTTON_MODE_B, TRUE);
+}
+
+
+
+
+
+static gint
+get_cho (gint code)
+{
+  return code / 100;
+}
+
+static gint
+get_jung (gint code)
+{
+  return code / 10000;
+}
+
+static gint
+get_jong (gint code)
+{
+  gint tmp;
+  tmp = code % 100;
+  if (tmp < 28)
+    return tmp;
+  else
+    return -1;
+}
+
+static void
+erase (KoreanVKBPrivate *priv)
+{
+  hildon_im_ui_send_communication_message (priv->ui, 
+      HILDON_IM_CONTEXT_HANDLE_BACKSPACE);
+
+  usleep(10000);
+}
+
+static gint
+cho2unioff (gint code)
+{
+  switch (code) {
+    case  0:
+    case  1: return code;
+    case  2: return code + 1;
+    case  3:
+    case  4:
+    case  5: return code + 3;
+    case  6:
+    case  7:
+    case  8: return code + 10;
+    case  9:
+    case 10:
+    case 11:
+    case 12:
+    case 13:
+    case 14:
+    case 15:
+    case 16:
+    case 17:
+    case 18: return code + 11;
+  }
+
+  return -1;
+}
+
+static gint
+offset2unicode (gint off)
+{
+    gint b1, b2, b3;
+
+    b1 = (off & 0xf000) << 4;
+    b2 = (off & 0x0fc0) << 2;
+    b3 = off & 0x003f;
+
+    return (0xe08080 | b1 | b2 | b3);
+}
+
+
+
+
+static void
+send_ko_key (KoreanVKBPrivate *priv)
+{
+  gint code;
+  gint tmp;
+#define UTF8_SIZE 6
+  gchar   text [UTF8_SIZE];
+
+
+  if (priv->state == STATE_0) {
+    return;
+  }
+  else if (priv->state == STATE_CHO) {
+    code = 0x3131 + cho2unioff(priv->ko_cho);
+  }
+  else if (priv->state == STATE_iJUNG) {
+    code = 0x314f + priv->ko_jung;
+  }
+  else if (priv->state == STATE_JUNG) {
+    code = 0xac00 + (priv->ko_cho * (0xae4c - 0xac00))
+         + (priv->ko_jung * (0xac1c - 0xac00));
+  }
+  else if (priv->state == STATE_JONG) {
+    code = 0xac00 + (priv->ko_cho * (0xae4c - 0xac00))
+         + (priv->ko_jung * (0xac1c - 0xac00)) + priv->ko_jong;
+  }
+
+  /* printf("code: %x ", code); */
+
+  code = offset2unicode(code);
+
+  text[0] = (code >> 16) & 0xff;
+  text[1] = (code >> 8) & 0xff;
+  text[2] = code & 0xff;
+  text[3] = 0;
+  text[4] = 0;
+  text[5] = 0;
+
+  /* printf("%x %x %x\n", text[0], text[1], text[2]); */
+
+  hildon_im_ui_send_utf8(priv->ui, text);
+}
+
+
+
+static gint
+compose_jung(gint old, gint new)
+{
+  if (old ==  8 && new ==  0) return  9;
+  if (old ==  8 && new ==  1) return 10;
+  if (old ==  8 && new == 20) return 11;
+  if (old == 13 && new ==  4) return 14;
+  if (old == 13 && new ==  5) return 15;
+  if (old == 13 && new == 20) return 16;
+  if (old == 18 && new == 20) return 19;
+  return -1;
+}
+
+static gint
+compose_jong(gint old, gint new)
+{
+  if (old ==  1 && new == 19) return  3;
+  if (old ==  4 && new == 22) return  5;
+  if (old ==  4 && new == 27) return  6;
+  if (old ==  8 && new ==  1) return  9;
+  if (old ==  8 && new == 16) return 10;
+  if (old ==  8 && new == 17) return 11;
+  if (old ==  8 && new == 19) return 12;
+  if (old ==  8 && new == 25) return 13;
+  if (old ==  8 && new == 26) return 14;
+  if (old ==  8 && new == 27) return 15;
+  if (old == 17 && new == 19) return 18;
+  return -1;
+}
+
+static void
+decompose_jong(gint old_jong, gint *jong, gint *cho)
+{
+  switch (old_jong) {
+    case  0: *jong =  0; *cho = -1; break;
+    case  1: *jong =  0; *cho =  0; break;
+    case  2: *jong =  0; *cho =  1; break;
+    case  3: *jong =  1; *cho =  9; break;
+    case  4: *jong =  0; *cho =  2; break;
+    case  5: *jong =  4; *cho = 12; break;
+    case  6: *jong =  4; *cho = 18; break;
+    case  7: *jong =  0; *cho =  3; break;
+    case  8: *jong =  0; *cho =  5; break;
+    case  9: *jong =  8; *cho =  0; break;
+    case 10: *jong =  8; *cho =  6; break;
+    case 11: *jong =  8; *cho =  7; break;
+    case 12: *jong =  8; *cho =  9; break;
+    case 13: *jong =  8; *cho = 16; break;
+    case 14: *jong =  8; *cho = 17; break;
+    case 15: *jong =  8; *cho = 18; break;
+    case 16: *jong =  0; *cho =  6; break;
+    case 17: *jong =  0; *cho =  7; break;
+    case 18: *jong = 17; *cho =  9; break;
+    case 19: *jong =  0; *cho =  9; break;
+    case 20: *jong =  0; *cho = 10; break;
+    case 21: *jong =  0; *cho = 11; break;
+    case 22: *jong =  0; *cho = 12; break;
+    case 23: *jong =  0; *cho = 14; break;
+    case 24: *jong =  0; *cho = 15; break;
+    case 25: *jong =  0; *cho = 16; break;
+    case 26: *jong =  0; *cho = 17; break;
+    case 27: *jong =  0; *cho = 18; break;
+    default: *jong = -1; *cho = -1; break;
+  }
+
+}
+
+
+
+
+static void 
+send_key (KoreanVKB *vkb, VKBKey *key)
+{
+  KoreanVKBPrivate *priv;
+#define UTF8_SIZE 6
+  gchar   text [UTF8_SIZE];
+  gint code, type;
+  gint old_jong, new_cho;
+
+  if (key == NULL)
+    return;
+
+  priv = KOREAN_VKB_GET_PRIVATE (vkb);    
+  code = key->code[priv->case_mode];
+  type = key->type[priv->case_mode];
+
+  memset (text, 0, UTF8_SIZE);
+  memcpy (text, key->ch[priv->case_mode], key->ch_length[priv->case_mode]);
+
+  
+  if (type == KEY_OTHER)
+  {
+    priv->state = STATE_0;
+    hildon_im_ui_send_utf8(priv->ui, text);
+  }
+  else if (type == KEY_KO_JA)
+  {
+    switch (priv->state) {
+
+      case STATE_0:
+        priv->ko_cho = get_cho(code);
+        priv->state = STATE_CHO;
+        send_ko_key(priv);
+        break;
+
+      case STATE_CHO:
+        priv->ko_cho = get_cho(code);
+        send_ko_key(priv);
+        break;
+
+      case STATE_JUNG:
+        priv->ko_jong = get_jong(code);
+        if(priv->ko_jong < 0) { /* if invalid ja for jong */
+          priv->ko_cho = get_cho(code);
+          priv->state = STATE_CHO;
+          hildon_im_ui_send_utf8(priv->ui, text);
+        } else {
+          erase(priv);
+          priv->state = STATE_JONG;
+          send_ko_key(priv);
+        }
+        break;
+
+      case STATE_iJUNG:
+        priv->ko_cho = get_cho(code);
+        priv->state = STATE_CHO;
+        send_ko_key(priv);
+        break;
+
+      case STATE_JONG:
+        priv->ko_jong = compose_jong(priv->ko_jong, get_jong(code));
+        if (priv->ko_jong < 0) { /* then new stuff becomes cho */
+          priv->ko_cho = get_cho(code);
+          priv->state = STATE_CHO;
+          send_ko_key(priv);
+        } else {
+          erase(priv);
+          send_ko_key(priv);
+          /* priv->state = STATE_0; we may later decompose jong */
+        }
+        break;
+    }
+
+  }
+  else if (type == KEY_KO_MO)
+  {
+    switch (priv->state) {
+
+      case STATE_0:
+        priv->state = STATE_iJUNG;
+        priv->ko_jung = get_jung(code);
+        send_ko_key(priv);
+        break;
+
+      case STATE_CHO:
+        priv->state = STATE_JUNG;
+        priv->ko_jung = get_jung(code);
+        erase(priv);
+        send_ko_key(priv);
+        break;
+
+      case STATE_JUNG:
+      case STATE_iJUNG:
+        priv->ko_jung = compose_jung(priv->ko_jung, get_jung(code));
+        if (priv->ko_jung < 0) {
+          priv->state = STATE_iJUNG;
+          priv->ko_jung = get_jung(code);
+          send_ko_key(priv);
+        } else {
+          erase(priv);
+          send_ko_key(priv);
+        }
+        break;
+
+      case STATE_JONG:
+        old_jong = priv->ko_jong;
+        decompose_jong(old_jong, &(priv->ko_jong), &new_cho);
+
+        if(new_cho < 0) {
+          priv->state = STATE_iJUNG;
+          priv->ko_jung = get_jung(code);
+          send_ko_key(priv);
+        } else {
+          erase(priv);
+          send_ko_key(priv); /* print old char with new jong */
+          priv->state = STATE_JUNG;
+          priv->ko_cho = new_cho;
+          priv->ko_jung = get_jung(code);
+          send_ko_key(priv); /* print new cho */
+        }
+        break;
+
+    }
+  }
+
+
+  if (priv->case_mode == CASE_KO_UPPER
+   || priv->case_mode == CASE_EN_UPPER) {
+    priv->case_mode--;
+    update_layout (vkb);
+  }
+}
+
+
+
+
+
+
+
+
+
+/* Callbacks starts here */
+static gboolean 
+expose_cb (GtkWidget *widget, GdkEventExpose *event)
+{
+  g_return_if_fail (IS_KOREAN_VKB (widget));
+
+/*
+  draw_keyboard (KOREAN_VKB (widget));
+*/
+  update_layout (KOREAN_VKB (widget));
+
+  return TRUE;
+}
+
+static gboolean
+destroy_cb (GtkWidget *widget, GdkEventAny *event)
+{
+  KoreanVKB            *vkb;
+  KoreanVKBPrivate     *priv;
+
+  vkb = KOREAN_VKB (widget);
+  priv = KOREAN_VKB_GET_PRIVATE (vkb);
+  if (priv->keys != NULL)
+  {
+    g_free (priv->keys);
+    priv->keys = NULL;
+  }
+
+  return FALSE;
+}
+
+static void
+realize_cb (GtkWidget *widget)
+{
+  KoreanVKB        *vkb;
+  KoreanVKBPrivate *priv;
+  GdkScreen            *screen;
+  GdkWindowAttr         attributes;
+  gint                  attributes_mask;
+  gint                  depth;
+
+  g_return_if_fail (IS_KOREAN_VKB (widget));
+  vkb = KOREAN_VKB (widget);
+  priv = KOREAN_VKB_GET_PRIVATE (vkb);
+
+  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+  attributes.x = widget->allocation.x;
+  attributes.y = widget->allocation.y;
+  attributes.width = widget->allocation.width;
+  attributes.height = widget->allocation.height;
+  attributes.wclass = GDK_INPUT_OUTPUT;
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.event_mask = 
+    gtk_widget_get_events (widget)  |
+    GDK_EXPOSURE_MASK               | 
+    GDK_BUTTON_PRESS_MASK           |
+    GDK_BUTTON_RELEASE_MASK;
+  attributes.visual = gtk_widget_get_visual (widget);
+  attributes.colormap = gtk_widget_get_colormap (widget);
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+  widget->window = gdk_window_new (
+      gtk_widget_get_parent_window (widget), 
+      &attributes, attributes_mask);
+
+  widget->style = gtk_style_attach(widget->style, widget->window);
+
+  gdk_window_set_user_data (widget->window, widget);
+  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+
+  priv->style = gtk_rc_get_style_by_paths (
+      gtk_widget_get_settings(GTK_WIDGET(widget)),
+      ".osso-im-key", NULL, GTK_TYPE_BUTTON);
+
+  screen = gtk_widget_get_screen(widget);
+  depth = DefaultDepth (
+      GDK_SCREEN_XDISPLAY (screen), 
+      GDK_SCREEN_XNUMBER (screen));
+
+  priv->layout = pango_layout_new (
+      gtk_widget_get_pango_context (widget));
+}
+
+static void 
+size_allocate_cb (GtkWidget *widget, 
+    GtkAllocation *allocation)
+{
+  KoreanVKBPrivate     *priv;
+
+  g_return_if_fail (IS_KOREAN_VKB (widget));
+  priv = KOREAN_VKB_GET_PRIVATE (KOREAN_VKB (widget));
+
+  widget->allocation = *allocation;
+
+  if (GTK_WIDGET_REALIZED (widget))
+  {
+    gdk_window_move_resize (widget->window, 
+        allocation->x, allocation->y,
+        allocation->width, allocation->height);
+
+/*
+    if (allocation->width > FULLSCREEN_WIDTH) {
+      priv->screen_mode = SCREEN_FULL;
+    } else {
+      priv->screen_mode = SCREEN_NORMAL;
+    }
+
+    if (priv->lang == LANG_EN)
+        draw_keyboard (KOREAN_VKB (widget));
+    else if (priv->lang ==LANG_KO)
+        draw_ko_keyboard (KOREAN_VKB (widget));
+*/
+
+/*
+    draw_keyboard (KOREAN_VKB (widget));
+*/
+    update_layout (KOREAN_VKB (widget));
+
+
+  }
+}
+
+static VKBKey *
+get_key (KoreanVKB *vkb, gint x, gint y)
+{
+  KoreanVKBPrivate *priv;
+  VKBKey *key;
+  gint w, h, r, c, i;
+
+  priv = KOREAN_VKB_GET_PRIVATE (vkb);
+
+  w = GTK_WIDGET(vkb)->allocation.width / MAX_ROW;
+  h = GTK_WIDGET(vkb)->allocation.height / KEYBOARD_ROWS;
+
+  r = y / h;
+  c = (x - row_offset[r]) / w;
+
+  for ( i=0 ; i<priv->total_keys ; i++ ) {
+    key = &(priv->keys[i]);
+    if(key->row == r && (c - key->col) < key->len && (c - key->col) >= 0)
+      return key;
+  }
+
+  return NULL;
+}
+
+
+static gboolean
+button_press_cb (GtkWidget *widget, 
+    GdkEventButton *event)
+{
+  VKBKey *key;
+  KoreanVKB *vkb;
+  KoreanVKBPrivate *priv;
+  gint ko_key;
+
+  vkb = KOREAN_VKB (widget);
+  priv = KOREAN_VKB_GET_PRIVATE (vkb);
+
+/*
+  if (priv->lang == LANG_EN) {
+    key = get_key (vkb, event->x, event->y);
+
+    if (key != NULL) 
+    {
+      draw_key (vkb, key, GTK_STATE_ACTIVE, priv->case_mode);
+      priv->last_key = key;
+    }
+  } else if (priv->lang == LANG_KO) {
+    ko_key = get_ko_key(vkb, event->x, event->y);
+    send_ko_key(vkb, ko_key);
+    priv->last_key = NULL;
+  }
+*/
+
+  key = get_key (vkb, event->x, event->y);
+
+  if (key != NULL) {
+    draw_key (vkb, key, GTK_STATE_ACTIVE, priv->case_mode);
+    priv->last_key = key;
+  }
+
+/*
+  Don't know why NULL is set here... Just a guess.
+  It is probably safe to ignore NULL cases...
+
+  else {
+    priv->last_key = NULL;
+  }
+*/
+  return FALSE;
+}
+
+static gboolean
+button_release_cb (GtkWidget *widget, 
+    GdkEventButton *event)
+{
+  KoreanVKB *vkb;
+  KoreanVKBPrivate *priv;
+
+  vkb = KOREAN_VKB (widget);
+  priv = KOREAN_VKB_GET_PRIVATE (vkb);
+
+  if (priv->last_key != NULL)
+  {
+    send_key (vkb, priv->last_key);
+    draw_key (KOREAN_VKB (widget), 
+        priv->last_key, 
+        GTK_STATE_NORMAL,
+        priv->case_mode);
+    priv->last_key = NULL;
+  }
+
+
+  return FALSE;
+}
+
+/* The end of the road */