static GtkWidget* get_toplevel_window (GdkWindow *window); static void im_hangul_set_client_window	(GtkIMContext *context, GdkWindow *client_window); static void status_window_show     (GtkIMContextHangul *hcontext); static void status_window_hide     (GtkIMContextHangul *hcontext); static void status_window_set_label (GtkIMContextHangul *hcontext);  static void popup_hanja_window	   (GtkIMContextHangul *hcontext);    #define STATE_DIRECT -1 #define STATE_HANGUL 0 static GObjectClass *parent_class;  static GSList *status_windows = NULL; static GtkWidget *hanja_window = NULL;  #define MODE_HANJA	-2 #define MODE_DIRECT	-1 #define MODE_HANGUL	 0 static gboolean         manual_mode = FALSE; static gint		input_mode = MODE_DIRECT;   static gboolean		pref_use_caps_lock = FALSE; static gboolean		pref_use_hangul_jamo = FALSE; static gboolean		pref_use_status_window = TRUE; static gboolean		pref_use_dvorak = FALSE; static gchar* 		pref_hanja_font = NULL; static gint		pref_preedit_style = 1; static void (*im_hangul_preedit_attr)(PangoAttrList **attrs, gint start, gint end) = NULL; static GdkColor		pref_fg = { 0, 0, 0, 0 }; static GdkColor		pref_bg = { 0, 0xFFFF, 0xFFFF, 0xFFFF };  static guint16 im_hangul_ignore_table[] = { GDK_Control_L, GDK_Control_R, GDK_Caps_Lock, GDK_Shift_Lock, GDK_Meta_L, GDK_Meta_R, GDK_Alt_L, GDK_Alt_R, GDK_Super_L, GDK_Super_R, GDK_Hyper_L, GDK_Hyper_R, GDK_Mode_switch, 0 };  static void im_hangul_register_type(GTypeModule *type_module, const gchar *type_name) { static const GTypeInfo im_context_hangul_info = { sizeof(GtkIMContextHangulClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) im_hangul_class_init, NULL, NULL, sizeof(GtkIMContextHangul), 0, (GtkObjectInitFunc) im_hangul_init, };  gtk_type_im_context_hangul = g_type_module_register_type(type_module, GTK_TYPE_IM_CONTEXT, type_name, &im_context_hangul_info, 0); }  static gboolean have_property (const gchar* str) { GtkSettingsClass *klass = gtk_type_class (GTK_TYPE_SETTINGS);  return (g_object_class_find_property (G_OBJECT_CLASS (klass), str) != NULL); }  static void im_hangul_class_init (GtkIMContextHangulClass *klass) { GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS(klass); GObjectClass *gobject_class = G_OBJECT_CLASS(klass);  parent_class = g_type_class_peek_parent (klass);  im_context_class->set_client_window = im_hangul_set_client_window; im_context_class->filter_keypress = im_hangul_filter_keypress; im_context_class->reset = im_hangul_reset; im_context_class->focus_in = im_hangul_focus_in; im_context_class->focus_out = im_hangul_focus_out; im_context_class->get_preedit_string = im_hangul_get_preedit_string; gobject_class->finalize = im_hangul_finalize;    if (!have_property("gtk-imhangul-status")) gtk_settings_install_property (g_param_spec_boolean ("gtk-imhangul-status", "Status Window", "Whether to show status window or not", TRUE, G_PARAM_READWRITE)); if (!have_property("gtk-imhangul-use-capslock")) gtk_settings_install_property (g_param_spec_boolean ("gtk-imhangul-use-capslock", "Use Caps Lock", "Whether to use Caps Lock key for changing hangul output mode to Jamo or not", FALSE, G_PARAM_READWRITE)); if (!have_property("gtk-imhangul-dvorak")) gtk_settings_install_property (g_param_spec_boolean ("gtk-imhangul-dvorak", "Dvorak Keyboard", "Whether to use dvorak keyboard or not", FALSE, G_PARAM_READWRITE)); if (!have_property("gtk-imhangul-style")) gtk_settings_install_property (g_param_spec_int ("gtk-imhangul-style", "Preedit Style", "Preedit string style", 0, 4, 0, G_PARAM_READWRITE)); }  static void im_hangul_clear_buf (GtkIMContextHangul *hcontext) { hcontext->index = -1; hcontext->stack[0] = 0; hcontext->stack[1] = 0; hcontext->stack[2] = 0; hcontext->stack[3] = 0; hcontext->stack[4] = 0; hcontext->stack[5] = 0; hcontext->stack[6] = 0; hcontext->stack[7] = 0; hcontext->stack[8] = 0; hcontext->stack[9] = 0; hcontext->stack[10] = 0; hcontext->stack[11] = 0;  hcontext->lindex = 0; hcontext->choseong[0] = 0; hcontext->choseong[1] = 0; hcontext->choseong[2] = 0; hcontext->choseong[3] = 0;  hcontext->vindex = 0; hcontext->jungseong[0] = 0; hcontext->jungseong[1] = 0; hcontext->jungseong[2] = 0; hcontext->jungseong[3] = 0;  hcontext->tindex = 0; hcontext->jongseong[0] = 0; hcontext->jongseong[1] = 0; hcontext->jongseong[2] = 0; hcontext->jongseong[3] = 0; }  static void im_hangul_init (GtkIMContextHangul *hcontext) { im_hangul_clear_buf(hcontext);  hcontext->state = STATE_DIRECT;	 hcontext->automata = NULL;	         hcontext->toplevel = NULL;  g_object_get (gtk_settings_get_default(), "gtk-imhangul-status", &pref_use_status_window, "gtk-imhangul-use-capslock", &pref_use_caps_lock, "gtk-imhangul-dvorak", &pref_use_dvorak, "gtk-imhangul-style", &pref_preedit_style, NULL);  switch (pref_preedit_style) { case 0: im_hangul_preedit_attr = im_hangul_preedit_underline; break; case 1: im_hangul_preedit_attr = im_hangul_preedit_foreground; break; case 2: case 3: im_hangul_preedit_attr = im_hangul_preedit_background; break; case 4: im_hangul_preedit_attr = im_hangul_preedit_nothing; break; default: im_hangul_preedit_attr = im_hangul_preedit_foreground; } }  static void im_hangul_finalize(GObject *obj) { parent_class->finalize (obj); }  static void im_hangul_set_client_window(GtkIMContext *context, GdkWindow *client_window) { GtkStyle *style; GtkIMContextHangul *hcontext = GTK_IM_CONTEXT_HANGUL(context);  hcontext->toplevel = get_toplevel_window(client_window);  if (hcontext->toplevel) {  style = hcontext->toplevel->style; switch (pref_preedit_style) { case 2: pref_fg.red 	= style->base[GTK_STATE_NORMAL].red; pref_fg.green 	= style->base[GTK_STATE_NORMAL].green; pref_fg.blue 	= style->base[GTK_STATE_NORMAL].blue; pref_bg.red 	= style->text[GTK_STATE_NORMAL].red; pref_bg.green 	= style->text[GTK_STATE_NORMAL].green; pref_bg.blue 	= style->text[GTK_STATE_NORMAL].blue; break; case 3: pref_fg.red 	= style->text[GTK_STATE_NORMAL].red; pref_fg.green 	= style->text[GTK_STATE_NORMAL].green; pref_fg.blue 	= style->text[GTK_STATE_NORMAL].blue; pref_bg.red 	= (style->base[GTK_STATE_NORMAL].red * 80 + style->text[GTK_STATE_NORMAL].red * 20) / 100; pref_bg.green 	= (style->base[GTK_STATE_NORMAL].green * 80 + style->text[GTK_STATE_NORMAL].green * 20) / 100; pref_bg.blue 	= (style->base[GTK_STATE_NORMAL].blue * 80 + style->text[GTK_STATE_NORMAL].blue * 20) / 100; break; default: pref_fg.red 	= style->text[GTK_STATE_NORMAL].red; pref_fg.green 	= style->text[GTK_STATE_NORMAL].green; pref_fg.blue 	= style->text[GTK_STATE_NORMAL].blue; pref_bg.red 	= style->base[GTK_STATE_NORMAL].red; pref_bg.green 	= style->base[GTK_STATE_NORMAL].green; pref_bg.blue 	= style->base[GTK_STATE_NORMAL].blue; } } }  static GtkIMContext * im_hangul_new(void) { return GTK_IM_CONTEXT (g_object_new (GTK_TYPE_IM_CONTEXT_HANGUL, NULL)); }  static void im_hangul_set_automata(GtkIMContextHangul *hcontext, GtkIMContextHangulAutomata automata) { hcontext->automata = automata; }  static void im_hangul_mode_hangul(GtkIMContextHangul *hcontext) { input_mode = MODE_HANGUL; hcontext->state = STATE_HANGUL; status_window_set_label(hcontext); }  static void im_hangul_mode_direct(GtkIMContextHangul *hcontext) { input_mode = MODE_DIRECT; hcontext->state = STATE_DIRECT; status_window_set_label(hcontext); }  static gunichar im_hangul_choseong_to_unicode(gunichar ch) { static gunichar table[] = { 0x3131,	 0x3132,	 0x3134,	 0x3137,	 0x3138,	 0x3139,	 0x3141,	 0x3142,	 0x3143,	 0x3145,	 0x3146,	 0x3147,	 0x3148,	 0x3149,	 0x314a,	 0x314b,	 0x314c,	 0x314d,	 0x314e	 }; if (ch < 0x1100 || ch > 0x1112) return 0; return table[ch - 0x1100]; }  static gunichar im_hangul_jungseong_to_unicode(gunichar ch) { static gunichar table[] = { 0x314f,	 0x3150,	 0x3151,	 0x3152,	 0x3153,	 0x3154,	 0x3155,	 0x3156,	 0x3157,	 0x3158,	 0x3159,	 0x315a,	 0x315b,	 0x315c,	 0x315d,	 0x315e,	 0x315f,	 0x3160,	 0x3161,	 0x3162,	 0x3163	 };  if (ch < 0x1161 || ch > 0x1175) return 0; return table[ch - 0x1161]; }  static gunichar im_hangul_jongseong_to_unicode(gunichar ch) { static gunichar table[] = { 0x3131,	 0x3132,	 0x3133,	 0x3134,	 0x3135,	 0x3136,	 0x3137,	 0x3139,	 0x313a,	 0x313b,	 0x313c,	 0x313d,	 0x313e,	 0x313f,	 0x3140,	 0x3141,	 0x3142,	 0x3144,	 0x3145,	 0x3146,	 0x3147,	 0x3148,	 0x314a,	 0x314b,	 0x314c,	 0x314d,	 0x314e	 }; if (ch < 0x11a8 || ch > 0x11c2) return 0; return table[ch - 0x11a8]; }  #define HCF	0x115f #define HJF	0x1160  static gunichar im_hangul_jamo_to_syllable(gunichar choseong, gunichar jungseong, gunichar jongseong) { static gunichar hangul_base = 0xac00; static gunichar choseong_base = 0x1100; static gunichar jungseong_base = 0x1161; static gunichar jongseong_base = 0x11a7;  static int njungseong = 21; static int njongseong = 28;  gunichar ch;   if (jongseong == 0) jongseong = 0x11a7;           if (!(choseong  >= 0x1100 && choseong  <= 0x1112)) return 0; if (!(jungseong >= 0x1161 && jungseong <= 0x1175)) return 0; if (!(jongseong >= 0x11a7 && jongseong <= 0x11c2)) return 0;  choseong  -= choseong_base; jungseong -= jungseong_base; jongseong -= jongseong_base;  ch = ((choseong * njungseong) + jungseong) * njongseong + jongseong + hangul_base; return ch; }  static int im_hangul_preedit_string(GtkIMContextHangul *hcontext, gchar *buf) { int i; int n = 0; gunichar ch;  if (im_hangul_is_empty(hcontext)) { buf[0] = '\0'; return 0; }  if (manual_mode) {  if (hcontext->choseong[0] == 0) n += g_unichar_to_utf8(HCF, buf + n); else { for (i = 0; i <= hcontext->lindex; i++) n += g_unichar_to_utf8(hcontext->choseong[i], buf + n); } if (hcontext->jungseong[0] == 0) n += g_unichar_to_utf8(HJF, buf + n); else { for (i = 0; i <= hcontext->vindex; i++) n += g_unichar_to_utf8(hcontext->jungseong[i], buf + n); } if (hcontext->jongseong[0] != 0) { for (i = 0; i <= hcontext->tindex; i++) n += g_unichar_to_utf8(hcontext->jongseong[i], buf + n); } buf[n] = '\0'; } else if (pref_use_hangul_jamo) {  if (hcontext->choseong[0] == 0) n = g_unichar_to_utf8(HCF, buf); else n = g_unichar_to_utf8(hcontext->choseong[0], buf);  if (hcontext->jungseong[0] == 0) n += g_unichar_to_utf8(HJF, buf + n); else n += g_unichar_to_utf8(hcontext->jungseong[0], buf + n);  if (hcontext->jongseong[0] != 0) { n += g_unichar_to_utf8(hcontext->jongseong[0], buf + n); } buf[n] = '\0'; } else {  if (hcontext->choseong[0]) { if (hcontext->jungseong[0]) {  ch = im_hangul_jamo_to_syllable(hcontext->choseong[0], hcontext->jungseong[0], hcontext->jongseong[0]); n = g_unichar_to_utf8(ch, buf); buf[n] = '\0'; return n; } else { if (hcontext->jongseong[0]) {  ch = im_hangul_choseong_to_unicode(hcontext->choseong[0]); n = g_unichar_to_utf8(ch, buf); ch = im_hangul_jongseong_to_unicode(hcontext->jongseong[0]); n += g_unichar_to_utf8(ch, buf + n); buf[n] = '\0'; return n; } else {  ch = im_hangul_choseong_to_unicode(hcontext->choseong[0]); n = g_unichar_to_utf8(ch, buf); buf[n] = '\0'; return n; } } } else { if (hcontext->jungseong[0]) { if (hcontext->jongseong[0]) {  ch = im_hangul_jungseong_to_unicode(hcontext->jungseong[0]); n = g_unichar_to_utf8(ch, buf); ch = im_hangul_jongseong_to_unicode(hcontext->jongseong[0]); n += g_unichar_to_utf8(ch, buf + n); buf[n] = '\0'; return n; } else {  ch = im_hangul_jungseong_to_unicode(hcontext->jungseong[0]); n = g_unichar_to_utf8(ch, buf); buf[n] = '\0'; return n; } } else { if (hcontext->jongseong[0]) {  ch = im_hangul_jongseong_to_unicode(hcontext->jongseong[0]); n = g_unichar_to_utf8(ch, buf); buf[n] = '\0'; return n; } else {  ; } } } }   return n; }  static void im_hangul_preedit_underline (PangoAttrList **attrs, gint start, gint end) { PangoAttribute *attr;  *attrs = pango_attr_list_new(); attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE); attr->start_index = start; attr->end_index = end; pango_attr_list_insert (*attrs, attr); }  static void im_hangul_preedit_foreground (PangoAttrList **attrs, gint start, gint end) { PangoAttribute *attr;  *attrs = pango_attr_list_new(); attr = pango_attr_foreground_new(0xeeee, 0x0, 0x0); attr->start_index = start; attr->end_index = end; pango_attr_list_insert (*attrs, attr); }  static void im_hangul_preedit_background (PangoAttrList **attrs, gint start, gint end) { PangoAttribute *attr;  *attrs = pango_attr_list_new(); attr = pango_attr_foreground_new(pref_fg.red, pref_fg.green, pref_fg.blue); attr->start_index = start; attr->end_index = end; pango_attr_list_insert (*attrs, attr);  attr = pango_attr_background_new(pref_bg.red, pref_bg.green, pref_bg.blue);  attr->start_index = start; attr->end_index = end; pango_attr_list_insert (*attrs, attr); }  static void im_hangul_preedit_nothing (PangoAttrList **attrs, gint start, gint end) {  *attrs = pango_attr_list_new(); }  static void im_hangul_get_preedit_string(GtkIMContext *context, gchar **str, PangoAttrList **attrs, gint *cursor_pos) { gchar buf[40]; int len = 0; GtkIMContextHangul *hcontext = GTK_IM_CONTEXT_HANGUL(context);  buf[0] = '\0'; len = im_hangul_preedit_string(hcontext, buf);  if (attrs) { im_hangul_preedit_attr(attrs, 0, len); }  if (cursor_pos) { *cursor_pos = len; }  if (str) { *str = g_strdup(buf); } }  static void im_hangul_focus_in(GtkIMContext *context) { GtkIMContextHangul *hcontext = GTK_IM_CONTEXT_HANGUL(context);  status_window_show(hcontext); }  static void im_hangul_focus_out(GtkIMContext *context) { GtkIMContextHangul *hcontext = GTK_IM_CONTEXT_HANGUL(context);  if (hanja_window == NULL) { if (im_hangul_commit(hcontext)) { g_signal_emit_by_name (hcontext, "preedit_changed"); hcontext->state = MODE_HANGUL; } } status_window_hide(hcontext); }  static gboolean im_hangul_is_ignore_key(guint16 key) { int i; for (i = 0; im_hangul_ignore_table[i]; i++) { if (key == im_hangul_ignore_table[i]) { return TRUE; } } return FALSE; }  static gboolean im_hangul_is_trigger(GdkEventKey *key) { return ( key->keyval == GDK_Hangul || (key->keyval == GDK_space && is_set(key->state, GDK_SHIFT_MASK))); }  static gboolean im_hangul_is_backspace(GdkEventKey *key) { return (key->keyval == GDK_BackSpace); }  static void im_hangul_reset(GtkIMContext *context) { GtkIMContextHangul *hcontext = GTK_IM_CONTEXT_HANGUL (context);  if (im_hangul_commit(hcontext)) g_signal_emit_by_name (hcontext, "preedit_changed"); }  static gboolean im_hangul_process_nonhangul(GtkIMContextHangul *hcontext, GdkEventKey *key) { if (!im_hangul_is_modifier(key->state)) { gunichar ch = gdk_keyval_to_unicode(key->keyval); if (ch != 0) { gchar buf[10]; gint n = g_unichar_to_utf8(ch, buf); buf[n] = '\0';  g_signal_emit_by_name(hcontext, "commit", buf); return TRUE; } } return FALSE; }  static gboolean im_hangul_handle_direct_mode(GtkIMContextHangul *hcontext, GdkEventKey *key) { if (im_hangul_is_trigger(key)) { if (im_hangul_commit(hcontext)) g_signal_emit_by_name (hcontext, "preedit_changed"); im_hangul_mode_hangul(hcontext); return FALSE; } return im_hangul_process_nonhangul(hcontext, key); }  static gboolean im_hangul_commit(GtkIMContextHangul *hcontext) { int i; int n = 0; gchar buf[40];  buf[0] = '\0';  if (im_hangul_is_empty(hcontext)) return FALSE;  if (manual_mode) {  if (hcontext->choseong[0] == 0) n += g_unichar_to_utf8(HCF, buf + n); else { for (i = 0; i <= hcontext->lindex; i++) n += g_unichar_to_utf8(hcontext->choseong[i], buf + n); } if (hcontext->jungseong[0] == 0) n += g_unichar_to_utf8(HJF, buf + n); else { for (i = 0; i <= hcontext->vindex; i++) n += g_unichar_to_utf8(hcontext->jungseong[i], buf + n); } if (hcontext->jongseong[0] != 0) { for (i = 0; i <= hcontext->tindex; i++) n += g_unichar_to_utf8(hcontext->jongseong[i], buf + n); } buf[n] = '\0'; } else if (pref_use_hangul_jamo) {  if (hcontext->choseong[0] == 0) n = g_unichar_to_utf8(HCF, buf); else n = g_unichar_to_utf8(hcontext->choseong[0], buf);  if (hcontext->jungseong[0] == 0) n += g_unichar_to_utf8(HJF, buf + n); else n += g_unichar_to_utf8(hcontext->jungseong[0], buf + n);  if (hcontext->jongseong[0] != 0) n += g_unichar_to_utf8(hcontext->jongseong[0], buf + n);  buf[n] = '\0'; } else {  gunichar ch; ch = im_hangul_jamo_to_syllable(hcontext->choseong[0], hcontext->jungseong[0], hcontext->jongseong[0]);  if (ch) { n = g_unichar_to_utf8(ch, buf); buf[n] = '\0';  } else { if (hcontext->choseong[0]) { ch = im_hangul_choseong_to_unicode(hcontext->choseong[0]); n = g_unichar_to_utf8(ch, buf); buf[n] = '\0';  } if (hcontext->jungseong[0]) { ch = im_hangul_jungseong_to_unicode(hcontext->jungseong[0]); n += g_unichar_to_utf8(ch, buf + n); buf[n] = '\0';  } if (hcontext->jongseong[0]) { ch = im_hangul_jongseong_to_unicode(hcontext->jongseong[0]); n += g_unichar_to_utf8(ch, buf + n); buf[n] = '\0';  } } }  im_hangul_clear_buf(hcontext);  g_signal_emit_by_name(hcontext, "commit", buf); return TRUE; }  static void im_hangul_commit_utf8(GtkIMContextHangul *hcontext, gchar *utf8) { gchar buf[9];  g_return_if_fail(utf8);  strncpy(buf, utf8, sizeof(buf)); buf[8] = '\0';  im_hangul_clear_buf(hcontext);  g_signal_emit_by_name(hcontext, "commit", buf); }   static guint im_hangul_dvorak_to_qwerty(guint code) {  static guint table[] = { GDK_exclam,			 GDK_Q,			 GDK_numbersign,		 GDK_dollar,			 GDK_percent,		 GDK_ampersand,		 GDK_q,			 GDK_parenleft,		 GDK_parenright,		 GDK_asterisk,		 GDK_braceright,		 GDK_w,			 GDK_apostrophe,		 GDK_e,			 GDK_bracketleft,		 GDK_0,			 GDK_1,			 GDK_2,			 GDK_3,			 GDK_4,			 GDK_5,			 GDK_6,			 GDK_7,			 GDK_8,			 GDK_9,			 GDK_Z,			 GDK_z,			 GDK_W,			 GDK_bracketright,		 GDK_E,			 GDK_braceleft,		 GDK_at,			 GDK_A,			 GDK_N,			 GDK_I,			 GDK_H,			 GDK_D,			 GDK_Y,			 GDK_U,			 GDK_J,			 GDK_G,			 GDK_C,			 GDK_V,			 GDK_P,			 GDK_M,			 GDK_L,			 GDK_S,			 GDK_R,			 GDK_X,			 GDK_O,			 GDK_colon,			 GDK_K,			 GDK_F,			 GDK_greater,		 GDK_less,			 GDK_B,			 GDK_T,			 GDK_question,		 GDK_minus,			 GDK_backslash,		 GDK_equal,			 GDK_asciicircum,		 GDK_quotedbl,		 GDK_grave,			 GDK_a,			 GDK_n,			 GDK_i,			 GDK_h,			 GDK_d,			 GDK_y,			 GDK_u,			 GDK_j,			 GDK_g,			 GDK_c,			 GDK_v,			 GDK_p,			 GDK_m,			 GDK_l,			 GDK_s,			 GDK_r,			 GDK_x,			 GDK_o,			 GDK_semicolon,		 GDK_k,			 GDK_f,			 GDK_period,			 GDK_comma,			 GDK_b,			 GDK_t,			 GDK_slash,			 GDK_underscore,		 GDK_bar,			 GDK_plus,			 GDK_asciitilde,		 }; if (code < GDK_exclam || code > GDK_asciitilde) return code; return table[code - GDK_exclam]; }   static gboolean im_hangul_filter_keypress(GtkIMContext *context, GdkEventKey *key) { GtkIMContextHangul *hcontext = GTK_IM_CONTEXT_HANGUL(context);   if (key->type == GDK_KEY_RELEASE) return FALSE;   if (key->keyval == GDK_Shift_L || key->keyval == GDK_Shift_R) return FALSE;   if (key->keyval == GDK_Hangul && key->state & GDK_CONTROL_MASK) manual_mode = !manual_mode;   if (pref_use_caps_lock && key->keyval == GDK_Caps_Lock) pref_use_hangul_jamo = !pref_use_hangul_jamo;    if (im_hangul_is_ignore_key(key->keyval)) { if (im_hangul_commit(hcontext)) g_signal_emit_by_name (hcontext, "preedit_changed"); return FALSE; }   if (input_mode == MODE_DIRECT) { return im_hangul_handle_direct_mode(hcontext, key); }   if (key->keyval == GDK_Escape) { if (im_hangul_commit(hcontext)) g_signal_emit_by_name (hcontext, "preedit_changed"); im_hangul_mode_direct(hcontext); return FALSE; }   if (im_hangul_is_modifier(key->state)) { if (im_hangul_commit(hcontext)) g_signal_emit_by_name (hcontext, "preedit_changed"); return FALSE; }   if (key->keyval == GDK_F9 || key->keyval == GDK_Hangul_Hanja) { popup_hanja_window(hcontext); return TRUE; }   if (hcontext->state == STATE_DIRECT) hcontext->state = STATE_HANGUL;  if (hcontext->automata) return hcontext->automata(hcontext, key); else return FALSE; }    static gboolean status_window_expose_event(GtkWidget *widget, GdkEventExpose *event) { gdk_draw_rectangle (widget->window, widget->style->fg_gc[GTK_STATE_NORMAL], FALSE, 0, 0, widget->allocation.width-1, widget->allocation.height-1);  return FALSE; }  static void status_window_free(StatusWindow *status_window) { status_windows = g_slist_remove(status_windows, status_window);  g_signal_handler_disconnect(status_window->toplevel, status_window->destroy_handler_id); g_signal_handler_disconnect(status_window->toplevel, status_window->configure_handler_id); gtk_widget_destroy(status_window->window); g_object_set_data(G_OBJECT(status_window->toplevel), "gtk-imhangul-status-window", NULL); g_free(status_window); }  static gboolean status_window_configure	(GtkWidget *toplevel, GdkEventConfigure *event, GtkWidget *window) { GdkRectangle rect; GtkRequisition requisition; gint y;  gdk_window_get_frame_extents(toplevel->window, &rect); gtk_widget_size_request(window, &requisition);  if (rect.y + rect.height + requisition.height < gdk_screen_height()) y = rect.y + rect.height; else y = gdk_screen_height() - requisition.height;  gtk_window_move(GTK_WINDOW(window), rect.x, y); return FALSE; }  static gboolean on_click_hangul (GtkWidget *widget, GdkEventButton *event, gpointer data) { GtkIMContextHangul *hcontext = GTK_IM_CONTEXT_HANGUL(data);  if (input_mode == STATE_DIRECT) {  im_hangul_mode_hangul(hcontext); } else {  if (im_hangul_commit(hcontext)) g_signal_emit_by_name (hcontext, "preedit_changed"); im_hangul_mode_direct(hcontext); } return TRUE; }  static gboolean on_click_hanja (GtkWidget *widget, GdkEventButton *event, gpointer data) { GtkIMContextHangul *hcontext = GTK_IM_CONTEXT_HANGUL(data);  popup_hanja_window(hcontext);  return TRUE; }  static GtkWidget *
static GtkWidget* get_toplevel_window (GdkWindow *window);static void im_hangul_set_client_window	(GtkIMContext *context,				         GdkWindow *client_window);static void status_window_show     (GtkIMContextHangul *hcontext);static void status_window_hide     (GtkIMContextHangul *hcontext);static void status_window_set_label (GtkIMContextHangul *hcontext);static void popup_hanja_window	   (GtkIMContextHangul *hcontext);/* * global variables for hangul immodule */#define STATE_DIRECT -1 #define STATE_HANGUL 0 static GObjectClass *parent_class;static GSList *status_windows = NULL;static GtkWidget *hanja_window = NULL;#define MODE_HANJA	-2#define MODE_DIRECT	-1#define MODE_HANGUL	 0static gboolean         manual_mode = FALSE;static gint		input_mode = MODE_DIRECT;/* preferences */static gboolean		pref_use_caps_lock = FALSE;static gboolean		pref_use_hangul_jamo = FALSE;static gboolean		pref_use_status_window = TRUE;static gboolean		pref_use_dvorak = FALSE;static gchar* 		pref_hanja_font = NULL;static gint		pref_preedit_style = 1;static void (*im_hangul_preedit_attr)(PangoAttrList **attrs, gint start, gint end) = NULL;static GdkColor		pref_fg = { 0, 0, 0, 0 };static GdkColor		pref_bg = { 0, 0xFFFF, 0xFFFF, 0xFFFF };static guint16 im_hangul_ignore_table[] = {  GDK_Control_L,  GDK_Control_R,  GDK_Caps_Lock,  GDK_Shift_Lock,  GDK_Meta_L,  GDK_Meta_R,  GDK_Alt_L,  GDK_Alt_R,  GDK_Super_L,  GDK_Super_R,  GDK_Hyper_L,  GDK_Hyper_R,  GDK_Mode_switch,  0};static voidim_hangul_register_type(GTypeModule *type_module, const gchar *type_name){  static const GTypeInfo im_context_hangul_info = {    sizeof(GtkIMContextHangulClass),    (GBaseInitFunc) NULL,    (GBaseFinalizeFunc) NULL,    (GClassInitFunc) im_hangul_class_init,    NULL,    NULL,    sizeof(GtkIMContextHangul),    0,    (GtkObjectInitFunc) im_hangul_init,  };  gtk_type_im_context_hangul =      g_type_module_register_type(type_module,				  GTK_TYPE_IM_CONTEXT,				  type_name,				  &im_context_hangul_info, 0);}static gbooleanhave_property (const gchar* str){  GtkSettingsClass *klass = gtk_type_class (GTK_TYPE_SETTINGS);  return (g_object_class_find_property (G_OBJECT_CLASS (klass), str) != NULL);}static void im_hangul_class_init (GtkIMContextHangulClass *klass){  GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS(klass);  GObjectClass *gobject_class = G_OBJECT_CLASS(klass);  parent_class = g_type_class_peek_parent (klass);  im_context_class->set_client_window = im_hangul_set_client_window;  im_context_class->filter_keypress = im_hangul_filter_keypress;  im_context_class->reset = im_hangul_reset;  im_context_class->focus_in = im_hangul_focus_in;  im_context_class->focus_out = im_hangul_focus_out;  im_context_class->get_preedit_string = im_hangul_get_preedit_string;  gobject_class->finalize = im_hangul_finalize;  /* install settings */  /* check whether installed or not */  if (!have_property("gtk-imhangul-status"))    gtk_settings_install_property (g_param_spec_boolean ("gtk-imhangul-status",  						         "Status Window",						         "Whether to show status window or not",						         TRUE,						         G_PARAM_READWRITE));  if (!have_property("gtk-imhangul-use-capslock"))    gtk_settings_install_property (g_param_spec_boolean ("gtk-imhangul-use-capslock",  						         "Use Caps Lock",						         "Whether to use Caps Lock key for changing hangul output mode to Jamo or not",						         FALSE,						         G_PARAM_READWRITE));  if (!have_property("gtk-imhangul-dvorak"))    gtk_settings_install_property (g_param_spec_boolean ("gtk-imhangul-dvorak",  						         "Dvorak Keyboard",						         "Whether to use dvorak keyboard or not",						         FALSE,						         G_PARAM_READWRITE));  if (!have_property("gtk-imhangul-style"))    gtk_settings_install_property (g_param_spec_int ("gtk-imhangul-style",  						     "Preedit Style",						     "Preedit string style",						     0,						     4,						     0,						     G_PARAM_READWRITE));}static voidim_hangul_clear_buf (GtkIMContextHangul *hcontext){  hcontext->index = -1;  hcontext->stack[0] = 0;  hcontext->stack[1] = 0;  hcontext->stack[2] = 0;  hcontext->stack[3] = 0;  hcontext->stack[4] = 0;  hcontext->stack[5] = 0;  hcontext->stack[6] = 0;  hcontext->stack[7] = 0;  hcontext->stack[8] = 0;  hcontext->stack[9] = 0;  hcontext->stack[10] = 0;  hcontext->stack[11] = 0;  hcontext->lindex = 0;  hcontext->choseong[0] = 0;  hcontext->choseong[1] = 0;  hcontext->choseong[2] = 0;  hcontext->choseong[3] = 0;  hcontext->vindex = 0;  hcontext->jungseong[0] = 0;  hcontext->jungseong[1] = 0;  hcontext->jungseong[2] = 0;  hcontext->jungseong[3] = 0;  hcontext->tindex = 0;  hcontext->jongseong[0] = 0;  hcontext->jongseong[1] = 0;  hcontext->jongseong[2] = 0;  hcontext->jongseong[3] = 0;}static void im_hangul_init (GtkIMContextHangul *hcontext){  im_hangul_clear_buf(hcontext);  hcontext->state = STATE_DIRECT;	/* english mode */  hcontext->automata = NULL;	        /* initial value: automata == null */  hcontext->toplevel = NULL;  g_object_get (gtk_settings_get_default(),  		"gtk-imhangul-status", &pref_use_status_window,  		"gtk-imhangul-use-capslock", &pref_use_caps_lock,  		"gtk-imhangul-dvorak", &pref_use_dvorak,  		"gtk-imhangul-style", &pref_preedit_style,		NULL);  switch (pref_preedit_style) {    case 0:      im_hangul_preedit_attr = im_hangul_preedit_underline;      break;    case 1:      im_hangul_preedit_attr = im_hangul_preedit_foreground;      break;    case 2:    case 3:      im_hangul_preedit_attr = im_hangul_preedit_background;      break;    case 4:      im_hangul_preedit_attr = im_hangul_preedit_nothing;      break;    default:      im_hangul_preedit_attr = im_hangul_preedit_foreground;  }}static voidim_hangul_finalize(GObject *obj){  parent_class->finalize (obj);}static voidim_hangul_set_client_window(GtkIMContext *context,			    GdkWindow *client_window){  GtkStyle *style;  GtkIMContextHangul *hcontext = GTK_IM_CONTEXT_HANGUL(context);  hcontext->toplevel = get_toplevel_window(client_window);  if (hcontext->toplevel) {    /* set fg, bg colors */    style = hcontext->toplevel->style;    switch (pref_preedit_style) {      case 2:        pref_fg.red 	= style->base[GTK_STATE_NORMAL].red;        pref_fg.green 	= style->base[GTK_STATE_NORMAL].green;        pref_fg.blue 	= style->base[GTK_STATE_NORMAL].blue;        pref_bg.red 	= style->text[GTK_STATE_NORMAL].red;        pref_bg.green 	= style->text[GTK_STATE_NORMAL].green;        pref_bg.blue 	= style->text[GTK_STATE_NORMAL].blue;	break;      case 3:        pref_fg.red 	= style->text[GTK_STATE_NORMAL].red;        pref_fg.green 	= style->text[GTK_STATE_NORMAL].green;        pref_fg.blue 	= style->text[GTK_STATE_NORMAL].blue;        pref_bg.red 	= (style->base[GTK_STATE_NORMAL].red * 80 +       			   style->text[GTK_STATE_NORMAL].red * 20) / 100;        pref_bg.green 	= (style->base[GTK_STATE_NORMAL].green * 80 +         	           style->text[GTK_STATE_NORMAL].green * 20) / 100;        pref_bg.blue 	= (style->base[GTK_STATE_NORMAL].blue * 80 +         	           style->text[GTK_STATE_NORMAL].blue * 20) / 100;	break;      default:        pref_fg.red 	= style->text[GTK_STATE_NORMAL].red;        pref_fg.green 	= style->text[GTK_STATE_NORMAL].green;        pref_fg.blue 	= style->text[GTK_STATE_NORMAL].blue;        pref_bg.red 	= style->base[GTK_STATE_NORMAL].red;        pref_bg.green 	= style->base[GTK_STATE_NORMAL].green;        pref_bg.blue 	= style->base[GTK_STATE_NORMAL].blue;    }  }}static GtkIMContext *im_hangul_new(void){  return GTK_IM_CONTEXT (g_object_new (GTK_TYPE_IM_CONTEXT_HANGUL, NULL));}static voidim_hangul_set_automata(GtkIMContextHangul *hcontext,		       GtkIMContextHangulAutomata automata){  hcontext->automata = automata;}static void im_hangul_mode_hangul(GtkIMContextHangul *hcontext){  input_mode = MODE_HANGUL;  hcontext->state = STATE_HANGUL;  status_window_set_label(hcontext);}static void im_hangul_mode_direct(GtkIMContextHangul *hcontext){  input_mode = MODE_DIRECT;  hcontext->state = STATE_DIRECT;  status_window_set_label(hcontext);}static gunicharim_hangul_choseong_to_unicode(gunichar ch){  static gunichar table[] = {    0x3131,	/* 0x1100 */    0x3132,	/* 0x1101 */    0x3134,	/* 0x1102 */    0x3137,	/* 0x1103 */    0x3138,	/* 0x1104 */    0x3139,	/* 0x1105 */    0x3141,	/* 0x1106 */    0x3142,	/* 0x1107 */    0x3143,	/* 0x1108 */    0x3145,	/* 0x1109 */    0x3146,	/* 0x110a */    0x3147,	/* 0x110b */    0x3148,	/* 0x110c */    0x3149,	/* 0x110d */    0x314a,	/* 0x110e */    0x314b,	/* 0x110f */    0x314c,	/* 0x1110 */    0x314d,	/* 0x1111 */    0x314e	/* 0x1112 */  };  if (ch < 0x1100 || ch > 0x1112)    return 0;  return table[ch - 0x1100];}static gunicharim_hangul_jungseong_to_unicode(gunichar ch){  static gunichar table[] = {    0x314f,	/* 0x1161 */    0x3150,	/* 0x1162 */    0x3151,	/* 0x1163 */    0x3152,	/* 0x1164 */    0x3153,	/* 0x1165 */    0x3154,	/* 0x1166 */    0x3155,	/* 0x1167 */    0x3156,	/* 0x1168 */    0x3157,	/* 0x1169 */    0x3158,	/* 0x116a */    0x3159,	/* 0x116b */    0x315a,	/* 0x116c */    0x315b,	/* 0x116d */    0x315c,	/* 0x116e */    0x315d,	/* 0x116f */    0x315e,	/* 0x1170 */    0x315f,	/* 0x1171 */    0x3160,	/* 0x1172 */    0x3161,	/* 0x1173 */    0x3162,	/* 0x1174 */    0x3163	/* 0x1175 */  };  if (ch < 0x1161 || ch > 0x1175)    return 0;  return table[ch - 0x1161];}static gunicharim_hangul_jongseong_to_unicode(gunichar ch){  static gunichar table[] = {    0x3131,	/* 0x11a8 */    0x3132,	/* 0x11a9 */    0x3133,	/* 0x11aa */    0x3134,	/* 0x11ab */    0x3135,	/* 0x11ac */    0x3136,	/* 0x11ad */    0x3137,	/* 0x11ae */    0x3139,	/* 0x11af */    0x313a,	/* 0x11b0 */    0x313b,	/* 0x11b1 */    0x313c,	/* 0x11b2 */    0x313d,	/* 0x11b3 */    0x313e,	/* 0x11b4 */    0x313f,	/* 0x11b5 */    0x3140,	/* 0x11b6 */    0x3141,	/* 0x11b7 */    0x3142,	/* 0x11b8 */    0x3144,	/* 0x11b9 */    0x3145,	/* 0x11ba */    0x3146,	/* 0x11bb */    0x3147,	/* 0x11bc */    0x3148,	/* 0x11bd */    0x314a,	/* 0x11be */    0x314b,	/* 0x11bf */    0x314c,	/* 0x11c0 */    0x314d,	/* 0x11c1 */    0x314e	/* 0x11c2 */  };  if (ch < 0x11a8 || ch > 0x11c2)    return 0;  return table[ch - 0x11a8];}#define HCF	0x115f#define HJF	0x1160static gunicharim_hangul_jamo_to_syllable(gunichar choseong,			   gunichar jungseong,			   gunichar jongseong){  static gunichar hangul_base = 0xac00;  static gunichar choseong_base = 0x1100;  static gunichar jungseong_base = 0x1161;  static gunichar jongseong_base = 0x11a7;/*  static int nchoseong = 19; */  static int njungseong = 21;  static int njongseong = 28;/*  static int njungjong = 21 * 28; */  gunichar ch;  /* we use 0x11a7 like a Jongseong filler */  if (jongseong == 0)    jongseong = 0x11a7;         /* Jongseong filler */  if (!(choseong  >= 0x1100 && choseong  <= 0x1112))    return 0;  if (!(jungseong >= 0x1161 && jungseong <= 0x1175))    return 0;  if (!(jongseong >= 0x11a7 && jongseong <= 0x11c2))    return 0;  choseong  -= choseong_base;  jungseong -= jungseong_base;  jongseong -= jongseong_base;   ch = ((choseong * njungseong) + jungseong) * njongseong + jongseong	 + hangul_base;  return ch;}static intim_hangul_preedit_string(GtkIMContextHangul *hcontext, gchar *buf){  int i;  int n = 0;  gunichar ch;  if (im_hangul_is_empty(hcontext)) {    buf[0] = '\0';    return 0;  }  if (manual_mode) {    /* we use conjoining jamo, U+1100 - U+11FF */    if (hcontext->choseong[0] == 0)      n += g_unichar_to_utf8(HCF, buf + n);    else {      for (i = 0; i <= hcontext->lindex; i++)        n += g_unichar_to_utf8(hcontext->choseong[i], buf + n);    }    if (hcontext->jungseong[0] == 0)      n += g_unichar_to_utf8(HJF, buf + n);    else {      for (i = 0; i <= hcontext->vindex; i++)        n += g_unichar_to_utf8(hcontext->jungseong[i], buf + n);    }    if (hcontext->jongseong[0] != 0) {      for (i = 0; i <= hcontext->tindex; i++)        n += g_unichar_to_utf8(hcontext->jongseong[i], buf + n);    }    buf[n] = '\0';  } else if (pref_use_hangul_jamo) {    /* we use conjoining jamo, U+1100 - U+11FF */    if (hcontext->choseong[0] == 0)      n = g_unichar_to_utf8(HCF, buf);    else      n = g_unichar_to_utf8(hcontext->choseong[0], buf);     if (hcontext->jungseong[0] == 0)      n += g_unichar_to_utf8(HJF, buf + n);    else      n += g_unichar_to_utf8(hcontext->jungseong[0], buf + n);     if (hcontext->jongseong[0] != 0) {      n += g_unichar_to_utf8(hcontext->jongseong[0], buf + n);    }    buf[n] = '\0';  } else {    /* this code is very stupid but pango has some bug in Xft     * therefore we have to do this */    if (hcontext->choseong[0]) {      if (hcontext->jungseong[0]) {        /* have cho, jung, jong or no jong */        ch = im_hangul_jamo_to_syllable(hcontext->choseong[0],        			        hcontext->jungseong[0],        			        hcontext->jongseong[0]);        n = g_unichar_to_utf8(ch, buf);        buf[n] = '\0';        return n;      } else {        if (hcontext->jongseong[0]) {          /* have cho, jong */          ch = im_hangul_choseong_to_unicode(hcontext->choseong[0]);          n = g_unichar_to_utf8(ch, buf);          ch = im_hangul_jongseong_to_unicode(hcontext->jongseong[0]);          n += g_unichar_to_utf8(ch, buf + n);          buf[n] = '\0';          return n;        } else {          /* have cho */          ch = im_hangul_choseong_to_unicode(hcontext->choseong[0]);          n = g_unichar_to_utf8(ch, buf);          buf[n] = '\0';          return n;        }      }    } else {      if (hcontext->jungseong[0]) {        if (hcontext->jongseong[0]) {          /* have jung, jong */          ch = im_hangul_jungseong_to_unicode(hcontext->jungseong[0]);          n = g_unichar_to_utf8(ch, buf);          ch = im_hangul_jongseong_to_unicode(hcontext->jongseong[0]);          n += g_unichar_to_utf8(ch, buf + n);          buf[n] = '\0';          return n;        } else {          /* have jung */          ch = im_hangul_jungseong_to_unicode(hcontext->jungseong[0]);          n = g_unichar_to_utf8(ch, buf);          buf[n] = '\0';          return n;        }      } else {        if (hcontext->jongseong[0]) {          /* have jong */          ch = im_hangul_jongseong_to_unicode(hcontext->jongseong[0]);          n = g_unichar_to_utf8(ch, buf);          buf[n] = '\0';          return n;        } else {          /* have nothing */          ;        }      }    }  }  /*  g_print("PreEdit: U+%04x U+%04x U+%04x (%d)\n",  	  hcontext->choseong[0],	  hcontext->jungseong[0],	  hcontext->jongseong[0], n);  */  return n;}static voidim_hangul_preedit_underline (PangoAttrList **attrs, gint start, gint end){  PangoAttribute *attr;  *attrs = pango_attr_list_new();  attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);  attr->start_index = start;  attr->end_index = end;  pango_attr_list_insert (*attrs, attr);}static voidim_hangul_preedit_foreground (PangoAttrList **attrs, gint start, gint end){  PangoAttribute *attr;  *attrs = pango_attr_list_new();  attr = pango_attr_foreground_new(0xeeee, 0x0, 0x0);  attr->start_index = start;  attr->end_index = end;  pango_attr_list_insert (*attrs, attr);}static voidim_hangul_preedit_background (PangoAttrList **attrs, gint start, gint end){  PangoAttribute *attr;  *attrs = pango_attr_list_new();  attr = pango_attr_foreground_new(pref_fg.red, pref_fg.green, pref_fg.blue);  attr->start_index = start;  attr->end_index = end;  pango_attr_list_insert (*attrs, attr);  attr = pango_attr_background_new(pref_bg.red, pref_bg.green, pref_bg.blue);  //attr = pango_attr_background_new(0xCCCC, 0xCCCC, 0xCCCC);  attr->start_index = start;  attr->end_index = end;  pango_attr_list_insert (*attrs, attr);}static voidim_hangul_preedit_nothing (PangoAttrList **attrs, gint start, gint end){  /* we do nothing */  *attrs = pango_attr_list_new();}static voidim_hangul_get_preedit_string(GtkIMContext *context, gchar **str,			     PangoAttrList **attrs,			     gint *cursor_pos){  gchar buf[40];  int len = 0;  GtkIMContextHangul *hcontext = GTK_IM_CONTEXT_HANGUL(context);  buf[0] = '\0';  len = im_hangul_preedit_string(hcontext, buf);  if (attrs) {    im_hangul_preedit_attr(attrs, 0, len);  }  if (cursor_pos) {    *cursor_pos = len;  }  if (str) {    *str = g_strdup(buf);  }}static voidim_hangul_focus_in(GtkIMContext *context){  GtkIMContextHangul *hcontext = GTK_IM_CONTEXT_HANGUL(context);  status_window_show(hcontext);}static voidim_hangul_focus_out(GtkIMContext *context){  GtkIMContextHangul *hcontext = GTK_IM_CONTEXT_HANGUL(context);  if (hanja_window == NULL) {    if (im_hangul_commit(hcontext)) {      g_signal_emit_by_name (hcontext, "preedit_changed");      hcontext->state = MODE_HANGUL;    }  }  status_window_hide(hcontext);}static gbooleanim_hangul_is_ignore_key(guint16 key){  int i;  for (i = 0; im_hangul_ignore_table[i]; i++) {    if (key == im_hangul_ignore_table[i]) {      return TRUE;    }  }  return FALSE;}static gbooleanim_hangul_is_trigger(GdkEventKey *key){  return ( key->keyval == GDK_Hangul || 	  (key->keyval == GDK_space && is_set(key->state, GDK_SHIFT_MASK)));}static gbooleanim_hangul_is_backspace(GdkEventKey *key){  return (key->keyval == GDK_BackSpace);}static voidim_hangul_reset(GtkIMContext *context){  GtkIMContextHangul *hcontext = GTK_IM_CONTEXT_HANGUL (context);  if (im_hangul_commit(hcontext))    g_signal_emit_by_name (hcontext, "preedit_changed");}static gbooleanim_hangul_process_nonhangul(GtkIMContextHangul *hcontext,		            GdkEventKey *key){  if (!im_hangul_is_modifier(key->state)) {    gunichar ch = gdk_keyval_to_unicode(key->keyval);    if (ch != 0) {      gchar buf[10];      gint n = g_unichar_to_utf8(ch, buf);      buf[n] = '\0';      /* g_print("Nonhangul: %s(U+%04x)\n", buf, ch); */      g_signal_emit_by_name(hcontext, "commit", buf);      return TRUE;    }   }  return FALSE;}static gbooleanim_hangul_handle_direct_mode(GtkIMContextHangul *hcontext,		             GdkEventKey *key){  if (im_hangul_is_trigger(key)) {    if (im_hangul_commit(hcontext))      g_signal_emit_by_name (hcontext, "preedit_changed");    im_hangul_mode_hangul(hcontext);    return FALSE;  }  return im_hangul_process_nonhangul(hcontext, key);}static gbooleanim_hangul_commit(GtkIMContextHangul *hcontext){  int i;  int n = 0;  gchar buf[40];   buf[0] = '\0';  if (im_hangul_is_empty(hcontext))    return FALSE;  if (manual_mode) {    /* we use conjoining jamo, U+1100 - U+11FF */    if (hcontext->choseong[0] == 0)      n += g_unichar_to_utf8(HCF, buf + n);    else {      for (i = 0; i <= hcontext->lindex; i++)        n += g_unichar_to_utf8(hcontext->choseong[i], buf + n);    }    if (hcontext->jungseong[0] == 0)      n += g_unichar_to_utf8(HJF, buf + n);    else {      for (i = 0; i <= hcontext->vindex; i++)        n += g_unichar_to_utf8(hcontext->jungseong[i], buf + n);    }    if (hcontext->jongseong[0] != 0) {      for (i = 0; i <= hcontext->tindex; i++)        n += g_unichar_to_utf8(hcontext->jongseong[i], buf + n);    }    buf[n] = '\0';  } else if (pref_use_hangul_jamo) {    /* we use conjoining jamo, U+1100 - U+11FF */    if (hcontext->choseong[0] == 0)      n = g_unichar_to_utf8(HCF, buf);    else      n = g_unichar_to_utf8(hcontext->choseong[0], buf);     if (hcontext->jungseong[0] == 0)      n += g_unichar_to_utf8(HJF, buf + n);    else      n += g_unichar_to_utf8(hcontext->jungseong[0], buf + n);     if (hcontext->jongseong[0] != 0)      n += g_unichar_to_utf8(hcontext->jongseong[0], buf + n);    buf[n] = '\0';  } else {    /* use hangul syllables (U+AC00 - U+D7AF)     * and compatibility jamo (U+3130 - U+318F) */    gunichar ch;    ch = im_hangul_jamo_to_syllable(hcontext->choseong[0],          			    hcontext->jungseong[0],                                    hcontext->jongseong[0]);     if (ch) {      n = g_unichar_to_utf8(ch, buf);      buf[n] = '\0';      /* g_print("commit char: %s(U+%04x)\n", buf, ch); */    } else {      if (hcontext->choseong[0]) {        ch = im_hangul_choseong_to_unicode(hcontext->choseong[0]);        n = g_unichar_to_utf8(ch, buf);        buf[n] = '\0';        /* g_print("commit char: %s(U+%x)\n", buf, ch); */      }      if (hcontext->jungseong[0]) {        ch = im_hangul_jungseong_to_unicode(hcontext->jungseong[0]);        n += g_unichar_to_utf8(ch, buf + n);        buf[n] = '\0';        /* g_print("commit char: %s(U+%x)\n", buf, ch); */      }      if (hcontext->jongseong[0]) {        ch = im_hangul_jongseong_to_unicode(hcontext->jongseong[0]);        n += g_unichar_to_utf8(ch, buf + n);        buf[n] = '\0';        /* g_print("commit char: %s(U+%x)\n", buf, ch); */      }    }  }  im_hangul_clear_buf(hcontext);  g_signal_emit_by_name(hcontext, "commit", buf);  return TRUE;}static voidim_hangul_commit_utf8(GtkIMContextHangul *hcontext, gchar *utf8){  gchar buf[9];  g_return_if_fail(utf8);  strncpy(buf, utf8, sizeof(buf));  buf[8] = '\0';  im_hangul_clear_buf(hcontext);  g_signal_emit_by_name(hcontext, "commit", buf);}/* this is a very dangerous function: * safe only when GDKKEYSYMS's value is enumarated  */static guintim_hangul_dvorak_to_qwerty(guint code){  /* maybe safe if we use switch statement */  static guint table[] = {    GDK_exclam,			/* GDK_exclam */    GDK_Q,			/* GDK_quotedbl */    GDK_numbersign,		/* GDK_numbersign */    GDK_dollar,			/* GDK_dollar */    GDK_percent,		/* GDK_percent */    GDK_ampersand,		/* GDK_ampersand */    GDK_q,			/* GDK_apostrophe */    GDK_parenleft,		/* GDK_parenleft */    GDK_parenright,		/* GDK_parenright */    GDK_asterisk,		/* GDK_asterisk */    GDK_braceright,		/* GDK_plus */    GDK_w,			/* GDK_comma */    GDK_apostrophe,		/* GDK_minus */    GDK_e,			/* GDK_period */    GDK_bracketleft,		/* GDK_slash */    GDK_0,			/* GDK_0 */    GDK_1,			/* GDK_1 */    GDK_2,			/* GDK_2 */    GDK_3,			/* GDK_3 */    GDK_4,			/* GDK_4 */    GDK_5,			/* GDK_5 */    GDK_6,			/* GDK_6 */    GDK_7,			/* GDK_7 */    GDK_8,			/* GDK_8 */    GDK_9,			/* GDK_9 */    GDK_Z,			/* GDK_colon */    GDK_z,			/* GDK_semicolon */    GDK_W,			/* GDK_less */    GDK_bracketright,		/* GDK_qual */    GDK_E,			/* GDK_greater */    GDK_braceleft,		/* GDK_question */    GDK_at,			/* GDK_at */    GDK_A,			/* GDK_A */    GDK_N,			/* GDK_B */    GDK_I,			/* GDK_C */    GDK_H,			/* GDK_D */    GDK_D,			/* GDK_E */    GDK_Y,			/* GDK_F */    GDK_U,			/* GDK_G */    GDK_J,			/* GDK_H */    GDK_G,			/* GDK_I */    GDK_C,			/* GDK_J */    GDK_V,			/* GDK_K */    GDK_P,			/* GDK_L */    GDK_M,			/* GDK_M */    GDK_L,			/* GDK_N */    GDK_S,			/* GDK_O */    GDK_R,			/* GDK_P */    GDK_X,			/* GDK_Q */    GDK_O,			/* GDK_R */    GDK_colon,			/* GDK_S */    GDK_K,			/* GDK_T */    GDK_F,			/* GDK_U */    GDK_greater,		/* GDK_V */    GDK_less,			/* GDK_W */    GDK_B,			/* GDK_X */    GDK_T,			/* GDK_Y */    GDK_question,		/* GDK_Z */    GDK_minus,			/* GDK_bracketleft */    GDK_backslash,		/* GDK_backslash */    GDK_equal,			/* GDK_bracketright */    GDK_asciicircum,		/* GDK_asciicircum */    GDK_quotedbl,		/* GDK_underscore */    GDK_grave,			/* GDK_grave */    GDK_a,			/* GDK_a */    GDK_n,			/* GDK_b */    GDK_i,			/* GDK_c */    GDK_h,			/* GDK_d */    GDK_d,			/* GDK_e */    GDK_y,			/* GDK_f */    GDK_u,			/* GDK_g */    GDK_j,			/* GDK_h */    GDK_g,			/* GDK_i */    GDK_c,			/* GDK_j */    GDK_v,			/* GDK_k */    GDK_p,			/* GDK_l */    GDK_m,			/* GDK_m */    GDK_l,			/* GDK_n */    GDK_s,			/* GDK_o */    GDK_r,			/* GDK_p */    GDK_x,			/* GDK_q */    GDK_o,			/* GDK_r */    GDK_semicolon,		/* GDK_s */    GDK_k,			/* GDK_t */    GDK_f,			/* GDK_u */    GDK_period,			/* GDK_v */    GDK_comma,			/* GDK_w */    GDK_b,			/* GDK_x */    GDK_t,			/* GDK_y */    GDK_slash,			/* GDK_z */    GDK_underscore,		/* GDK_braceleft */    GDK_bar,			/* GDK_bar */    GDK_plus,			/* GDK_braceright */    GDK_asciitilde,		/* GDK_asciitilde */  };  if (code < GDK_exclam || code > GDK_asciitilde)    return code;  return table[code - GDK_exclam];}/* use hangul automata */static gbooleanim_hangul_filter_keypress(GtkIMContext *context, GdkEventKey *key){  GtkIMContextHangul *hcontext = GTK_IM_CONTEXT_HANGUL(context);  /* ignore key release */  if (key->type == GDK_KEY_RELEASE)    return FALSE;  /* we silently ignore shift keys */  if (key->keyval == GDK_Shift_L || key->keyval == GDK_Shift_R)    return FALSE;  /* on Ctrl-Hangul we turn on/off manual_mode */  if (key->keyval == GDK_Hangul && key->state & GDK_CONTROL_MASK)    manual_mode = !manual_mode;  /* on capslock, we use Hangul Jamo */  if (pref_use_caps_lock && key->keyval == GDK_Caps_Lock)    pref_use_hangul_jamo = !pref_use_hangul_jamo;  /* some keys are ignored: Ctrl, Alt, Meta */  /* we flush out all preedit text */  if (im_hangul_is_ignore_key(key->keyval)) {    if (im_hangul_commit(hcontext))      g_signal_emit_by_name (hcontext, "preedit_changed");    return FALSE;  }  /* handle direct mode */  if (input_mode == MODE_DIRECT) {    return im_hangul_handle_direct_mode(hcontext, key);  }  /* handle Escape key: automaticaly change to direct mode */  if (key->keyval == GDK_Escape) {    if (im_hangul_commit(hcontext))      g_signal_emit_by_name (hcontext, "preedit_changed");    im_hangul_mode_direct(hcontext);    return FALSE;  }  /* modifiler key */  if (im_hangul_is_modifier(key->state)) {    if (im_hangul_commit(hcontext))      g_signal_emit_by_name (hcontext, "preedit_changed");    return FALSE;  }  /* hanja key */  if (key->keyval == GDK_F9 || key->keyval == GDK_Hangul_Hanja) {    popup_hanja_window(hcontext);    return TRUE;  }  /* here we must hangul mode, so set STATE_HANGUL   * static variable input_mode is not yet applied so we change it   * below line must not removed */  if (hcontext->state == STATE_DIRECT)    hcontext->state = STATE_HANGUL;  if (hcontext->automata)    return hcontext->automata(hcontext, key);  else    return FALSE;}/* * status window */static gbooleanstatus_window_expose_event(GtkWidget *widget, GdkEventExpose *event){  gdk_draw_rectangle (widget->window,		      widget->style->fg_gc[GTK_STATE_NORMAL],		      FALSE,		      0, 0,		      widget->allocation.width-1, widget->allocation.height-1);  return FALSE;}static voidstatus_window_free(StatusWindow *status_window){  status_windows = g_slist_remove(status_windows, status_window);    g_signal_handler_disconnect(status_window->toplevel,			      status_window->destroy_handler_id);  g_signal_handler_disconnect(status_window->toplevel,			      status_window->configure_handler_id);  gtk_widget_destroy(status_window->window);  g_object_set_data(G_OBJECT(status_window->toplevel),  		    "gtk-imhangul-status-window", NULL);  g_free(status_window);}static gbooleanstatus_window_configure	(GtkWidget *toplevel,			 GdkEventConfigure *event,			 GtkWidget *window){  GdkRectangle rect;  GtkRequisition requisition;  gint y;  gdk_window_get_frame_extents(toplevel->window, &rect);  gtk_widget_size_request(window, &requisition);  if (rect.y + rect.height + requisition.height < gdk_screen_height())    y = rect.y + rect.height;  else    y = gdk_screen_height() - requisition.height;  gtk_window_move(GTK_WINDOW(window), rect.x, y);  return FALSE;}static gbooleanon_click_hangul (GtkWidget *widget,		 GdkEventButton *event,		 gpointer data){  GtkIMContextHangul *hcontext = GTK_IM_CONTEXT_HANGUL(data);  if (input_mode == STATE_DIRECT) {    /* english mode change to hangul mode */    im_hangul_mode_hangul(hcontext);  } else {    /* hangul mode change to english mode */    if (im_hangul_commit(hcontext))      g_signal_emit_by_name (hcontext, "preedit_changed");    im_hangul_mode_direct(hcontext);  }  return TRUE;}static gbooleanon_click_hanja (GtkWidget *widget,		 GdkEventButton *event,		 gpointer data){  GtkIMContextHangul *hcontext = GTK_IM_CONTEXT_HANGUL(data);  popup_hanja_window(hcontext);  return TRUE;}static GtkWidget *get_toplevel_window(GdkWindow *window){  GtkWidget *toplevel;  GdkWindow *parent;  if (window == NULL)    return NULL;  while (TRUE) {    parent = gdk_window_get_parent(window);    if (parent == gdk_get_default_root_window())      break;    else      window = parent;  }  gdk_window_get_user_data(window, (gpointer *)&toplevel);  return toplevel;}