1
/* ImHangul - Gtk+ 2.0 Input Method Module for Hangul
2
* Copyright (C) 2002-2008 Choe Hwanjin
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Library General Public
6
* License as published by the Free Software Foundation; either
7
* version 2 of the License, or (at your option) any later version.
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Library General Public License for more details.
14
* You should have received a copy of the GNU Library General Public
15
* License along with this library; if not, write to the
16
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17
* Boston, MA 02111-1307, USA.
26
#include <gdk/gdkkeysyms.h>
32
#include "gtkimcontexthangul.h"
42
INPUT_MODE_INFO_ENGLISH,
43
INPUT_MODE_INFO_HANGUL
44
} IMHangulInputModeInfo;
46
typedef struct _CandidateItem CandidateItem;
47
typedef struct _StatusWindow StatusWindow;
55
guint destroy_handler_id;
56
guint configure_handler_id;
59
/* Candidate window */
62
GtkIMContextHangul *hangul_context;
75
struct _CandidateItem {
80
static size_t ucschar_strlen(const ucschar* s);
82
static Candidate* candidate_new (char *key,
87
GtkIMContextHangul *hcontext);
88
static void candidate_prev (Candidate *candidate);
89
static void candidate_next (Candidate *candidate);
90
static void candidate_prev_page (Candidate *candidate);
91
static void candidate_next_page (Candidate *candidate);
92
static const Hanja* candidate_get_current (Candidate *candidate);
93
static const Hanja* candidate_get_nth (Candidate *candidate, int index);
94
static void candidate_delete (Candidate *candidate);
96
static void im_hangul_class_init (GtkIMContextHangulClass *klass);
97
static void im_hangul_ic_init (GtkIMContextHangul *hcontext);
98
static void im_hangul_ic_finalize (GObject *obj);
100
static void im_hangul_ic_reset (GtkIMContext *context);
101
static gboolean im_hangul_ic_slave_filter_keypress (GtkIMContext *context,
103
static gboolean im_hangul_ic_filter_keypress (GtkIMContext *context,
106
static void im_hangul_get_preedit_string (GtkIMContext *ic,
108
PangoAttrList **attrs,
111
static void im_hangul_ic_focus_in (GtkIMContext *context);
112
static void im_hangul_ic_focus_out (GtkIMContext *context);
113
static void im_hangul_ic_set_client_window (GtkIMContext *context,
114
GdkWindow *client_window);
115
static void im_hangul_ic_set_use_preedit (GtkIMContext *context,
116
gboolean use_preedit);
117
static void im_hangul_ic_cursor_location (GtkIMContext *context,
120
/* asistant function for hangul composer */
121
static inline gboolean im_hangul_is_modifier (guint state);
122
static inline gboolean im_hangul_is_trigger (GdkEventKey *key);
123
static inline gboolean im_hangul_is_backspace (GdkEventKey *key);
124
static inline void im_hangul_ic_emit_preedit_changed (GtkIMContextHangul *hcontext);
126
/* commit functions */
127
static void im_hangul_ic_commit_by_slave (GtkIMContext *context,
128
gchar *str, gpointer data);
130
/* for feedback (preedit attribute) */
131
static void im_hangul_preedit_underline (GtkIMContextHangul *hic,
132
PangoAttrList **attrs,
133
gint start, gint end);
134
static void im_hangul_preedit_reverse (GtkIMContextHangul *hic,
135
PangoAttrList **attrs,
136
gint start, gint end);
137
static void im_hangul_preedit_shade (GtkIMContextHangul *hic,
138
PangoAttrList **attrs,
139
gint start, gint end);
140
static void im_hangul_preedit_foreground (GtkIMContextHangul *hic,
141
PangoAttrList **attrs,
142
gint start, gint end);
143
static void im_hangul_preedit_background (GtkIMContextHangul *hic,
144
PangoAttrList **attrs,
145
gint start, gint end);
146
static void im_hangul_preedit_color (GtkIMContextHangul *hic,
147
PangoAttrList **attrs,
148
gint start, gint end);
149
static void im_hangul_preedit_normal (GtkIMContextHangul *hic,
150
PangoAttrList **attrs,
151
gint start, gint end);
153
static char* im_hangul_get_candidate_string(GtkIMContextHangul *ic);
155
static void im_hangul_ic_show_status_window (GtkIMContextHangul *hcontext);
156
static void im_hangul_ic_hide_status_window (GtkIMContextHangul *hcontext);
157
static int im_hangul_ic_get_toplevel_input_mode(GtkIMContextHangul *hcontext);
158
static void im_hangul_ic_set_toplevel_input_mode(GtkIMContextHangul *hcontext,
161
static Toplevel* toplevel_new(GtkWidget *toplevel_widget);
162
static Toplevel* toplevel_get(GdkWindow *window);
163
static void toplevel_append_context(Toplevel *toplevel,
164
GtkIMContextHangul *context);
165
static void toplevel_remove_context(Toplevel *toplevel,
166
GtkIMContextHangul *context);
167
static void toplevel_delete(Toplevel *toplevel);
168
static GtkWidget* status_window_new(GtkWidget *parent);
170
static void popup_candidate_window (GtkIMContextHangul *hcontext);
171
static void close_candidate_window (GtkIMContextHangul *hic);
173
GType gtk_type_im_context_hangul = 0;
175
/* static variables for hangul immodule */
176
static GObjectClass *parent_class;
178
static GSList *toplevels = NULL;
180
static guint snooper_handler_id = 0;
181
static GtkIMContext *current_focused_ic = NULL;
183
static HanjaTable* hanja_table = NULL;
186
static gboolean pref_use_capslock = FALSE;
187
static gboolean pref_use_status_window = FALSE;
188
static gboolean pref_use_dvorak = FALSE;
189
static gboolean pref_use_system_keymap = FALSE;
190
static gboolean pref_use_preedit_string = TRUE;
191
static void (*im_hangul_preedit_attr)(GtkIMContextHangul *hic,
192
PangoAttrList **attrs,
195
im_hangul_preedit_foreground;
196
static GdkColor pref_fg = { 0, 0xeeee, 0, 0 };
197
static GdkColor pref_bg = { 0, 0xFFFF, 0xFFFF, 0xFFFF };
200
static const GScannerConfig im_hangul_scanner_config = {
203
) /* cset_skip_characters */,
208
) /* cset_identifier_first */,
216
) /* cset_identifier_nth */,
217
( "#\n" ) /* cpair_comment_single */,
219
FALSE /* case_sensitive */,
221
TRUE /* skip_comment_multi */,
222
TRUE /* skip_comment_single */,
223
TRUE /* scan_comment_multi */,
224
TRUE /* scan_identifier */,
225
FALSE /* scan_identifier_1char */,
226
FALSE /* scan_identifier_NULL */,
227
TRUE /* scan_symbols */,
228
FALSE /* scan_binary */,
229
TRUE /* scan_octal */,
230
TRUE /* scan_float */,
232
FALSE /* scan_hex_dollar */,
233
TRUE /* scan_string_sq */,
234
TRUE /* scan_string_dq */,
235
TRUE /* numbers_2_int */,
236
FALSE /* int_2_float */,
237
FALSE /* identifier_2_string */,
238
TRUE /* char_2_token */,
239
TRUE /* symbol_2_token */,
240
FALSE /* scope_0_fallback */,
241
FALSE /* store_int64 */,
245
TOKEN_FALSE = G_TOKEN_LAST,
247
TOKEN_ENABLE_STATUS_WINDOW,
248
TOKEN_ENABLE_PREEDIT,
249
TOKEN_ENABLE_CAPSLOCK,
251
TOKEN_ENABLE_SYSTEM_KEYMAP,
253
TOKEN_PREEDIT_STYLE_FG,
254
TOKEN_PREEDIT_STYLE_BG
257
static const struct {
261
{ "false", TOKEN_FALSE },
262
{ "true", TOKEN_TRUE },
263
{ "off", TOKEN_FALSE },
264
{ "on", TOKEN_TRUE },
265
{ "enable_status_window", TOKEN_ENABLE_STATUS_WINDOW },
266
{ "enable_preedit", TOKEN_ENABLE_PREEDIT },
267
{ "enable_capslock", TOKEN_ENABLE_CAPSLOCK },
268
{ "enable_dvorak", TOKEN_ENABLE_DVORAK },
269
{ "enable_system_keymap", TOKEN_ENABLE_SYSTEM_KEYMAP },
270
{ "preedit_style", TOKEN_PREEDIT_STYLE },
271
{ "preedit_style_fg", TOKEN_PREEDIT_STYLE_FG },
272
{ "preedit_style_bg", TOKEN_PREEDIT_STYLE_BG }
276
set_preedit_style (const char *style)
279
im_hangul_preedit_attr = im_hangul_preedit_foreground;
280
} else if (g_ascii_strcasecmp(style, "underline") == 0) {
281
im_hangul_preedit_attr = im_hangul_preedit_underline;
282
} else if (g_ascii_strcasecmp(style, "reverse") == 0) {
283
im_hangul_preedit_attr = im_hangul_preedit_reverse;
284
} else if (g_ascii_strcasecmp(style, "shade") == 0) {
285
im_hangul_preedit_attr = im_hangul_preedit_shade;
286
} else if (g_ascii_strcasecmp(style, "foreground") == 0) {
287
im_hangul_preedit_attr = im_hangul_preedit_foreground;
288
} else if (g_ascii_strcasecmp(style, "background") == 0) {
289
im_hangul_preedit_attr = im_hangul_preedit_background;
290
} else if (g_ascii_strcasecmp(style, "color") == 0) {
291
im_hangul_preedit_attr = im_hangul_preedit_color;
292
} else if (g_ascii_strcasecmp(style, "normal") == 0) {
293
im_hangul_preedit_attr = im_hangul_preedit_normal;
295
im_hangul_preedit_attr = im_hangul_preedit_foreground;
299
void im_hangul_config_parser(void)
305
const gchar *env_conf_file;
306
gchar *conf_file = NULL;
311
env_conf_file = g_getenv("IM_HANGUL_CONF_FILE");
312
if (env_conf_file == NULL) {
313
const gchar *homedir = g_get_home_dir();
317
conf_file = g_build_filename(homedir, ".imhangul.conf", NULL);
319
conf_file = g_strdup(env_conf_file);
322
file = fopen(conf_file, "r");
328
scanner = g_scanner_new(&im_hangul_scanner_config);
329
g_scanner_input_file(scanner, fd);
331
for (i = 0; i < G_N_ELEMENTS (symbols); i++) {
332
g_scanner_scope_add_symbol(scanner, 0,
333
symbols[i].name, GINT_TO_POINTER(symbols[i].token));
337
type = g_scanner_get_next_token(scanner);
338
if (type == TOKEN_ENABLE_PREEDIT) {
339
type = g_scanner_get_next_token(scanner);
340
if (type == G_TOKEN_EQUAL_SIGN) {
341
type = g_scanner_get_next_token(scanner);
342
if (type == TOKEN_TRUE) {
343
pref_use_preedit_string = TRUE;
345
pref_use_preedit_string = FALSE;
348
} else if (type == TOKEN_ENABLE_STATUS_WINDOW) {
349
type = g_scanner_get_next_token(scanner);
350
if (type == G_TOKEN_EQUAL_SIGN) {
351
type = g_scanner_get_next_token(scanner);
352
if (type == TOKEN_TRUE) {
353
pref_use_status_window = TRUE;
355
pref_use_status_window = FALSE;
358
} else if (type == TOKEN_ENABLE_CAPSLOCK) {
359
type = g_scanner_get_next_token(scanner);
360
if (type == G_TOKEN_EQUAL_SIGN) {
361
type = g_scanner_get_next_token(scanner);
362
if (type == TOKEN_TRUE) {
363
pref_use_capslock = TRUE;
365
pref_use_capslock = FALSE;
368
} else if (type == TOKEN_ENABLE_DVORAK) {
369
type = g_scanner_get_next_token(scanner);
370
if (type == G_TOKEN_EQUAL_SIGN) {
371
type = g_scanner_get_next_token(scanner);
372
if (type == TOKEN_TRUE) {
373
pref_use_dvorak = TRUE;
375
pref_use_dvorak = FALSE;
378
} else if (type == TOKEN_ENABLE_SYSTEM_KEYMAP) {
379
type = g_scanner_get_next_token(scanner);
380
if (type == G_TOKEN_EQUAL_SIGN) {
381
type = g_scanner_get_next_token(scanner);
382
if (type == TOKEN_TRUE) {
383
pref_use_system_keymap = TRUE;
385
pref_use_system_keymap = FALSE;
388
} else if (type == TOKEN_PREEDIT_STYLE) {
389
type = g_scanner_get_next_token(scanner);
390
if (type == G_TOKEN_EQUAL_SIGN) {
391
type = g_scanner_get_next_token(scanner);
392
if (type == G_TOKEN_IDENTIFIER) {
393
value = g_scanner_cur_value(scanner);
394
str = value.v_identifier;
395
set_preedit_style(str);
398
} else if (type == TOKEN_PREEDIT_STYLE_FG) {
399
type = g_scanner_get_next_token(scanner);
400
if (type == G_TOKEN_EQUAL_SIGN) {
401
type = g_scanner_get_next_token(scanner);
402
if (type == G_TOKEN_STRING) {
403
value = g_scanner_cur_value(scanner);
404
str = value.v_identifier;
405
gdk_color_parse(str, &pref_fg);
408
} else if (type == TOKEN_PREEDIT_STYLE_BG) {
409
type = g_scanner_get_next_token(scanner);
410
if (type == G_TOKEN_EQUAL_SIGN) {
411
type = g_scanner_get_next_token(scanner);
412
if (type == G_TOKEN_STRING) {
413
value = g_scanner_cur_value(scanner);
414
str = value.v_identifier;
415
gdk_color_parse(str, &pref_bg);
419
type = g_scanner_get_next_token(scanner);
420
if (type == G_TOKEN_EQUAL_SIGN) {
421
type = g_scanner_get_next_token(scanner);
424
} while (!g_scanner_eof(scanner));
426
g_scanner_destroy(scanner);
432
gtk_im_context_hangul_register_type (GTypeModule *type_module)
434
static const GTypeInfo im_context_hangul_info = {
435
sizeof(GtkIMContextHangulClass),
436
(GBaseInitFunc) NULL,
437
(GBaseFinalizeFunc) NULL,
438
(GClassInitFunc) im_hangul_class_init,
441
sizeof(GtkIMContextHangul),
443
(GInstanceInitFunc) im_hangul_ic_init,
446
gtk_type_im_context_hangul =
447
g_type_module_register_type (type_module,
449
"GtkIMContextHangul",
450
&im_context_hangul_info, 0);
454
im_hangul_class_init (GtkIMContextHangulClass *klass)
456
GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS(klass);
457
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
459
parent_class = g_type_class_peek_parent (klass);
461
im_context_class->set_client_window = im_hangul_ic_set_client_window;
462
im_context_class->filter_keypress = im_hangul_ic_slave_filter_keypress;
463
im_context_class->reset = im_hangul_ic_reset;
464
im_context_class->focus_in = im_hangul_ic_focus_in;
465
im_context_class->focus_out = im_hangul_ic_focus_out;
466
im_context_class->get_preedit_string = im_hangul_get_preedit_string;
467
im_context_class->set_use_preedit = im_hangul_ic_set_use_preedit;
468
im_context_class->set_cursor_location = im_hangul_ic_cursor_location;
470
gobject_class->finalize = im_hangul_ic_finalize;
474
im_hangul_ic_init (GtkIMContextHangul *hcontext)
476
hcontext->slave = gtk_im_context_simple_new();
477
g_signal_connect(G_OBJECT(hcontext->slave), "commit",
478
G_CALLBACK(im_hangul_ic_commit_by_slave), hcontext);
480
hcontext->client_window = NULL;
481
hcontext->toplevel = NULL;
482
hcontext->cursor.x = 0;
483
hcontext->cursor.y = 0;
484
hcontext->cursor.width = -1;
485
hcontext->cursor.height = -1;
487
hcontext->hic = hangul_ic_new("2");
488
hcontext->preedit = g_string_new(NULL);
490
hcontext->candidate = NULL;
491
hcontext->candidate_string = NULL;
494
hcontext->use_preedit = TRUE;
498
im_hangul_ic_finalize (GObject *object)
500
GtkIMContextHangul *hic = GTK_IM_CONTEXT_HANGUL(object);
502
if (hic->toplevel != NULL)
503
toplevel_remove_context(hic->toplevel, hic);
505
hangul_ic_delete(hic->hic);
506
g_string_free(hic->preedit, TRUE);
508
gtk_im_context_reset(hic->slave);
509
g_signal_handlers_disconnect_by_func(hic->slave,
510
im_hangul_ic_commit_by_slave,
512
g_object_unref(G_OBJECT(hic->slave));
515
G_OBJECT_CLASS(parent_class)->finalize (object);
516
if ((GObject*)current_focused_ic == object)
517
current_focused_ic = NULL;
521
im_hangul_ic_set_client_window (GtkIMContext *context,
522
GdkWindow *client_window)
524
GtkIMContextHangul *hcontext;
526
g_return_if_fail (context != NULL);
527
g_return_if_fail (GTK_IS_IM_CONTEXT_HANGUL (context));
529
hcontext = GTK_IM_CONTEXT_HANGUL(context);
531
if (hcontext->client_window == client_window)
534
if (hcontext->toplevel != NULL)
535
toplevel_remove_context(hcontext->toplevel, hcontext);
537
if (client_window == NULL) {
538
hcontext->client_window = NULL;
539
hcontext->toplevel = NULL;
543
hcontext->client_window = client_window;
544
hcontext->toplevel = toplevel_get (client_window);
545
toplevel_append_context(hcontext->toplevel, hcontext);
549
gtk_im_context_hangul_new (void)
551
return GTK_IM_CONTEXT (g_object_new (GTK_TYPE_IM_CONTEXT_HANGUL, NULL));
555
gtk_im_context_hangul_select_keyboard(GtkIMContextHangul *hcontext,
556
const char *keyboard)
558
g_return_if_fail (hcontext);
560
hangul_ic_select_keyboard(hcontext->hic, keyboard);
564
im_hangul_set_input_mode_info_for_screen (GdkScreen *screen, int state)
566
if (screen != NULL) {
567
GdkWindow *root_window = gdk_screen_get_root_window(screen);
569
gdk_property_change (root_window,
570
gdk_atom_intern ("_HANGUL_INPUT_MODE", FALSE),
571
gdk_atom_intern ("INTEGER", FALSE),
572
32, GDK_PROP_MODE_REPLACE,
573
(const guchar *)&data, 1);
578
im_hangul_set_input_mode_info (GdkWindow *window, int state)
580
if (window != NULL) {
581
GdkScreen *screen = gdk_drawable_get_screen(window);
582
im_hangul_set_input_mode_info_for_screen (screen, state);
587
im_hangul_set_input_mode(GtkIMContextHangul *hcontext, int mode)
590
case INPUT_MODE_DIRECT:
591
im_hangul_set_input_mode_info (hcontext->client_window,
592
INPUT_MODE_INFO_ENGLISH);
593
im_hangul_ic_hide_status_window(hcontext);
594
g_signal_emit_by_name (hcontext, "preedit_end");
596
case INPUT_MODE_HANGUL:
597
im_hangul_set_input_mode_info (hcontext->client_window,
598
INPUT_MODE_INFO_HANGUL);
599
im_hangul_ic_show_status_window(hcontext);
600
g_signal_emit_by_name (hcontext, "preedit_start");
603
im_hangul_ic_set_toplevel_input_mode(hcontext, mode);
607
im_hangul_preedit_underline (GtkIMContextHangul *hic,
608
PangoAttrList **attrs, gint start, gint end)
610
PangoAttribute *attr;
612
*attrs = pango_attr_list_new ();
613
attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
614
attr->start_index = start;
615
attr->end_index = end;
616
pango_attr_list_insert (*attrs, attr);
620
im_hangul_preedit_reverse (GtkIMContextHangul *hic,
621
PangoAttrList **attrs, gint start, gint end)
623
static GdkColor default_base = { 0, 0xffff, 0xffff, 0xffff };
624
static GdkColor default_text = { 0, 0, 0, 0 };
626
PangoAttribute *attr;
627
GtkWidget *widget = NULL;
628
GdkColor *fg = &default_base;
629
GdkColor *bg = &default_text;
631
gdk_window_get_user_data(hic->client_window, (gpointer)&widget);
632
if (widget != NULL) {
633
GtkStyle *style = gtk_widget_get_style(widget);
634
fg = &style->base[GTK_STATE_NORMAL];
635
bg = &style->text[GTK_STATE_NORMAL];
638
*attrs = pango_attr_list_new ();
639
attr = pango_attr_foreground_new (fg->red, fg->green, fg->blue);
640
attr->start_index = start;
641
attr->end_index = end;
642
pango_attr_list_insert (*attrs, attr);
644
attr = pango_attr_background_new (bg->red, bg->green, bg->blue);
645
attr->start_index = start;
646
attr->end_index = end;
647
pango_attr_list_insert (*attrs, attr);
651
im_hangul_preedit_shade (GtkIMContextHangul *hic,
652
PangoAttrList **attrs, gint start, gint end)
654
static const GdkColor default_text = { 0, 0, 0, 0 };
655
static const GdkColor default_base = { 0, 0xffff * 90 / 100,
659
PangoAttribute *attr;
660
GtkWidget *widget = NULL;
661
GdkColor fg = default_text;
662
GdkColor bg = default_base;
664
gdk_window_get_user_data(hic->client_window, (gpointer)&widget);
665
if (widget != NULL) {
666
GtkStyle *style = gtk_widget_get_style(widget);
668
fg.red = style->text[GTK_STATE_NORMAL].red;
669
fg.green = style->text[GTK_STATE_NORMAL].green;
670
fg.blue = style->text[GTK_STATE_NORMAL].blue;
671
bg.red = (style->base[GTK_STATE_NORMAL].red * 90 +
672
style->text[GTK_STATE_NORMAL].red * 10) / 100;
673
bg.green = (style->base[GTK_STATE_NORMAL].green * 90 +
674
style->text[GTK_STATE_NORMAL].green * 10) / 100;
675
bg.blue = (style->base[GTK_STATE_NORMAL].blue * 90 +
676
style->text[GTK_STATE_NORMAL].blue * 10) / 100;
680
*attrs = pango_attr_list_new ();
681
attr = pango_attr_foreground_new (fg.red, fg.green, fg.blue);
682
attr->start_index = start;
683
attr->end_index = end;
684
pango_attr_list_insert (*attrs, attr);
686
attr = pango_attr_background_new (bg.red, bg.green, bg.blue);
687
attr->start_index = start;
688
attr->end_index = end;
689
pango_attr_list_insert (*attrs, attr);
693
im_hangul_preedit_foreground (GtkIMContextHangul *hic,
694
PangoAttrList **attrs, gint start, gint end)
696
PangoAttribute *attr;
698
*attrs = pango_attr_list_new ();
699
attr = pango_attr_foreground_new (pref_fg.red, pref_fg.green, pref_fg.blue);
700
attr->start_index = start;
701
attr->end_index = end;
702
pango_attr_list_insert (*attrs, attr);
706
im_hangul_preedit_background (GtkIMContextHangul *hic,
707
PangoAttrList **attrs, gint start, gint end)
709
PangoAttribute *attr;
711
*attrs = pango_attr_list_new ();
712
attr = pango_attr_background_new (pref_bg.red, pref_bg.green, pref_bg.blue);
713
attr->start_index = start;
714
attr->end_index = end;
715
pango_attr_list_insert (*attrs, attr);
719
im_hangul_preedit_color (GtkIMContextHangul *hic,
720
PangoAttrList **attrs, gint start, gint end)
722
PangoAttribute *attr;
724
*attrs = pango_attr_list_new ();
725
attr = pango_attr_foreground_new (pref_fg.red, pref_fg.green, pref_fg.blue);
726
attr->start_index = start;
727
attr->end_index = end;
728
pango_attr_list_insert (*attrs, attr);
730
attr = pango_attr_background_new (pref_bg.red, pref_bg.green, pref_bg.blue);
731
attr->start_index = start;
732
attr->end_index = end;
733
pango_attr_list_insert (*attrs, attr);
737
im_hangul_preedit_normal (GtkIMContextHangul *hic,
738
PangoAttrList **attrs, gint start, gint end)
741
*attrs = pango_attr_list_new ();
745
im_hangul_get_preedit_string (GtkIMContext *context, gchar **str,
746
PangoAttrList **attrs,
750
GtkIMContextHangul *ic;
752
g_return_if_fail (context != NULL);
754
ic = GTK_IM_CONTEXT_HANGUL(context);
755
len = g_utf8_strlen(ic->preedit->str, -1);
758
im_hangul_preedit_attr(ic, attrs, 0, ic->preedit->len);
764
*str = g_strdup(ic->preedit->str);
768
im_hangul_ic_focus_in (GtkIMContext *context)
771
GtkIMContextHangul *hcontext;
773
g_return_if_fail (context != NULL);
775
hcontext = GTK_IM_CONTEXT_HANGUL(context);
776
input_mode = im_hangul_ic_get_toplevel_input_mode(hcontext);
777
im_hangul_set_input_mode(hcontext, input_mode);
779
current_focused_ic = context;
783
im_hangul_ic_set_preedit(GtkIMContextHangul* hic, const ucschar* preedit)
788
old = g_strdup(hic->preedit->str);
790
g_string_assign(hic->preedit, "");
791
if (preedit != NULL) {
792
for (i = 0; preedit[i] != 0; i++) {
793
g_string_append_unichar(hic->preedit, preedit[i]);
797
// preedit string이 바뀌지 않았는데도 preedit changed signal을 너무 자주
798
// 보내게 되면 오작동하는 프로그램이 있을 수 있다.
799
// GtkHtml 같은 것은 backspace키를 처리하는 과정에서도 reset을 부르는데
800
// 여기서 매번 preedit changed signal을 보내면 오작동한다.
801
if (strcmp(hic->preedit->str, old) != 0)
802
im_hangul_ic_emit_preedit_changed(hic);
808
im_hangul_ic_emit_preedit_changed (GtkIMContextHangul *hcontext)
810
if (hcontext->use_preedit)
811
g_signal_emit_by_name (hcontext, "preedit_changed");
815
im_hangul_ic_focus_out (GtkIMContext *context)
817
GtkIMContextHangul *hcontext;
819
g_return_if_fail (context != NULL);
821
im_hangul_ic_reset(context);
823
hcontext = GTK_IM_CONTEXT_HANGUL(context);
824
im_hangul_ic_hide_status_window (hcontext);
825
im_hangul_set_input_mode_info (hcontext->client_window, INPUT_MODE_INFO_NONE);
826
if (current_focused_ic == context)
827
current_focused_ic = NULL;
831
im_hangul_ic_set_use_preedit (GtkIMContext *context, gboolean use_preedit)
833
GtkIMContextHangul *hcontext;
835
g_return_if_fail (context != NULL);
837
hcontext = GTK_IM_CONTEXT_HANGUL(context);
838
hcontext->use_preedit = use_preedit;
842
im_hangul_ic_cursor_location (GtkIMContext *context, GdkRectangle *area)
844
GtkIMContextHangul *hcontext;
846
g_return_if_fail (context != NULL);
848
hcontext = GTK_IM_CONTEXT_HANGUL(context);
849
hcontext->cursor = *area;
852
static inline gboolean
853
im_hangul_is_modifier (guint state)
855
return ((state & GDK_CONTROL_MASK) || (state & GDK_MOD1_MASK));
858
static inline gboolean
859
im_hangul_is_trigger (GdkEventKey *key)
861
return ( key->keyval == GDK_Hangul ||
862
key->keyval == GDK_Alt_R ||
863
(key->keyval == GDK_space && (key->state & GDK_SHIFT_MASK)));
866
static inline gboolean
867
im_hangul_is_candidate (GdkEventKey *key)
869
return (key->keyval == GDK_Hangul_Hanja ||
870
key->keyval == GDK_F9 ||
871
key->keyval == GDK_Control_R);
874
static inline gboolean
875
im_hangul_is_backspace (GdkEventKey *key)
877
return (key->keyval == GDK_BackSpace);
881
im_hangul_ic_reset (GtkIMContext *context)
883
const ucschar* preedit;
884
const ucschar* flush;
885
GtkIMContextHangul *hic = GTK_IM_CONTEXT_HANGUL (context);
887
flush = hangul_ic_flush(hic->hic);
889
preedit = hangul_ic_get_preedit_string(hic->hic);
890
im_hangul_ic_set_preedit(hic, preedit);
893
char* str = g_ucs4_to_utf8(flush, -1, NULL, NULL, NULL);
894
g_signal_emit_by_name(hic, "commit", str);
900
im_hangul_handle_direct_mode (GtkIMContextHangul *hcontext,
903
if (im_hangul_is_trigger (key)) {
904
im_hangul_ic_reset(GTK_IM_CONTEXT(hcontext));
905
im_hangul_set_input_mode(hcontext, INPUT_MODE_HANGUL);
912
im_hangul_ic_commit_by_slave (GtkIMContext *context, gchar *str, gpointer data)
914
g_signal_emit_by_name (GTK_IM_CONTEXT_HANGUL(data), "commit", str);
917
/* this is a very dangerous function:
918
* safe only when GDKKEYSYMS's value is enumarated */
920
im_hangul_dvorak_to_qwerty (guint code)
922
/* maybe safe if we use switch statement */
923
static guint table[] = {
924
GDK_exclam, /* GDK_exclam */
925
GDK_Q, /* GDK_quotedbl */
926
GDK_numbersign, /* GDK_numbersign */
927
GDK_dollar, /* GDK_dollar */
928
GDK_percent, /* GDK_percent */
929
GDK_ampersand, /* GDK_ampersand */
930
GDK_q, /* GDK_apostrophe */
931
GDK_parenleft, /* GDK_parenleft */
932
GDK_parenright, /* GDK_parenright */
933
GDK_asterisk, /* GDK_asterisk */
934
GDK_braceright, /* GDK_plus */
935
GDK_w, /* GDK_comma */
936
GDK_apostrophe, /* GDK_minus */
937
GDK_e, /* GDK_period */
938
GDK_bracketleft, /* GDK_slash */
949
GDK_Z, /* GDK_colon */
950
GDK_z, /* GDK_semicolon */
951
GDK_W, /* GDK_less */
952
GDK_bracketright, /* GDK_qual */
953
GDK_E, /* GDK_greater */
954
GDK_braceleft, /* GDK_question */
974
GDK_colon, /* GDK_S */
977
GDK_greater, /* GDK_V */
978
GDK_less, /* GDK_W */
981
GDK_question, /* GDK_Z */
982
GDK_minus, /* GDK_bracketleft */
983
GDK_backslash, /* GDK_backslash */
984
GDK_equal, /* GDK_bracketright */
985
GDK_asciicircum, /* GDK_asciicircum */
986
GDK_quotedbl, /* GDK_underscore */
987
GDK_grave, /* GDK_grave */
1006
GDK_semicolon, /* GDK_s */
1009
GDK_period, /* GDK_v */
1010
GDK_comma, /* GDK_w */
1013
GDK_slash, /* GDK_z */
1014
GDK_underscore, /* GDK_braceleft */
1015
GDK_bar, /* GDK_bar */
1016
GDK_plus, /* GDK_braceright */
1017
GDK_asciitilde, /* GDK_asciitilde */
1020
if (code < GDK_exclam || code > GDK_asciitilde)
1022
return table[code - GDK_exclam];
1025
static const guint keymap[][2] = {
1026
{ GDK_1, GDK_exclam }, /* 10 */
1027
{ GDK_2, GDK_at }, /* 11 */
1028
{ GDK_3, GDK_numbersign }, /* 12 */
1029
{ GDK_4, GDK_dollar }, /* 13 */
1030
{ GDK_5, GDK_percent }, /* 14 */
1031
{ GDK_6, GDK_asciicircum }, /* 15 */
1032
{ GDK_7, GDK_ampersand }, /* 16 */
1033
{ GDK_8, GDK_asterisk }, /* 17 */
1034
{ GDK_9, GDK_parenleft }, /* 18 */
1035
{ GDK_0, GDK_parenright }, /* 19 */
1036
{ GDK_minus, GDK_underscore }, /* 20 */
1037
{ GDK_equal, GDK_plus }, /* 21 */
1038
{ GDK_BackSpace, GDK_BackSpace }, /* 22 */
1039
{ GDK_Tab, GDK_Tab }, /* 23 */
1040
{ GDK_q, GDK_Q }, /* 24 */
1041
{ GDK_w, GDK_W }, /* 25 */
1042
{ GDK_e, GDK_E }, /* 26 */
1043
{ GDK_r, GDK_R }, /* 27 */
1044
{ GDK_t, GDK_T }, /* 28 */
1045
{ GDK_y, GDK_Y }, /* 29 */
1046
{ GDK_u, GDK_U }, /* 30 */
1047
{ GDK_i, GDK_I }, /* 31 */
1048
{ GDK_o, GDK_O }, /* 32 */
1049
{ GDK_p, GDK_P }, /* 33 */
1050
{ GDK_bracketleft, GDK_braceleft }, /* 34 */
1051
{ GDK_bracketright, GDK_braceright }, /* 35 */
1052
{ GDK_Return, GDK_Return }, /* 36 */
1053
{ GDK_Control_L, GDK_Control_L }, /* 37 */
1054
{ GDK_a, GDK_A }, /* 38 */
1055
{ GDK_s, GDK_S }, /* 39 */
1056
{ GDK_d, GDK_D }, /* 40 */
1057
{ GDK_f, GDK_F }, /* 41 */
1058
{ GDK_g, GDK_G }, /* 42 */
1059
{ GDK_h, GDK_H }, /* 43 */
1060
{ GDK_j, GDK_J }, /* 44 */
1061
{ GDK_k, GDK_K }, /* 45 */
1062
{ GDK_l, GDK_L }, /* 46 */
1063
{ GDK_semicolon, GDK_colon }, /* 47 */
1064
{ GDK_apostrophe, GDK_quotedbl }, /* 48 */
1065
{ GDK_grave, GDK_asciitilde }, /* 49 */
1066
{ GDK_Shift_L, GDK_Shift_L }, /* 50 */
1067
{ GDK_backslash, GDK_bar }, /* 51 */
1068
{ GDK_z, GDK_Z }, /* 52 */
1069
{ GDK_x, GDK_X }, /* 53 */
1070
{ GDK_c, GDK_C }, /* 54 */
1071
{ GDK_v, GDK_V }, /* 55 */
1072
{ GDK_b, GDK_B }, /* 56 */
1073
{ GDK_n, GDK_N }, /* 57 */
1074
{ GDK_m, GDK_M }, /* 58 */
1075
{ GDK_comma, GDK_less }, /* 59 */
1076
{ GDK_period, GDK_greater }, /* 60 */
1077
{ GDK_slash, GDK_question }, /* 61 */
1080
/* 한글 입력기는 각 키의 위치에 따라서 입력되는 자모가 결정되어 있다.
1081
* 그래서 키보드를 드보락이라든가, 유럽언어로 바꾸게 되면 각 키가 생성하는
1082
* 라틴문자가 qwerty와 달라지게 된다. 그 상태에서 keyval을 그대로 사용하면
1083
* 키의 영문자가 그 위치에 대한 정보를 가지지 못한 상태가 되므로 libhangul의
1084
* 조합 함수를 사용할 수 없게 된다.
1085
* 그래서 GDK의 hardware_keycode값에서 keyval로 변환하는 내장 테이블을 사용하여
1086
* 어떤 경우에든 US qwerty 자판인 것과 같은 변환을 해줌으로써 사용자가 설정한
1087
* 자판 정보에 관계없이, 한글 입력이 제대로 되도록 한다.
1088
* 이렇게 고치게 되면, capslock 처리라던가, dvorak을 위한 처리가 따로 필요 없다.
1089
* 단 키보드 하드웨어가 달라서 hardware_keycode(scancode)가 다른 값이 나오는
1090
* 키보드의 경우에는 한글 입력에 문제가 발생하게 될 가능성이 있다.
1091
* 그런 경우에는 예전과 같은 방식으로 동작하도록 system_keymap 옵션을
1095
im_hangul_get_keyval(GtkIMContextHangul *hcontext,
1100
/* hangul jamo keysym */
1101
if (keyval >= 0x01001100 && keyval <= 0x010011ff)
1102
return keyval & 0x0000ffff;
1104
if (pref_use_system_keymap) {
1105
/* treat for dvorak */
1106
if (pref_use_dvorak)
1107
keyval = im_hangul_dvorak_to_qwerty (keyval);
1109
if (keyval >= GDK_exclam && keyval <= GDK_asciitilde) {
1110
/* treat capslock, as capslock is not on */
1111
if (state & GDK_LOCK_MASK) {
1112
if (state & GDK_SHIFT_MASK) {
1113
if (keyval >= GDK_a && keyval <= GDK_z)
1114
keyval -= (GDK_a - GDK_A);
1116
if (keyval >= GDK_A && keyval <= GDK_Z)
1117
keyval += (GDK_a - GDK_A);
1122
/* keycode가 10에서 61 범위에 있으면 내장 keymap을 이용해서 변환한다. */
1123
if (keycode >= 10 && keycode <= 61) {
1124
if (state & GDK_SHIFT_MASK) {
1125
keyval = keymap[keycode - 10][1];
1127
keyval = keymap[keycode - 10][0];
1136
im_hangul_candidate_commit(GtkIMContextHangul *ic,
1137
const char* match_key,
1143
key = hanja_get_key(hanja);
1144
value = hanja_get_value(hanja);
1145
if (value != NULL) {
1146
ucschar* candidate_str = (gunichar*)ic->candidate_string->data;
1147
int candidate_str_len = ic->candidate_string->len;
1148
int len_to_delete = g_utf8_strlen(key, -1);
1150
// 먼저 hangul_ic의 preedit string을 제거한다.
1151
if (!hangul_ic_is_empty(ic->hic)) {
1152
const ucschar* preedit;
1154
preedit = hangul_ic_get_preedit_string(ic->hic);
1155
preedit_len = ucschar_strlen(preedit);
1157
// 여기서 preedit가 자모 스트링이라면 preedit_len을 바로 빼면
1158
// 안되고, NFC normalize 한 스트링으로 해야 하는데
1159
// 편의상 hangul_ic는 한번에 한 음절만 가지고 있다고 보고
1162
// candidate_str에는 preedit 조차도 자모 스트링으로 들어 있을 수
1163
// 있으므로 preedit_len을 뺀다.
1164
candidate_str_len -= preedit_len;
1165
hangul_ic_reset(ic->hic);
1166
im_hangul_ic_set_preedit(ic, NULL);
1169
// candidate string은 자모스트링일 수도 있으므로
1171
if (len_to_delete > 0) {
1173
ucschar* end = candidate_str + candidate_str_len;
1174
const ucschar* p = end;
1177
while (len_to_delete > 0) {
1178
p = hangul_syllable_iterator_prev(p, candidate_str);
1182
gtk_im_context_delete_surrounding(GTK_IM_CONTEXT(ic), -len, len);
1185
g_signal_emit_by_name(ic, "commit", value);
1186
close_candidate_window(ic);
1191
im_hangul_cadidate_filter_keypress (GtkIMContextHangul *hcontext,
1194
const Hanja* hanja = NULL;
1196
switch (key->keyval)
1200
hanja = candidate_get_current(hcontext->candidate);
1205
candidate_prev_page(hcontext->candidate);
1210
candidate_next_page(hcontext->candidate);
1215
case GDK_KP_Subtract:
1216
candidate_prev(hcontext->candidate);
1223
candidate_next(hcontext->candidate);
1226
close_candidate_window(hcontext);
1229
hanja = candidate_get_nth(hcontext->candidate, 9);
1240
hanja = candidate_get_nth(hcontext->candidate, key->keyval - GDK_1);
1247
im_hangul_candidate_commit(hcontext, hcontext->candidate->key, hanja);
1253
im_hangul_ic_slave_filter_keypress (GtkIMContext *context, GdkEventKey *key)
1255
GtkIMContextHangul *hcontext;
1257
g_return_val_if_fail (context != NULL, FALSE);
1258
g_return_val_if_fail (key != NULL, FALSE);
1260
hcontext = GTK_IM_CONTEXT_HANGUL(context);
1261
return gtk_im_context_filter_keypress(hcontext->slave, key);
1264
/* use hangul composer */
1266
im_hangul_ic_filter_keypress (GtkIMContext *context, GdkEventKey *key)
1270
const ucschar* commit;
1271
const ucschar* preedit;
1272
GtkIMContextHangul *hcontext;
1274
g_return_val_if_fail (context != NULL, FALSE);
1275
g_return_val_if_fail (key != NULL, FALSE);
1277
hcontext = GTK_IM_CONTEXT_HANGUL(context);
1279
/* ignore key release */
1280
if (key->type == GDK_KEY_RELEASE)
1283
/* we silently ignore shift keys */
1284
if (key->keyval == GDK_Shift_L || key->keyval == GDK_Shift_R)
1287
/* candidate window mode */
1288
if (hcontext->candidate != NULL)
1289
return im_hangul_cadidate_filter_keypress (hcontext, key);
1291
/* on capslock, we use Hangul Jamo */
1292
if (pref_use_capslock) {
1293
if (key->state & GDK_LOCK_MASK)
1294
hangul_ic_set_output_mode(hcontext->hic, HANGUL_OUTPUT_JAMO);
1296
hangul_ic_set_output_mode(hcontext->hic, HANGUL_OUTPUT_SYLLABLE);
1299
/* handle direct mode */
1300
if (im_hangul_ic_get_toplevel_input_mode(hcontext) == INPUT_MODE_DIRECT)
1301
return im_hangul_handle_direct_mode (hcontext, key);
1303
/* handle Escape key: automaticaly change to direct mode */
1304
if (key->keyval == GDK_Escape)
1306
im_hangul_ic_reset(context);
1307
im_hangul_set_input_mode(hcontext, INPUT_MODE_DIRECT);
1312
if (im_hangul_is_modifier (key->state))
1314
im_hangul_ic_reset(context);
1319
if (im_hangul_is_candidate(key))
1321
popup_candidate_window (hcontext);
1325
/* trigger key: mode change to direct mode */
1326
if (im_hangul_is_trigger(key)) {
1327
im_hangul_ic_reset(context);
1328
im_hangul_set_input_mode(hcontext, INPUT_MODE_DIRECT);
1333
if (im_hangul_is_backspace(key)) {
1334
res = hangul_ic_backspace(hcontext->hic);
1336
preedit = hangul_ic_get_preedit_string(hcontext->hic);
1337
im_hangul_ic_set_preedit(hcontext, preedit);
1343
keyval = im_hangul_get_keyval(hcontext,
1344
key->hardware_keycode, key->keyval, key->state);
1345
res = hangul_ic_process(hcontext->hic, keyval);
1347
commit = hangul_ic_get_commit_string(hcontext->hic);
1348
if (commit[0] != 0) {
1349
char* str = g_ucs4_to_utf8(commit, -1, NULL, NULL, NULL);
1350
/* 몇몇 어플리케이션에서 입력기 관련 구현에 버그가 있어서
1351
* commit하기 전에 preedit string을 빈 스트링으로 만들지
1352
* 않으면 오작동하는 경우가 있다. 이 문제를 피하기 위해서
1353
* commit하기 전에 preedit string을 빈 스트링으로 만든다. */
1354
im_hangul_ic_set_preedit(hcontext, NULL);
1355
g_signal_emit_by_name (hcontext, "commit", str);
1359
preedit = hangul_ic_get_preedit_string(hcontext->hic);
1360
im_hangul_ic_set_preedit(hcontext, preedit);
1367
status_window_expose_event (GtkWidget *widget, GdkEventExpose *event)
1369
gdk_draw_rectangle (widget->window,
1370
widget->style->fg_gc[GTK_STATE_NORMAL],
1373
widget->allocation.width-1, widget->allocation.height-1);
1379
status_window_configure (GtkWidget *widget,
1380
GdkEventConfigure *event,
1384
GtkRequisition requisition;
1387
if (toplevel == NULL || toplevel->status == NULL)
1390
gdk_window_get_frame_extents (widget->window, &rect);
1391
gtk_widget_size_request (toplevel->status, &requisition);
1393
if (rect.y + rect.height + requisition.height < gdk_screen_height ())
1394
y = rect.y + rect.height;
1396
y = gdk_screen_height () - requisition.height;
1398
gtk_window_move (GTK_WINDOW(toplevel->status), rect.x, y);
1403
status_window_new(GtkWidget *parent)
1412
window = gtk_window_new (GTK_WINDOW_POPUP);
1414
gtk_container_set_border_width (GTK_CONTAINER(window), 1);
1415
/* gtk_window_set_decorated (GTK_WINDOW(window), FALSE); */
1416
gtk_widget_set_name (window, "imhangul_status");
1417
gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
1418
gtk_widget_set_app_paintable (window, TRUE);
1420
frame = gtk_frame_new (NULL);
1421
gtk_frame_set_shadow_type (GTK_FRAME(frame), GTK_SHADOW_OUT);
1422
gtk_widget_show (frame);
1423
gtk_container_add (GTK_CONTAINER(window), frame);
1425
/* hangul status window label */
1426
label = gtk_label_new (_("hangul"));
1427
gtk_container_add (GTK_CONTAINER(frame), label);
1428
gtk_widget_show (label);
1430
g_signal_connect (G_OBJECT(window), "expose-event",
1431
G_CALLBACK(status_window_expose_event), NULL);
1437
im_hangul_ic_show_status_window (GtkIMContextHangul *hcontext)
1439
g_return_if_fail (hcontext != NULL);
1441
if (pref_use_status_window && hcontext->toplevel != NULL) {
1442
if (hcontext->toplevel->status == NULL) {
1443
hcontext->toplevel->status =
1444
status_window_new(hcontext->toplevel->widget);
1445
status_window_configure (hcontext->toplevel->widget,
1447
hcontext->toplevel);
1449
gtk_widget_show (hcontext->toplevel->status);
1454
im_hangul_ic_hide_status_window (GtkIMContextHangul *hcontext)
1456
g_return_if_fail (hcontext != NULL);
1458
if (hcontext->toplevel != NULL && hcontext->toplevel->status != NULL) {
1459
gtk_widget_hide (hcontext->toplevel->status);
1464
get_toplevel_widget (GdkWindow *window)
1466
GtkWidget *gtk_toplevel;
1472
gdk_window_get_user_data (window, &ptr);
1473
memcpy(>k_toplevel, &ptr, sizeof(gtk_toplevel));
1474
if (gtk_toplevel != NULL)
1475
gtk_toplevel = gtk_widget_get_toplevel(GTK_WIDGET(gtk_toplevel));
1477
return gtk_toplevel;
1481
toplevel_destroy(Toplevel *toplevel)
1483
if (toplevel != NULL) {
1484
toplevel_delete(toplevel);
1485
toplevels = g_slist_remove(toplevels, toplevel);
1490
toplevel_new(GtkWidget *toplevel_widget)
1492
Toplevel *toplevel = NULL;
1494
toplevel = g_new(Toplevel, 1);
1495
toplevel->input_mode = INPUT_MODE_DIRECT;
1496
toplevel->widget = toplevel_widget;
1497
toplevel->status = NULL;
1498
toplevel->contexts = NULL;
1499
toplevel->destroy_handler_id =
1500
g_signal_connect_swapped (G_OBJECT(toplevel->widget), "destroy",
1501
G_CALLBACK(toplevel_destroy), toplevel);
1502
toplevel->configure_handler_id =
1503
g_signal_connect (G_OBJECT(toplevel->widget), "configure-event",
1504
G_CALLBACK(status_window_configure),
1507
g_object_set_data(G_OBJECT(toplevel_widget),
1508
"gtk-imhangul-toplevel-info", toplevel);
1513
toplevel_get(GdkWindow *window)
1515
Toplevel *toplevel = NULL;
1516
GtkWidget *toplevel_widget;
1518
toplevel_widget = get_toplevel_widget (window);
1519
if (toplevel_widget == NULL) {
1523
toplevel = g_object_get_data(G_OBJECT(toplevel_widget),
1524
"gtk-imhangul-toplevel-info");
1525
if (toplevel == NULL) {
1526
toplevel = toplevel_new(toplevel_widget);
1527
toplevels = g_slist_prepend(toplevels, toplevel);
1534
toplevel_remove_context(Toplevel *toplevel, GtkIMContextHangul *context)
1536
if (toplevel == NULL || context == NULL)
1539
toplevel->contexts = g_slist_remove(toplevel->contexts, context);
1543
toplevel_append_context(Toplevel *toplevel, GtkIMContextHangul *context)
1545
if (toplevel == NULL || context == NULL)
1548
toplevel->contexts = g_slist_prepend(toplevel->contexts, context);
1552
toplevel_delete(Toplevel *toplevel)
1554
if (toplevel != NULL) {
1555
if (toplevel->status != NULL) {
1556
gtk_widget_destroy(toplevel->status);
1558
if (toplevel->contexts != NULL) {
1559
GSList *item = toplevel->contexts;
1560
while (item != NULL) {
1561
GtkIMContextHangul *context = (GtkIMContextHangul *)(item->data);
1562
context->toplevel = NULL;
1563
item = g_slist_next(item);
1565
g_slist_free(toplevel->contexts);
1567
g_signal_handler_disconnect (toplevel->widget,
1568
toplevel->configure_handler_id);
1569
g_signal_handler_disconnect (toplevel->widget,
1570
toplevel->destroy_handler_id);
1571
g_object_set_data (G_OBJECT(toplevel->widget),
1572
"gtk-imhangul-toplevel-info", NULL);
1578
im_hangul_ic_get_toplevel_input_mode(GtkIMContextHangul *hcontext)
1580
if (hcontext->toplevel == NULL)
1581
return INPUT_MODE_DIRECT;
1582
return hcontext->toplevel->input_mode;
1586
im_hangul_ic_set_toplevel_input_mode(GtkIMContextHangul *hcontext, int mode)
1588
if (hcontext->toplevel != NULL)
1589
hcontext->toplevel->input_mode = mode;
1593
* candidate selection window
1596
im_hangul_get_candidate_string(GtkIMContextHangul *ic)
1601
gint cursor_index = 0;
1602
gunichar buf[20] = { 0, };
1605
n = G_N_ELEMENTS(buf);
1606
if (!hangul_ic_is_empty(ic->hic)) {
1607
const ucschar* preedit;
1610
preedit = hangul_ic_get_preedit_string(ic->hic);
1611
preedit_len = ucschar_strlen(preedit);
1614
for (i = 0; i < preedit_len; i++) {
1615
buf[n + i] = preedit[i];
1619
res = gtk_im_context_get_surrounding(GTK_IM_CONTEXT(ic),
1620
&text, &cursor_index);
1621
if (res && text != NULL) {
1624
p = g_utf8_find_prev_char(text, text + cursor_index);
1625
while (n > 0 && p != NULL) {
1629
buf[n - 1] = g_utf8_get_char(p);
1631
p = g_utf8_find_prev_char(text, p);
1638
if (n < G_N_ELEMENTS(buf)) {
1640
int len = G_N_ELEMENTS(buf) - n;
1641
if (ic->candidate_string == NULL) {
1642
ic->candidate_string = g_array_sized_new(FALSE, FALSE,
1643
sizeof(gunichar), len);
1644
} else if (ic->candidate_string->len > 0) {
1645
g_array_set_size(ic->candidate_string, 0);
1648
g_array_insert_vals(ic->candidate_string, 0, buf + n, len);
1649
utf8 = g_ucs4_to_utf8((const gunichar*)ic->candidate_string->data,
1650
len, NULL, NULL, NULL);
1651
str = g_utf8_normalize(utf8, -1, G_NORMALIZE_DEFAULT_COMPOSE);
1659
popup_candidate_window (GtkIMContextHangul *hcontext)
1664
if (hcontext->candidate != NULL)
1666
close_candidate_window(hcontext);
1669
if (hanja_table == NULL)
1670
hanja_table = hanja_table_load(NULL);
1672
key = im_hangul_get_candidate_string(hcontext);
1673
list = hanja_table_match_suffix(hanja_table, key);
1675
hcontext->candidate = candidate_new (key,
1678
hcontext->client_window,
1686
close_candidate_window (GtkIMContextHangul *hic)
1688
if (hic->candidate_string != NULL && hic->candidate_string->len > 0)
1689
g_array_set_size(hic->candidate_string, 0);
1690
candidate_delete(hic->candidate);
1691
hic->candidate = NULL;
1695
im_hangul_key_snooper(GtkWidget *widget, GdkEventKey *event, gpointer data)
1697
if (current_focused_ic != NULL) {
1698
/* Some keys like return, tab, ':' is usually used for auto completion or
1699
* commiting some changes. Some application programmers make the program
1700
* catch the key before the im module getting the key and check that it is
1701
* return or tab or so. Then the program get the string from the text entry
1702
* or textview, so there is no chance for im module to commit the current
1703
* string. So in this case, we catch it first and process the filter
1704
* function of the input context. Then mostly imhangul will work fine,
1706
return im_hangul_ic_filter_keypress(current_focused_ic, event);
1713
im_hangul_init(void)
1715
im_hangul_config_parser();
1717
/* install gtk key snooper
1718
* this is work around code for the problem:
1719
* http://bugzilla.gnome.org/show_bug.cgi?id=62948
1720
* I finally decided to install key snooper and catch the keys before the
1721
* widget getting it. */
1722
snooper_handler_id = gtk_key_snooper_install(im_hangul_key_snooper, NULL);
1726
im_hangul_finalize (void)
1730
/* remove gtk key snooper */
1731
if (snooper_handler_id > 0) {
1732
gtk_key_snooper_remove(snooper_handler_id);
1733
snooper_handler_id = 0;
1736
/* remove toplevel info */
1737
for (item = toplevels; item != NULL; item = g_slist_next(item)) {
1738
toplevel_delete((Toplevel*)item->data);
1740
g_slist_free(toplevels);
1744
/* candidate window */
1754
candidate_on_row_activated(GtkWidget *widget,
1756
GtkTreeViewColumn *column,
1757
Candidate *candidate)
1763
GtkIMContextHangul *hcontext = candidate->hangul_context;
1765
indices = gtk_tree_path_get_indices(path);
1766
candidate->current = candidate->first + indices[0];
1767
hanja = candidate_get_current(candidate);
1768
im_hangul_candidate_commit(hcontext, candidate->key, hanja);
1773
candidate_on_cursor_changed(GtkWidget *widget,
1774
Candidate *candidate)
1778
gtk_tree_view_get_cursor(GTK_TREE_VIEW(widget), &path, NULL);
1782
indices = gtk_tree_path_get_indices(path);
1783
candidate->current = candidate->first + indices[0];
1784
gtk_tree_path_free(path);
1789
candidate_on_scroll(GtkWidget *widget,
1790
GdkEventScroll *event,
1793
Candidate *candidate;
1798
candidate = (Candidate*)data;
1799
switch (event->direction) {
1801
candidate_prev_page(candidate);
1803
case GDK_SCROLL_DOWN:
1804
candidate_next_page(candidate);
1813
candidate_on_key_press(GtkWidget *widget,
1817
Candidate *candidate;
1818
const Hanja* hanja = NULL;
1823
candidate = (Candidate*)data;
1824
switch (event->keyval) {
1827
hanja = candidate_get_current(candidate);
1832
candidate_prev_page(candidate);
1837
candidate_next_page(candidate);
1842
case GDK_KP_Subtract:
1843
candidate_prev(candidate);
1850
candidate_next(candidate);
1853
close_candidate_window(candidate->hangul_context);
1856
hanja = candidate_get_nth(candidate, 9);
1867
hanja = candidate_get_nth(candidate, event->keyval - GDK_1);
1874
im_hangul_candidate_commit(candidate->hangul_context,
1875
candidate->key, hanja);
1880
candidate_on_expose (GtkWidget *widget,
1881
GdkEventExpose *event,
1885
GtkAllocation alloc;
1887
style = gtk_widget_get_style(widget);
1888
alloc = GTK_WIDGET(widget)->allocation;
1889
gdk_draw_rectangle(widget->window, style->black_gc,
1891
0, 0, alloc.width - 1, alloc.height - 1);
1895
candidate_update_cursor(Candidate *candidate)
1899
if (candidate->treeview == NULL)
1902
path = gtk_tree_path_new_from_indices(candidate->current - candidate->first, -1);
1903
gtk_tree_view_set_cursor(GTK_TREE_VIEW(candidate->treeview),
1905
gtk_tree_path_free(path);
1909
candidate_set_window_position (Candidate *candidate)
1911
gint width = 0, height = 0;
1912
gint absx = 0, absy = 0;
1913
gint root_w, root_h, cand_w, cand_h;
1914
GtkRequisition requisition;
1916
if (candidate->parent == NULL)
1919
gdk_window_get_origin (GDK_WINDOW(candidate->parent), &absx, &absy);
1920
gdk_drawable_get_size (GDK_DRAWABLE(candidate->parent), &width, &height);
1922
root_w = gdk_screen_width();
1923
root_h = gdk_screen_height();
1925
gtk_widget_size_request(GTK_WIDGET(candidate->window), &requisition);
1926
cand_w = requisition.width;
1927
cand_h = requisition.height;
1929
absx += candidate->cursor.x;
1930
absy += (candidate->cursor.height < 0)?
1931
height : candidate->cursor.y + candidate->cursor.height;
1933
if (absy + cand_h > root_h)
1934
absy = root_h - cand_h;
1935
if (absx + cand_w > root_w)
1936
absx = root_w - cand_w;
1937
gtk_window_move(GTK_WINDOW(candidate->window), absx, absy);
1941
candidate_update_list(Candidate *candidate)
1946
gtk_list_store_clear(candidate->store);
1948
i < candidate->n_per_page && candidate->first + i < candidate->n;
1952
const char* comment;
1955
hanja = hanja_list_get_nth(candidate->list, candidate->first + i);
1956
value = hanja_get_value(hanja);
1957
comment = hanja_get_comment(hanja);
1959
gtk_list_store_append(candidate->store, &iter);
1960
gtk_list_store_set(candidate->store, &iter,
1961
COLUMN_INDEX, (i + 1) % 10,
1962
COLUMN_VALUE, value,
1963
COLUMN_COMMENT, comment,
1966
candidate_set_window_position (candidate);
1970
candidate_on_realize(GtkWidget* widget, gpointer data)
1972
gtk_widget_modify_fg (widget, GTK_STATE_ACTIVE,
1973
&widget->style->fg[GTK_STATE_SELECTED]);
1974
gtk_widget_modify_bg (widget, GTK_STATE_ACTIVE,
1975
&widget->style->bg[GTK_STATE_SELECTED]);
1976
gtk_widget_modify_text(widget, GTK_STATE_ACTIVE,
1977
&widget->style->text[GTK_STATE_SELECTED]);
1978
gtk_widget_modify_base(widget, GTK_STATE_ACTIVE,
1979
&widget->style->base[GTK_STATE_SELECTED]);
1983
candidate_create_window(Candidate *candidate)
1986
GtkWidget *treeview;
1987
GtkTreeViewColumn *column;
1988
GtkCellRenderer *renderer;
1990
candidate->window = gtk_window_new(GTK_WINDOW_POPUP);
1992
candidate_update_list(candidate);
1994
frame = gtk_frame_new(candidate->key);
1995
gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1996
gtk_container_add(GTK_CONTAINER(candidate->window), frame);
1998
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(candidate->store));
1999
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
2000
gtk_widget_set_name(GTK_WIDGET(treeview), "imhangul_candidate");
2001
gtk_container_add(GTK_CONTAINER(frame), treeview);
2002
candidate->treeview = treeview;
2003
g_object_unref(candidate->store);
2006
renderer = gtk_cell_renderer_text_new();
2007
column = gtk_tree_view_column_new_with_attributes("No",
2009
"text", COLUMN_INDEX,
2011
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
2013
/* character column */
2014
renderer = gtk_cell_renderer_text_new();
2015
g_object_set(renderer, "scale", 2.0, NULL);
2016
column = gtk_tree_view_column_new_with_attributes("Character",
2018
"text", COLUMN_VALUE,
2020
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
2022
/* comment column */
2023
renderer = gtk_cell_renderer_text_new();
2024
column = gtk_tree_view_column_new_with_attributes("Comment",
2026
"text", COLUMN_COMMENT,
2028
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
2030
candidate_update_cursor(candidate);
2032
/* 테마에 따라서 active 색상이 normal 색상과 같은 것들이 있다.
2033
* 그런 경우에 active 상태의 row가 표시가 안되므로
2034
* focus를 받지 못하는 candidat window에서는 구분이 안된다.
2035
* 이를 피하기 위해서 widget의 스타일을 수정해서
2036
* activate와 select를 같은 색으로 그려주도록 한다. */
2037
g_signal_connect_after(G_OBJECT(treeview), "realize",
2038
G_CALLBACK(candidate_on_realize), NULL);
2039
g_signal_connect(G_OBJECT(treeview), "row-activated",
2040
G_CALLBACK(candidate_on_row_activated), candidate);
2041
g_signal_connect(G_OBJECT(treeview), "cursor-changed",
2042
G_CALLBACK(candidate_on_cursor_changed), candidate);
2043
g_signal_connect(G_OBJECT(candidate->window), "scroll-event",
2044
G_CALLBACK(candidate_on_scroll), candidate);
2045
g_signal_connect(G_OBJECT(candidate->window), "key-press-event",
2046
G_CALLBACK(candidate_on_key_press), candidate);
2047
g_signal_connect_after(G_OBJECT(candidate->window), "expose-event",
2048
G_CALLBACK(candidate_on_expose), candidate);
2049
g_signal_connect_swapped(G_OBJECT(candidate->window), "realize",
2050
G_CALLBACK(candidate_set_window_position),
2053
gtk_widget_show_all(candidate->window);
2054
gtk_grab_add(candidate->window);
2058
candidate_new(char *key,
2063
GtkIMContextHangul *hcontext)
2065
Candidate *candidate;
2067
candidate = (Candidate*)g_malloc(sizeof(Candidate));
2068
candidate->key = g_strdup(key);
2069
candidate->first = 0;
2070
candidate->current = 0;
2071
candidate->n_per_page = n_per_page;
2072
candidate->list = list;
2073
candidate->n = hanja_list_get_size(list);
2074
candidate->parent = parent;
2075
candidate->cursor = *area;
2076
candidate->store = NULL;
2077
candidate->treeview = NULL;
2078
candidate->hangul_context = hcontext;
2080
if (n_per_page == 0)
2081
candidate->n_per_page = candidate->n;
2083
candidate->store = gtk_list_store_new(NO_OF_COLUMNS,
2084
G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
2085
candidate_create_window(candidate);
2091
candidate_prev(Candidate *candidate)
2093
if (candidate == NULL)
2096
if (candidate->current > 0)
2097
candidate->current--;
2099
if (candidate->current < candidate->first)
2101
candidate->first -= candidate->n_per_page;
2102
candidate_update_list(candidate);
2104
candidate_update_cursor(candidate);
2108
candidate_next(Candidate *candidate)
2110
if (candidate == NULL)
2113
if (candidate->current < candidate->n - 1)
2114
candidate->current++;
2116
if (candidate->current >= candidate->first + candidate->n_per_page)
2118
candidate->first += candidate->n_per_page;
2119
candidate_update_list(candidate);
2121
candidate_update_cursor(candidate);
2125
candidate_prev_page(Candidate *candidate)
2127
if (candidate == NULL)
2130
if (candidate->first - candidate->n_per_page >= 0)
2132
candidate->current -= candidate->n_per_page;
2133
if (candidate->current < 0)
2134
candidate->current = 0;
2135
candidate->first -= candidate->n_per_page;
2136
candidate_update_list(candidate);
2138
candidate_update_cursor(candidate);
2142
candidate_next_page(Candidate *candidate)
2144
if (candidate == NULL)
2147
if (candidate->first + candidate->n_per_page < candidate->n)
2149
candidate->current += candidate->n_per_page;
2150
if (candidate->current > candidate->n - 1)
2151
candidate->current = candidate->n - 1;
2152
candidate->first += candidate->n_per_page;
2153
candidate_update_list(candidate);
2155
candidate_update_cursor(candidate);
2159
candidate_get_current(Candidate *candidate)
2161
if (candidate == NULL)
2164
return hanja_list_get_nth(candidate->list, candidate->current);
2168
candidate_get_nth(Candidate *candidate, int index_)
2170
if (candidate == NULL)
2173
index_ += candidate->first;
2174
if (index_ < 0 || index_ >= candidate->n)
2177
return hanja_list_get_nth(candidate->list, index_);
2181
candidate_delete(Candidate *candidate)
2183
if (candidate == NULL)
2186
gtk_grab_remove(candidate->window);
2187
gtk_widget_destroy(candidate->window);
2188
hanja_list_delete(candidate->list);
2189
g_free(candidate->key);
2194
ucschar_strlen(const ucschar* s)
2196
const ucschar* p = s;
2202
/* vim: set cindent sw=4 sts=4 ts=8 : */