~ubuntu-branches/ubuntu/trusty/imhangul/trusty

« back to all changes in this revision

Viewing changes to .pc/default_style.patch/gtkimcontexthangul.c

  • Committer: Bazaar Package Importer
  • Author(s): Changwoo Ryu
  • Date: 2011-01-09 03:09:32 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20110109030932-kmtf6xjpjriaum0o
Tags: 0.9.16-1
* New upstream release
* Fix debian/watch for the new KLDP.net site
* Standards-Version: 3.9.1
* Use source format 3.0 (quilt)
* Switch to debhelper7 from CDBS
* im-config support

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ImHangul - Gtk+ 2.0 Input Method Module for Hangul
 
2
 * Copyright (C) 2002-2008 Choe Hwanjin
 
3
 *
 
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.
 
8
 *
 
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.
 
13
 *
 
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.
 
18
 */
 
19
 
 
20
#ifdef HAVE_CONFIG_H
 
21
#include <config.h>
 
22
#endif
 
23
 
 
24
#include <stdio.h>
 
25
#include <string.h>
 
26
#include <gdk/gdkkeysyms.h>
 
27
#include <gtk/gtk.h>
 
28
 
 
29
#include <hangul.h>
 
30
 
 
31
#include "gettext.h"
 
32
#include "gtkimcontexthangul.h"
 
33
 
 
34
enum {
 
35
  INPUT_MODE_DIRECT,
 
36
  INPUT_MODE_HANGUL,
 
37
  INPUT_MODE_HANJA
 
38
} IMHangulInputMode;
 
39
 
 
40
enum {
 
41
  INPUT_MODE_INFO_NONE,
 
42
  INPUT_MODE_INFO_ENGLISH,
 
43
  INPUT_MODE_INFO_HANGUL
 
44
} IMHangulInputModeInfo;
 
45
 
 
46
typedef struct _CandidateItem CandidateItem;
 
47
typedef struct _StatusWindow  StatusWindow;
 
48
 
 
49
struct _Toplevel
 
50
{
 
51
  int input_mode;
 
52
  GtkWidget *widget;
 
53
  GtkWidget *status;
 
54
  GSList *contexts;
 
55
  guint destroy_handler_id;
 
56
  guint configure_handler_id;
 
57
};
 
58
 
 
59
/* Candidate window */
 
60
struct _Candidate {
 
61
  gchar *key;
 
62
  GtkIMContextHangul *hangul_context;
 
63
  GtkWidget *window;
 
64
  GdkWindow *parent;
 
65
  GdkRectangle cursor;
 
66
  GtkListStore *store;
 
67
  GtkWidget *treeview;
 
68
  HanjaList *list;
 
69
  int first;
 
70
  int n;
 
71
  int n_per_page;
 
72
  int current;
 
73
};
 
74
 
 
75
struct _CandidateItem {
 
76
    gunichar ch;
 
77
    gchar *comment;
 
78
};
 
79
 
 
80
static size_t ucschar_strlen(const ucschar* s);
 
81
 
 
82
static Candidate*  candidate_new             (char *key,
 
83
                                              int n_per_page,
 
84
                                              HanjaList *list,
 
85
                                              GdkWindow *parent,
 
86
                                              GdkRectangle *area,
 
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);
 
95
 
 
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);
 
99
 
 
100
static void     im_hangul_ic_reset           (GtkIMContext *context);
 
101
static gboolean im_hangul_ic_slave_filter_keypress (GtkIMContext *context,
 
102
                                              GdkEventKey  *key);
 
103
static gboolean im_hangul_ic_filter_keypress (GtkIMContext *context,
 
104
                                              GdkEventKey  *key);
 
105
 
 
106
static void     im_hangul_get_preedit_string (GtkIMContext  *ic,
 
107
                                              gchar         **str,
 
108
                                              PangoAttrList **attrs,
 
109
                                              gint          *cursor_pos);
 
110
 
 
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,
 
118
                                              GdkRectangle *area);
 
119
 
 
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);
 
125
 
 
126
/* commit functions */
 
127
static void     im_hangul_ic_commit_by_slave (GtkIMContext *context,
 
128
                                              gchar *str, gpointer data);
 
129
 
 
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);
 
152
 
 
153
static char*    im_hangul_get_candidate_string(GtkIMContextHangul *ic);
 
154
 
 
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,
 
159
                                                  int mode);
 
160
 
 
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);
 
169
 
 
170
static void popup_candidate_window  (GtkIMContextHangul *hcontext);
 
171
static void close_candidate_window  (GtkIMContextHangul *hic);
 
172
 
 
173
GType gtk_type_im_context_hangul = 0;
 
174
 
 
175
/* static variables for hangul immodule */
 
176
static GObjectClass *parent_class;
 
177
 
 
178
static GSList          *toplevels = NULL;
 
179
 
 
180
static guint            snooper_handler_id = 0;
 
181
static GtkIMContext    *current_focused_ic = NULL;
 
182
 
 
183
static HanjaTable*      hanja_table = NULL;
 
184
 
 
185
/* preferences */
 
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,
 
193
                                                  gint start,
 
194
                                                  gint end) =
 
195
                                                im_hangul_preedit_foreground;
 
196
static GdkColor         pref_fg = { 0, 0xeeee, 0, 0 };
 
197
static GdkColor         pref_bg = { 0, 0xFFFF, 0xFFFF, 0xFFFF };
 
198
 
 
199
/* scanner */
 
200
static const GScannerConfig im_hangul_scanner_config = {
 
201
    (
 
202
     " \t\r\n"
 
203
    )                    /* cset_skip_characters */,
 
204
    (
 
205
     G_CSET_a_2_z
 
206
     "_"
 
207
     G_CSET_A_2_Z
 
208
    )                    /* cset_identifier_first */,
 
209
    (
 
210
     G_CSET_a_2_z
 
211
     "_"
 
212
     G_CSET_A_2_Z
 
213
     G_CSET_DIGITS
 
214
     G_CSET_LATINS
 
215
     G_CSET_LATINC
 
216
    )                    /* cset_identifier_nth */,
 
217
    ( "#\n" )             /* cpair_comment_single */,
 
218
 
 
219
    FALSE                 /* case_sensitive */,
 
220
 
 
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 */,
 
231
    TRUE                  /* scan_hex */,
 
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 */,
 
242
};
 
243
 
 
244
enum {
 
245
    TOKEN_FALSE = G_TOKEN_LAST,
 
246
    TOKEN_TRUE,
 
247
    TOKEN_ENABLE_STATUS_WINDOW,
 
248
    TOKEN_ENABLE_PREEDIT,
 
249
    TOKEN_ENABLE_CAPSLOCK,
 
250
    TOKEN_ENABLE_DVORAK,
 
251
    TOKEN_ENABLE_SYSTEM_KEYMAP,
 
252
    TOKEN_PREEDIT_STYLE,
 
253
    TOKEN_PREEDIT_STYLE_FG,
 
254
    TOKEN_PREEDIT_STYLE_BG
 
255
};
 
256
 
 
257
static const struct {
 
258
    char *name;
 
259
    guint token;
 
260
} symbols[] = {
 
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 }
 
273
};
 
274
 
 
275
static void
 
276
set_preedit_style (const char *style)
 
277
{
 
278
    if (style == NULL) {
 
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;
 
294
    } else {
 
295
        im_hangul_preedit_attr = im_hangul_preedit_foreground;
 
296
    }
 
297
}
 
298
 
 
299
void im_hangul_config_parser(void)
 
300
{
 
301
    int i;
 
302
    int fd;
 
303
    FILE *file;
 
304
    GScanner *scanner;
 
305
    const gchar *env_conf_file;
 
306
    gchar *conf_file = NULL; 
 
307
    guint type;
 
308
    GTokenValue value;
 
309
    char *str;
 
310
 
 
311
    env_conf_file = g_getenv("IM_HANGUL_CONF_FILE");
 
312
    if (env_conf_file == NULL) {
 
313
        const gchar *homedir = g_get_home_dir();
 
314
        if (homedir == NULL)
 
315
            return;
 
316
 
 
317
        conf_file = g_build_filename(homedir, ".imhangul.conf", NULL);
 
318
    } else {
 
319
        conf_file = g_strdup(env_conf_file);
 
320
    }
 
321
 
 
322
    file = fopen(conf_file, "r");
 
323
    g_free(conf_file);
 
324
    if (file == NULL)
 
325
        return;
 
326
 
 
327
    fd = fileno(file);
 
328
    scanner = g_scanner_new(&im_hangul_scanner_config);
 
329
    g_scanner_input_file(scanner, fd);
 
330
 
 
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));
 
334
    }
 
335
 
 
336
    do {
 
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;
 
344
                } else {
 
345
                    pref_use_preedit_string = FALSE;
 
346
                }
 
347
            }
 
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;
 
354
                } else {
 
355
                    pref_use_status_window = FALSE;
 
356
                }
 
357
            }
 
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;
 
364
                } else {
 
365
                    pref_use_capslock = FALSE;
 
366
                }
 
367
            }
 
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;
 
374
                } else {
 
375
                    pref_use_dvorak = FALSE;
 
376
                }
 
377
            }
 
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;
 
384
                } else {
 
385
                    pref_use_system_keymap = FALSE;
 
386
                }
 
387
            }
 
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);
 
396
                }
 
397
            }
 
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);
 
406
                }
 
407
            }
 
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);
 
416
                }
 
417
            }
 
418
        } else {
 
419
            type = g_scanner_get_next_token(scanner);
 
420
            if (type == G_TOKEN_EQUAL_SIGN) {
 
421
                type = g_scanner_get_next_token(scanner);
 
422
            }
 
423
        }
 
424
    } while (!g_scanner_eof(scanner));
 
425
 
 
426
    g_scanner_destroy(scanner);
 
427
 
 
428
    fclose(file);
 
429
}
 
430
 
 
431
void
 
432
gtk_im_context_hangul_register_type (GTypeModule *type_module)
 
433
{
 
434
  static const GTypeInfo im_context_hangul_info = {
 
435
    sizeof(GtkIMContextHangulClass),
 
436
    (GBaseInitFunc) NULL,
 
437
    (GBaseFinalizeFunc) NULL,
 
438
    (GClassInitFunc) im_hangul_class_init,
 
439
    NULL,
 
440
    NULL,
 
441
    sizeof(GtkIMContextHangul),
 
442
    0,
 
443
    (GInstanceInitFunc) im_hangul_ic_init,
 
444
  };
 
445
 
 
446
  gtk_type_im_context_hangul =
 
447
      g_type_module_register_type (type_module,
 
448
                                   GTK_TYPE_IM_CONTEXT,
 
449
                                   "GtkIMContextHangul",
 
450
                                   &im_context_hangul_info, 0);
 
451
}
 
452
 
 
453
static void 
 
454
im_hangul_class_init (GtkIMContextHangulClass *klass)
 
455
{
 
456
  GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS(klass);
 
457
  GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
 
458
 
 
459
  parent_class = g_type_class_peek_parent (klass);
 
460
 
 
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;
 
469
 
 
470
  gobject_class->finalize = im_hangul_ic_finalize;
 
471
}
 
472
 
 
473
static void 
 
474
im_hangul_ic_init (GtkIMContextHangul *hcontext)
 
475
{
 
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);
 
479
 
 
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;
 
486
 
 
487
  hcontext->hic = hangul_ic_new("2");
 
488
  hcontext->preedit = g_string_new(NULL);
 
489
 
 
490
  hcontext->candidate = NULL;
 
491
  hcontext->candidate_string = NULL;
 
492
 
 
493
  /* options */
 
494
  hcontext->use_preedit = TRUE;
 
495
}
 
496
 
 
497
static void
 
498
im_hangul_ic_finalize (GObject *object)
 
499
{
 
500
  GtkIMContextHangul *hic = GTK_IM_CONTEXT_HANGUL(object);
 
501
 
 
502
  if (hic->toplevel != NULL)
 
503
    toplevel_remove_context(hic->toplevel, hic);
 
504
 
 
505
  hangul_ic_delete(hic->hic);
 
506
  g_string_free(hic->preedit, TRUE);
 
507
 
 
508
  gtk_im_context_reset(hic->slave);
 
509
  g_signal_handlers_disconnect_by_func(hic->slave,
 
510
                                       im_hangul_ic_commit_by_slave,
 
511
                                       object);
 
512
  g_object_unref(G_OBJECT(hic->slave));
 
513
  hic->slave = NULL;
 
514
 
 
515
  G_OBJECT_CLASS(parent_class)->finalize (object);
 
516
  if ((GObject*)current_focused_ic == object)
 
517
    current_focused_ic = NULL;
 
518
}
 
519
 
 
520
static void
 
521
im_hangul_ic_set_client_window (GtkIMContext *context,
 
522
                             GdkWindow *client_window)
 
523
{
 
524
  GtkIMContextHangul *hcontext;
 
525
 
 
526
  g_return_if_fail (context != NULL);
 
527
  g_return_if_fail (GTK_IS_IM_CONTEXT_HANGUL (context));
 
528
 
 
529
  hcontext = GTK_IM_CONTEXT_HANGUL(context);
 
530
 
 
531
  if (hcontext->client_window == client_window)
 
532
    return;
 
533
 
 
534
  if (hcontext->toplevel != NULL)
 
535
    toplevel_remove_context(hcontext->toplevel, hcontext);
 
536
 
 
537
  if (client_window == NULL) {
 
538
    hcontext->client_window = NULL;
 
539
    hcontext->toplevel = NULL;
 
540
    return;
 
541
  }
 
542
 
 
543
  hcontext->client_window = client_window;
 
544
  hcontext->toplevel = toplevel_get (client_window);
 
545
  toplevel_append_context(hcontext->toplevel, hcontext);
 
546
}
 
547
 
 
548
GtkIMContext *
 
549
gtk_im_context_hangul_new (void)
 
550
{
 
551
  return GTK_IM_CONTEXT (g_object_new (GTK_TYPE_IM_CONTEXT_HANGUL, NULL));
 
552
}
 
553
 
 
554
void
 
555
gtk_im_context_hangul_select_keyboard(GtkIMContextHangul *hcontext,
 
556
                                      const char *keyboard)
 
557
{
 
558
    g_return_if_fail (hcontext);
 
559
 
 
560
    hangul_ic_select_keyboard(hcontext->hic, keyboard);
 
561
}
 
562
 
 
563
static void
 
564
im_hangul_set_input_mode_info_for_screen (GdkScreen *screen, int state)
 
565
{
 
566
  if (screen != NULL) {
 
567
    GdkWindow *root_window = gdk_screen_get_root_window(screen);
 
568
    long data = state;
 
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);
 
574
  }
 
575
}
 
576
 
 
577
static void
 
578
im_hangul_set_input_mode_info (GdkWindow *window, int state)
 
579
{
 
580
  if (window != NULL) {
 
581
    GdkScreen *screen = gdk_drawable_get_screen(window);
 
582
    im_hangul_set_input_mode_info_for_screen (screen, state);
 
583
  }
 
584
}
 
585
 
 
586
static void
 
587
im_hangul_set_input_mode(GtkIMContextHangul *hcontext, int mode)
 
588
{
 
589
  switch (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");
 
595
      break;
 
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");
 
601
      break;
 
602
  }
 
603
  im_hangul_ic_set_toplevel_input_mode(hcontext, mode);
 
604
}
 
605
 
 
606
static void
 
607
im_hangul_preedit_underline (GtkIMContextHangul *hic,
 
608
                             PangoAttrList **attrs, gint start, gint end)
 
609
{
 
610
    PangoAttribute *attr;
 
611
 
 
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);
 
617
}
 
618
 
 
619
static void
 
620
im_hangul_preedit_reverse (GtkIMContextHangul *hic,
 
621
                           PangoAttrList **attrs, gint start, gint end)
 
622
{
 
623
    static GdkColor default_base = { 0, 0xffff, 0xffff, 0xffff };
 
624
    static GdkColor default_text = { 0, 0, 0, 0 };
 
625
 
 
626
    PangoAttribute *attr;
 
627
    GtkWidget *widget = NULL;
 
628
    GdkColor *fg = &default_base;
 
629
    GdkColor *bg = &default_text;
 
630
 
 
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];
 
636
    }
 
637
 
 
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);
 
643
 
 
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);
 
648
}
 
649
 
 
650
static void
 
651
im_hangul_preedit_shade (GtkIMContextHangul *hic,
 
652
                           PangoAttrList **attrs, gint start, gint end)
 
653
{
 
654
    static const GdkColor default_text = { 0, 0, 0, 0 };
 
655
    static const GdkColor default_base = { 0, 0xffff * 90 / 100,
 
656
                                              0xffff * 90 / 100,
 
657
                                              0xffff * 90 / 100 };
 
658
 
 
659
    PangoAttribute *attr;
 
660
    GtkWidget *widget = NULL;
 
661
    GdkColor fg = default_text;
 
662
    GdkColor bg = default_base;
 
663
 
 
664
    gdk_window_get_user_data(hic->client_window, (gpointer)&widget);
 
665
    if (widget != NULL) {
 
666
        GtkStyle *style = gtk_widget_get_style(widget);
 
667
        if (style != NULL) {
 
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;
 
677
        }
 
678
    }
 
679
 
 
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);
 
685
 
 
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);
 
690
}
 
691
 
 
692
static void
 
693
im_hangul_preedit_foreground (GtkIMContextHangul *hic,
 
694
                              PangoAttrList **attrs, gint start, gint end)
 
695
{
 
696
  PangoAttribute *attr;
 
697
 
 
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);
 
703
}
 
704
 
 
705
static void
 
706
im_hangul_preedit_background (GtkIMContextHangul *hic,
 
707
                              PangoAttrList **attrs, gint start, gint end)
 
708
{
 
709
  PangoAttribute *attr;
 
710
 
 
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);
 
716
}
 
717
 
 
718
static void
 
719
im_hangul_preedit_color (GtkIMContextHangul *hic,
 
720
                         PangoAttrList **attrs, gint start, gint end)
 
721
{
 
722
  PangoAttribute *attr;
 
723
 
 
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);
 
729
 
 
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);
 
734
}
 
735
 
 
736
static void
 
737
im_hangul_preedit_normal (GtkIMContextHangul *hic,
 
738
                          PangoAttrList **attrs, gint start, gint end)
 
739
{
 
740
  /* we do nothing */
 
741
  *attrs = pango_attr_list_new ();
 
742
}
 
743
 
 
744
static void
 
745
im_hangul_get_preedit_string (GtkIMContext *context, gchar **str,
 
746
                              PangoAttrList **attrs,
 
747
                              gint *cursor_pos)
 
748
{
 
749
    int len;
 
750
    GtkIMContextHangul *ic;
 
751
 
 
752
    g_return_if_fail (context != NULL);
 
753
 
 
754
    ic = GTK_IM_CONTEXT_HANGUL(context);
 
755
    len = g_utf8_strlen(ic->preedit->str, -1);
 
756
 
 
757
    if (attrs)
 
758
        im_hangul_preedit_attr(ic, attrs, 0, ic->preedit->len);
 
759
 
 
760
    if (cursor_pos)
 
761
        *cursor_pos = len;
 
762
 
 
763
    if (str)
 
764
        *str = g_strdup(ic->preedit->str);
 
765
}
 
766
 
 
767
static void
 
768
im_hangul_ic_focus_in (GtkIMContext *context)
 
769
{
 
770
  int input_mode;
 
771
  GtkIMContextHangul *hcontext;
 
772
 
 
773
  g_return_if_fail (context != NULL);
 
774
 
 
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);
 
778
 
 
779
  current_focused_ic = context;
 
780
}
 
781
 
 
782
static void
 
783
im_hangul_ic_set_preedit(GtkIMContextHangul* hic, const ucschar* preedit)
 
784
{
 
785
    int i;
 
786
    char* old;
 
787
 
 
788
    old = g_strdup(hic->preedit->str);
 
789
 
 
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]);
 
794
        }
 
795
    }
 
796
 
 
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);
 
803
 
 
804
    g_free(old);
 
805
}
 
806
 
 
807
static inline void
 
808
im_hangul_ic_emit_preedit_changed (GtkIMContextHangul *hcontext)
 
809
{
 
810
  if (hcontext->use_preedit)
 
811
    g_signal_emit_by_name (hcontext, "preedit_changed");
 
812
}
 
813
 
 
814
static void
 
815
im_hangul_ic_focus_out (GtkIMContext *context)
 
816
{
 
817
  GtkIMContextHangul *hcontext;
 
818
 
 
819
  g_return_if_fail (context != NULL);
 
820
 
 
821
  im_hangul_ic_reset(context);
 
822
 
 
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;
 
828
}
 
829
 
 
830
static void
 
831
im_hangul_ic_set_use_preedit (GtkIMContext *context, gboolean use_preedit)
 
832
{
 
833
  GtkIMContextHangul *hcontext;
 
834
 
 
835
  g_return_if_fail (context != NULL);
 
836
 
 
837
  hcontext = GTK_IM_CONTEXT_HANGUL(context);
 
838
  hcontext->use_preedit = use_preedit;
 
839
}
 
840
 
 
841
static void
 
842
im_hangul_ic_cursor_location (GtkIMContext *context, GdkRectangle *area)
 
843
{
 
844
  GtkIMContextHangul *hcontext;
 
845
 
 
846
  g_return_if_fail (context != NULL);
 
847
 
 
848
  hcontext = GTK_IM_CONTEXT_HANGUL(context);
 
849
  hcontext->cursor = *area;
 
850
}
 
851
 
 
852
static inline gboolean
 
853
im_hangul_is_modifier (guint state)
 
854
{
 
855
  return ((state & GDK_CONTROL_MASK) || (state & GDK_MOD1_MASK));
 
856
}
 
857
 
 
858
static inline gboolean
 
859
im_hangul_is_trigger (GdkEventKey *key)
 
860
{
 
861
  return ( key->keyval == GDK_Hangul || 
 
862
           key->keyval == GDK_Alt_R ||
 
863
          (key->keyval == GDK_space && (key->state & GDK_SHIFT_MASK)));
 
864
}
 
865
 
 
866
static inline gboolean
 
867
im_hangul_is_candidate (GdkEventKey *key)
 
868
{
 
869
  return (key->keyval == GDK_Hangul_Hanja ||
 
870
          key->keyval == GDK_F9 ||
 
871
          key->keyval == GDK_Control_R);
 
872
}
 
873
 
 
874
static inline gboolean
 
875
im_hangul_is_backspace (GdkEventKey *key)
 
876
{
 
877
  return (key->keyval == GDK_BackSpace);
 
878
}
 
879
 
 
880
static void
 
881
im_hangul_ic_reset (GtkIMContext *context)
 
882
{
 
883
    const ucschar* preedit;
 
884
    const ucschar* flush;
 
885
    GtkIMContextHangul *hic = GTK_IM_CONTEXT_HANGUL (context);
 
886
 
 
887
    flush = hangul_ic_flush(hic->hic);
 
888
 
 
889
    preedit = hangul_ic_get_preedit_string(hic->hic);
 
890
    im_hangul_ic_set_preedit(hic, preedit);
 
891
 
 
892
    if (flush[0] != 0) {
 
893
        char* str = g_ucs4_to_utf8(flush, -1, NULL, NULL, NULL);
 
894
        g_signal_emit_by_name(hic, "commit", str);
 
895
        g_free(str);
 
896
    }
 
897
}
 
898
 
 
899
static gboolean
 
900
im_hangul_handle_direct_mode (GtkIMContextHangul *hcontext,
 
901
                              GdkEventKey *key)
 
902
{
 
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);
 
906
        return TRUE;
 
907
    }
 
908
    return FALSE;
 
909
}
 
910
 
 
911
static void
 
912
im_hangul_ic_commit_by_slave (GtkIMContext *context, gchar *str, gpointer data)
 
913
{
 
914
  g_signal_emit_by_name (GTK_IM_CONTEXT_HANGUL(data), "commit", str);
 
915
}
 
916
 
 
917
/* this is a very dangerous function:
 
918
 * safe only when GDKKEYSYMS's value is enumarated  */
 
919
static guint
 
920
im_hangul_dvorak_to_qwerty (guint code)
 
921
{
 
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 */
 
939
    GDK_0,                      /* GDK_0 */
 
940
    GDK_1,                      /* GDK_1 */
 
941
    GDK_2,                      /* GDK_2 */
 
942
    GDK_3,                      /* GDK_3 */
 
943
    GDK_4,                      /* GDK_4 */
 
944
    GDK_5,                      /* GDK_5 */
 
945
    GDK_6,                      /* GDK_6 */
 
946
    GDK_7,                      /* GDK_7 */
 
947
    GDK_8,                      /* GDK_8 */
 
948
    GDK_9,                      /* GDK_9 */
 
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 */
 
955
    GDK_at,                     /* GDK_at */
 
956
    GDK_A,                      /* GDK_A */
 
957
    GDK_N,                      /* GDK_B */
 
958
    GDK_I,                      /* GDK_C */
 
959
    GDK_H,                      /* GDK_D */
 
960
    GDK_D,                      /* GDK_E */
 
961
    GDK_Y,                      /* GDK_F */
 
962
    GDK_U,                      /* GDK_G */
 
963
    GDK_J,                      /* GDK_H */
 
964
    GDK_G,                      /* GDK_I */
 
965
    GDK_C,                      /* GDK_J */
 
966
    GDK_V,                      /* GDK_K */
 
967
    GDK_P,                      /* GDK_L */
 
968
    GDK_M,                      /* GDK_M */
 
969
    GDK_L,                      /* GDK_N */
 
970
    GDK_S,                      /* GDK_O */
 
971
    GDK_R,                      /* GDK_P */
 
972
    GDK_X,                      /* GDK_Q */
 
973
    GDK_O,                      /* GDK_R */
 
974
    GDK_colon,                  /* GDK_S */
 
975
    GDK_K,                      /* GDK_T */
 
976
    GDK_F,                      /* GDK_U */
 
977
    GDK_greater,                /* GDK_V */
 
978
    GDK_less,                   /* GDK_W */
 
979
    GDK_B,                      /* GDK_X */
 
980
    GDK_T,                      /* GDK_Y */
 
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 */
 
988
    GDK_a,                      /* GDK_a */
 
989
    GDK_n,                      /* GDK_b */
 
990
    GDK_i,                      /* GDK_c */
 
991
    GDK_h,                      /* GDK_d */
 
992
    GDK_d,                      /* GDK_e */
 
993
    GDK_y,                      /* GDK_f */
 
994
    GDK_u,                      /* GDK_g */
 
995
    GDK_j,                      /* GDK_h */
 
996
    GDK_g,                      /* GDK_i */
 
997
    GDK_c,                      /* GDK_j */
 
998
    GDK_v,                      /* GDK_k */
 
999
    GDK_p,                      /* GDK_l */
 
1000
    GDK_m,                      /* GDK_m */
 
1001
    GDK_l,                      /* GDK_n */
 
1002
    GDK_s,                      /* GDK_o */
 
1003
    GDK_r,                      /* GDK_p */
 
1004
    GDK_x,                      /* GDK_q */
 
1005
    GDK_o,                      /* GDK_r */
 
1006
    GDK_semicolon,              /* GDK_s */
 
1007
    GDK_k,                      /* GDK_t */
 
1008
    GDK_f,                      /* GDK_u */
 
1009
    GDK_period,                 /* GDK_v */
 
1010
    GDK_comma,                  /* GDK_w */
 
1011
    GDK_b,                      /* GDK_x */
 
1012
    GDK_t,                      /* GDK_y */
 
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 */
 
1018
  };
 
1019
 
 
1020
  if (code < GDK_exclam || code > GDK_asciitilde)
 
1021
    return code;
 
1022
  return table[code - GDK_exclam];
 
1023
}
 
1024
 
 
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 */
 
1078
};
 
1079
 
 
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 옵션을 
 
1092
 * 켜도록 한다.
 
1093
 */
 
1094
static gunichar
 
1095
im_hangul_get_keyval(GtkIMContextHangul *hcontext,
 
1096
                     guint             keycode,
 
1097
                     guint             keyval,
 
1098
                     guint             state)
 
1099
{
 
1100
    /* hangul jamo keysym */
 
1101
    if (keyval >= 0x01001100 && keyval <= 0x010011ff)
 
1102
        return keyval & 0x0000ffff;
 
1103
 
 
1104
    if (pref_use_system_keymap) {
 
1105
        /* treat for dvorak */
 
1106
        if (pref_use_dvorak)
 
1107
            keyval = im_hangul_dvorak_to_qwerty (keyval);
 
1108
 
 
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);
 
1115
                } else {
 
1116
                    if (keyval >= GDK_A && keyval <= GDK_Z)
 
1117
                        keyval += (GDK_a - GDK_A);
 
1118
                }
 
1119
            }
 
1120
        }
 
1121
    } else {
 
1122
        /* keycode가 10에서 61 범위에 있으면 내장 keymap을 이용해서 변환한다. */
 
1123
        if (keycode >= 10 && keycode <= 61) {
 
1124
            if (state & GDK_SHIFT_MASK) {
 
1125
                keyval = keymap[keycode - 10][1];
 
1126
            } else {
 
1127
                keyval = keymap[keycode - 10][0];
 
1128
            }
 
1129
        }
 
1130
    }
 
1131
 
 
1132
    return keyval;
 
1133
}
 
1134
 
 
1135
static void
 
1136
im_hangul_candidate_commit(GtkIMContextHangul *ic,
 
1137
                           const char* match_key,
 
1138
                           const Hanja* hanja)
 
1139
{
 
1140
    const char* key;
 
1141
    const char* value;
 
1142
 
 
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);
 
1149
 
 
1150
        // 먼저 hangul_ic의 preedit string을 제거한다.
 
1151
        if (!hangul_ic_is_empty(ic->hic)) {
 
1152
            const ucschar* preedit;
 
1153
            int preedit_len;
 
1154
            preedit = hangul_ic_get_preedit_string(ic->hic);
 
1155
            preedit_len = ucschar_strlen(preedit);
 
1156
 
 
1157
            // 여기서 preedit가 자모 스트링이라면 preedit_len을 바로 빼면 
 
1158
            // 안되고, NFC normalize 한 스트링으로 해야 하는데
 
1159
            // 편의상 hangul_ic는 한번에 한 음절만 가지고 있다고 보고
 
1160
            // 1만 빼서 계산 한다.
 
1161
            len_to_delete -= 1;
 
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);
 
1167
        }
 
1168
 
 
1169
        // candidate string은 자모스트링일 수도 있으므로 
 
1170
        // 주의한다.
 
1171
        if (len_to_delete > 0) {
 
1172
            int len = 0;
 
1173
            ucschar* end = candidate_str + candidate_str_len;
 
1174
            const ucschar* p = end;
 
1175
 
 
1176
            // 끝에서부터 한음절씩 빼본다.
 
1177
            while (len_to_delete > 0) {
 
1178
                p = hangul_syllable_iterator_prev(p, candidate_str);
 
1179
                len_to_delete--;
 
1180
            }
 
1181
            len = end - p;
 
1182
            gtk_im_context_delete_surrounding(GTK_IM_CONTEXT(ic), -len, len);
 
1183
        }
 
1184
 
 
1185
        g_signal_emit_by_name(ic, "commit", value);
 
1186
        close_candidate_window(ic);
 
1187
    }
 
1188
}
 
1189
 
 
1190
static gboolean
 
1191
im_hangul_cadidate_filter_keypress (GtkIMContextHangul *hcontext,
 
1192
                                    GdkEventKey *key)
 
1193
{
 
1194
  const Hanja* hanja = NULL;
 
1195
 
 
1196
  switch (key->keyval)
 
1197
    {
 
1198
      case GDK_Return:
 
1199
      case GDK_KP_Enter:
 
1200
        hanja = candidate_get_current(hcontext->candidate);
 
1201
        break;
 
1202
      case GDK_Left:
 
1203
      case GDK_h:
 
1204
      case GDK_Page_Up:
 
1205
        candidate_prev_page(hcontext->candidate);
 
1206
        break;
 
1207
      case GDK_Right:
 
1208
      case GDK_l:
 
1209
      case GDK_Page_Down:
 
1210
        candidate_next_page(hcontext->candidate);
 
1211
        break;
 
1212
      case GDK_Up:
 
1213
      case GDK_k:
 
1214
      case GDK_BackSpace:
 
1215
      case GDK_KP_Subtract:
 
1216
        candidate_prev(hcontext->candidate);
 
1217
        break;
 
1218
      case GDK_Down:
 
1219
      case GDK_j:
 
1220
      case GDK_space:
 
1221
      case GDK_KP_Add:
 
1222
      case GDK_KP_Tab:
 
1223
        candidate_next(hcontext->candidate);
 
1224
        break;
 
1225
      case GDK_Escape:
 
1226
        close_candidate_window(hcontext);
 
1227
        break;
 
1228
      case GDK_0:
 
1229
        hanja = candidate_get_nth(hcontext->candidate, 9);
 
1230
        break;
 
1231
      case GDK_1:
 
1232
      case GDK_2:
 
1233
      case GDK_3:
 
1234
      case GDK_4:
 
1235
      case GDK_5:
 
1236
      case GDK_6:
 
1237
      case GDK_7:
 
1238
      case GDK_8:
 
1239
      case GDK_9:
 
1240
        hanja = candidate_get_nth(hcontext->candidate, key->keyval - GDK_1);
 
1241
        break;
 
1242
      default:
 
1243
        break;
 
1244
    }
 
1245
 
 
1246
  if (hanja != NULL)
 
1247
      im_hangul_candidate_commit(hcontext, hcontext->candidate->key, hanja);
 
1248
 
 
1249
  return TRUE;
 
1250
}
 
1251
 
 
1252
static gboolean
 
1253
im_hangul_ic_slave_filter_keypress (GtkIMContext *context, GdkEventKey *key)
 
1254
{
 
1255
  GtkIMContextHangul *hcontext;
 
1256
 
 
1257
  g_return_val_if_fail (context != NULL, FALSE);
 
1258
  g_return_val_if_fail (key != NULL, FALSE);
 
1259
 
 
1260
  hcontext = GTK_IM_CONTEXT_HANGUL(context);
 
1261
  return gtk_im_context_filter_keypress(hcontext->slave, key);
 
1262
}
 
1263
 
 
1264
/* use hangul composer */
 
1265
static gboolean
 
1266
im_hangul_ic_filter_keypress (GtkIMContext *context, GdkEventKey *key)
 
1267
{
 
1268
  int keyval;
 
1269
  bool res;
 
1270
  const ucschar* commit;
 
1271
  const ucschar* preedit;
 
1272
  GtkIMContextHangul *hcontext;
 
1273
 
 
1274
  g_return_val_if_fail (context != NULL, FALSE);
 
1275
  g_return_val_if_fail (key != NULL, FALSE);
 
1276
 
 
1277
  hcontext = GTK_IM_CONTEXT_HANGUL(context);
 
1278
 
 
1279
  /* ignore key release */
 
1280
  if (key->type == GDK_KEY_RELEASE)
 
1281
    return FALSE;
 
1282
 
 
1283
  /* we silently ignore shift keys */
 
1284
  if (key->keyval == GDK_Shift_L || key->keyval == GDK_Shift_R)
 
1285
    return FALSE;
 
1286
 
 
1287
  /* candidate window mode */
 
1288
  if (hcontext->candidate != NULL)
 
1289
    return im_hangul_cadidate_filter_keypress (hcontext, key);
 
1290
 
 
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);
 
1295
      else
 
1296
          hangul_ic_set_output_mode(hcontext->hic, HANGUL_OUTPUT_SYLLABLE);
 
1297
  }
 
1298
 
 
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);
 
1302
 
 
1303
  /* handle Escape key: automaticaly change to direct mode */
 
1304
  if (key->keyval == GDK_Escape)
 
1305
    {
 
1306
      im_hangul_ic_reset(context);
 
1307
      im_hangul_set_input_mode(hcontext, INPUT_MODE_DIRECT);
 
1308
      return FALSE;
 
1309
    }
 
1310
 
 
1311
  /* modifiler key */
 
1312
  if (im_hangul_is_modifier (key->state))
 
1313
    {
 
1314
      im_hangul_ic_reset(context);
 
1315
      return FALSE;
 
1316
    }
 
1317
 
 
1318
  /* hanja key */
 
1319
  if (im_hangul_is_candidate(key))
 
1320
    {
 
1321
      popup_candidate_window (hcontext);
 
1322
      return TRUE;
 
1323
    }
 
1324
 
 
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);
 
1329
      return TRUE;
 
1330
  }
 
1331
 
 
1332
  /* backspace */
 
1333
  if (im_hangul_is_backspace(key)) {
 
1334
      res = hangul_ic_backspace(hcontext->hic);
 
1335
      if (res) {
 
1336
          preedit = hangul_ic_get_preedit_string(hcontext->hic);
 
1337
          im_hangul_ic_set_preedit(hcontext, preedit);
 
1338
      }
 
1339
      return res;
 
1340
  }
 
1341
 
 
1342
  /* process */
 
1343
  keyval = im_hangul_get_keyval(hcontext,
 
1344
                             key->hardware_keycode, key->keyval, key->state);
 
1345
  res = hangul_ic_process(hcontext->hic, keyval);
 
1346
 
 
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);
 
1356
      g_free(str);
 
1357
  }
 
1358
 
 
1359
  preedit = hangul_ic_get_preedit_string(hcontext->hic);
 
1360
  im_hangul_ic_set_preedit(hcontext, preedit);
 
1361
 
 
1362
  return res;
 
1363
}
 
1364
 
 
1365
/* status window */
 
1366
static gboolean
 
1367
status_window_expose_event (GtkWidget *widget, GdkEventExpose *event)
 
1368
{
 
1369
  gdk_draw_rectangle (widget->window,
 
1370
                      widget->style->fg_gc[GTK_STATE_NORMAL],
 
1371
                      FALSE,
 
1372
                      0, 0,
 
1373
                      widget->allocation.width-1, widget->allocation.height-1);
 
1374
 
 
1375
  return FALSE;
 
1376
}
 
1377
 
 
1378
static gboolean
 
1379
status_window_configure (GtkWidget *widget,
 
1380
                         GdkEventConfigure *event,
 
1381
                         Toplevel *toplevel)
 
1382
{
 
1383
  GdkRectangle rect;
 
1384
  GtkRequisition requisition;
 
1385
  gint y;
 
1386
 
 
1387
  if (toplevel == NULL || toplevel->status == NULL)
 
1388
    return FALSE;
 
1389
 
 
1390
  gdk_window_get_frame_extents (widget->window, &rect);
 
1391
  gtk_widget_size_request (toplevel->status, &requisition);
 
1392
 
 
1393
  if (rect.y + rect.height + requisition.height < gdk_screen_height ())
 
1394
    y = rect.y + rect.height;
 
1395
  else
 
1396
    y = gdk_screen_height () - requisition.height;
 
1397
 
 
1398
  gtk_window_move (GTK_WINDOW(toplevel->status), rect.x, y);
 
1399
  return FALSE;
 
1400
}
 
1401
 
 
1402
static GtkWidget*
 
1403
status_window_new(GtkWidget *parent)
 
1404
{
 
1405
  GtkWidget *window;
 
1406
  GtkWidget *frame;
 
1407
  GtkWidget *label;
 
1408
 
 
1409
  if (parent == NULL)
 
1410
    return NULL;
 
1411
 
 
1412
  window = gtk_window_new (GTK_WINDOW_POPUP);
 
1413
 
 
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);
 
1419
 
 
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);
 
1424
 
 
1425
  /* hangul status window label */
 
1426
  label = gtk_label_new (_("hangul")); 
 
1427
  gtk_container_add (GTK_CONTAINER(frame), label);
 
1428
  gtk_widget_show (label);
 
1429
 
 
1430
  g_signal_connect (G_OBJECT(window), "expose-event",
 
1431
                   G_CALLBACK(status_window_expose_event), NULL);
 
1432
 
 
1433
  return window;
 
1434
}
 
1435
 
 
1436
static void
 
1437
im_hangul_ic_show_status_window (GtkIMContextHangul *hcontext)
 
1438
{
 
1439
  g_return_if_fail (hcontext != NULL);
 
1440
 
 
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,
 
1446
                                 NULL,
 
1447
                                 hcontext->toplevel);
 
1448
    }
 
1449
    gtk_widget_show (hcontext->toplevel->status);
 
1450
  }
 
1451
}
 
1452
 
 
1453
static void
 
1454
im_hangul_ic_hide_status_window (GtkIMContextHangul *hcontext)
 
1455
{
 
1456
  g_return_if_fail (hcontext != NULL);
 
1457
 
 
1458
  if (hcontext->toplevel != NULL && hcontext->toplevel->status != NULL) {
 
1459
    gtk_widget_hide (hcontext->toplevel->status);
 
1460
  }
 
1461
}
 
1462
 
 
1463
static GtkWidget *
 
1464
get_toplevel_widget (GdkWindow *window)
 
1465
{
 
1466
  GtkWidget *gtk_toplevel;
 
1467
  gpointer ptr;
 
1468
 
 
1469
  if (window == NULL)
 
1470
    return NULL;
 
1471
 
 
1472
  gdk_window_get_user_data (window, &ptr);
 
1473
  memcpy(&gtk_toplevel, &ptr, sizeof(gtk_toplevel));
 
1474
  if (gtk_toplevel != NULL)
 
1475
    gtk_toplevel = gtk_widget_get_toplevel(GTK_WIDGET(gtk_toplevel));
 
1476
 
 
1477
  return gtk_toplevel;
 
1478
}
 
1479
 
 
1480
static void
 
1481
toplevel_destroy(Toplevel *toplevel)
 
1482
{
 
1483
  if (toplevel != NULL) {
 
1484
    toplevel_delete(toplevel);
 
1485
    toplevels = g_slist_remove(toplevels, toplevel);
 
1486
  }
 
1487
}
 
1488
 
 
1489
static Toplevel *
 
1490
toplevel_new(GtkWidget *toplevel_widget)
 
1491
{
 
1492
  Toplevel *toplevel = NULL;
 
1493
 
 
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),
 
1505
                             toplevel);
 
1506
 
 
1507
  g_object_set_data(G_OBJECT(toplevel_widget),
 
1508
                     "gtk-imhangul-toplevel-info", toplevel);
 
1509
  return toplevel;
 
1510
}
 
1511
 
 
1512
static Toplevel *
 
1513
toplevel_get(GdkWindow *window)
 
1514
{
 
1515
  Toplevel *toplevel = NULL;
 
1516
  GtkWidget *toplevel_widget;
 
1517
 
 
1518
  toplevel_widget = get_toplevel_widget (window);
 
1519
  if (toplevel_widget == NULL) {
 
1520
    return NULL;
 
1521
  }
 
1522
 
 
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);
 
1528
  }
 
1529
 
 
1530
  return toplevel;
 
1531
}
 
1532
 
 
1533
static void
 
1534
toplevel_remove_context(Toplevel *toplevel, GtkIMContextHangul *context)
 
1535
{
 
1536
  if (toplevel == NULL || context == NULL)
 
1537
    return;
 
1538
 
 
1539
  toplevel->contexts = g_slist_remove(toplevel->contexts, context);
 
1540
}
 
1541
 
 
1542
static void
 
1543
toplevel_append_context(Toplevel *toplevel, GtkIMContextHangul *context)
 
1544
{
 
1545
  if (toplevel == NULL || context == NULL)
 
1546
    return;
 
1547
 
 
1548
  toplevel->contexts = g_slist_prepend(toplevel->contexts, context);
 
1549
}
 
1550
 
 
1551
static void
 
1552
toplevel_delete(Toplevel *toplevel)
 
1553
{
 
1554
  if (toplevel != NULL) {
 
1555
    if (toplevel->status != NULL) {
 
1556
      gtk_widget_destroy(toplevel->status);
 
1557
    }
 
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);
 
1564
      }
 
1565
      g_slist_free(toplevel->contexts);
 
1566
    }
 
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);
 
1573
    g_free(toplevel);
 
1574
  }
 
1575
}
 
1576
 
 
1577
static int
 
1578
im_hangul_ic_get_toplevel_input_mode(GtkIMContextHangul *hcontext)
 
1579
{
 
1580
  if (hcontext->toplevel == NULL)
 
1581
    return INPUT_MODE_DIRECT;
 
1582
  return hcontext->toplevel->input_mode;
 
1583
}
 
1584
 
 
1585
static void
 
1586
im_hangul_ic_set_toplevel_input_mode(GtkIMContextHangul *hcontext, int mode)
 
1587
{
 
1588
  if (hcontext->toplevel != NULL)
 
1589
    hcontext->toplevel->input_mode = mode;
 
1590
}
 
1591
 
 
1592
/*
 
1593
 * candidate selection window
 
1594
 */
 
1595
static char*
 
1596
im_hangul_get_candidate_string(GtkIMContextHangul *ic)
 
1597
{
 
1598
    int n;
 
1599
    gboolean res;
 
1600
    gchar* text = NULL;
 
1601
    gint cursor_index = 0;
 
1602
    gunichar buf[20] = { 0, };
 
1603
    char* str = NULL;
 
1604
 
 
1605
    n = G_N_ELEMENTS(buf);
 
1606
    if (!hangul_ic_is_empty(ic->hic)) {
 
1607
        const ucschar* preedit;
 
1608
        int i, preedit_len;
 
1609
 
 
1610
        preedit = hangul_ic_get_preedit_string(ic->hic);
 
1611
        preedit_len = ucschar_strlen(preedit);
 
1612
 
 
1613
        n -= preedit_len;
 
1614
        for (i = 0; i < preedit_len; i++) {
 
1615
            buf[n + i] = preedit[i];
 
1616
        }
 
1617
    }
 
1618
 
 
1619
    res = gtk_im_context_get_surrounding(GTK_IM_CONTEXT(ic),
 
1620
                                         &text, &cursor_index);
 
1621
    if (res && text != NULL) {
 
1622
        gchar* p;
 
1623
 
 
1624
        p = g_utf8_find_prev_char(text, text + cursor_index);
 
1625
        while (n > 0 && p != NULL) {
 
1626
            if (*p == ' ')
 
1627
                break;
 
1628
 
 
1629
            buf[n - 1] = g_utf8_get_char(p);
 
1630
 
 
1631
            p = g_utf8_find_prev_char(text, p);
 
1632
            n--;
 
1633
        }
 
1634
 
 
1635
        g_free(text);
 
1636
    }
 
1637
 
 
1638
    if (n < G_N_ELEMENTS(buf)) {
 
1639
        char* utf8;
 
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);
 
1646
        }
 
1647
 
 
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);
 
1652
        g_free(utf8);
 
1653
    }
 
1654
 
 
1655
    return str;
 
1656
}
 
1657
 
 
1658
static void
 
1659
popup_candidate_window (GtkIMContextHangul *hcontext)
 
1660
{
 
1661
  char* key;
 
1662
  HanjaList* list;
 
1663
 
 
1664
  if (hcontext->candidate != NULL)
 
1665
    {
 
1666
      close_candidate_window(hcontext);
 
1667
    }
 
1668
 
 
1669
  if (hanja_table == NULL)
 
1670
      hanja_table = hanja_table_load(NULL);
 
1671
 
 
1672
  key = im_hangul_get_candidate_string(hcontext);
 
1673
  list = hanja_table_match_suffix(hanja_table, key);
 
1674
  if (list != NULL) {
 
1675
      hcontext->candidate = candidate_new (key,
 
1676
                                           9,
 
1677
                                           list,
 
1678
                                           hcontext->client_window,
 
1679
                                           &hcontext->cursor,
 
1680
                                           hcontext);
 
1681
  }
 
1682
  g_free(key);
 
1683
}
 
1684
 
 
1685
static void
 
1686
close_candidate_window (GtkIMContextHangul *hic)
 
1687
{
 
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;
 
1692
}
 
1693
 
 
1694
static gint
 
1695
im_hangul_key_snooper(GtkWidget *widget, GdkEventKey *event, gpointer data)
 
1696
{
 
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,
 
1705
     * I think :) */
 
1706
    return im_hangul_ic_filter_keypress(current_focused_ic, event);
 
1707
  }
 
1708
 
 
1709
  return FALSE;
 
1710
}
 
1711
 
 
1712
void
 
1713
im_hangul_init(void)
 
1714
{
 
1715
  im_hangul_config_parser();
 
1716
 
 
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);
 
1723
}
 
1724
 
 
1725
void
 
1726
im_hangul_finalize (void)
 
1727
{
 
1728
  GSList *item;
 
1729
 
 
1730
  /* remove gtk key snooper */
 
1731
  if (snooper_handler_id > 0) {
 
1732
    gtk_key_snooper_remove(snooper_handler_id);
 
1733
    snooper_handler_id = 0;
 
1734
  }
 
1735
 
 
1736
  /* remove toplevel info */
 
1737
  for (item = toplevels; item != NULL; item = g_slist_next(item)) {
 
1738
    toplevel_delete((Toplevel*)item->data);
 
1739
  }
 
1740
  g_slist_free(toplevels);
 
1741
  toplevels = NULL;
 
1742
}
 
1743
 
 
1744
/* candidate window */
 
1745
enum {
 
1746
  COLUMN_INDEX,
 
1747
  COLUMN_KEY,
 
1748
  COLUMN_VALUE,
 
1749
  COLUMN_COMMENT,
 
1750
  NO_OF_COLUMNS
 
1751
};
 
1752
 
 
1753
static void
 
1754
candidate_on_row_activated(GtkWidget *widget,
 
1755
                           GtkTreePath *path,
 
1756
                           GtkTreeViewColumn *column,
 
1757
                           Candidate *candidate)
 
1758
{
 
1759
  if (path != NULL)
 
1760
    {
 
1761
      int *indices;
 
1762
      const Hanja* hanja;
 
1763
      GtkIMContextHangul *hcontext = candidate->hangul_context;
 
1764
 
 
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);
 
1769
    }
 
1770
}
 
1771
 
 
1772
static void
 
1773
candidate_on_cursor_changed(GtkWidget *widget,
 
1774
                            Candidate *candidate)
 
1775
{
 
1776
  GtkTreePath *path;
 
1777
 
 
1778
  gtk_tree_view_get_cursor(GTK_TREE_VIEW(widget), &path, NULL);
 
1779
  if (path != NULL)
 
1780
    {
 
1781
      int *indices;
 
1782
      indices = gtk_tree_path_get_indices(path);
 
1783
      candidate->current = candidate->first + indices[0];
 
1784
      gtk_tree_path_free(path);
 
1785
    }
 
1786
}
 
1787
 
 
1788
static gboolean
 
1789
candidate_on_scroll(GtkWidget *widget,
 
1790
                    GdkEventScroll *event,
 
1791
                    gpointer data)
 
1792
{
 
1793
  Candidate *candidate;
 
1794
 
 
1795
  if (data == NULL)
 
1796
    return FALSE;
 
1797
 
 
1798
  candidate = (Candidate*)data;
 
1799
  switch (event->direction) {
 
1800
    case GDK_SCROLL_UP:
 
1801
      candidate_prev_page(candidate);
 
1802
      break;
 
1803
    case GDK_SCROLL_DOWN:
 
1804
      candidate_next_page(candidate);
 
1805
      break;
 
1806
    default:
 
1807
      return FALSE;
 
1808
  }
 
1809
  return TRUE;
 
1810
}
 
1811
 
 
1812
static gboolean
 
1813
candidate_on_key_press(GtkWidget *widget,
 
1814
                       GdkEventKey *event,
 
1815
                       gpointer data)
 
1816
{
 
1817
  Candidate *candidate;
 
1818
  const Hanja* hanja = NULL;
 
1819
 
 
1820
  if (data == NULL)
 
1821
    return FALSE;
 
1822
 
 
1823
  candidate = (Candidate*)data;
 
1824
  switch (event->keyval) {
 
1825
    case GDK_Return:
 
1826
    case GDK_KP_Enter:
 
1827
      hanja = candidate_get_current(candidate);
 
1828
      break;
 
1829
    case GDK_Left:
 
1830
    case GDK_h:
 
1831
    case GDK_Page_Up:
 
1832
      candidate_prev_page(candidate);
 
1833
      break;
 
1834
    case GDK_Right:
 
1835
    case GDK_l:
 
1836
    case GDK_Page_Down:
 
1837
      candidate_next_page(candidate);
 
1838
      break;
 
1839
    case GDK_Up:
 
1840
    case GDK_k:
 
1841
    case GDK_BackSpace:
 
1842
    case GDK_KP_Subtract:
 
1843
      candidate_prev(candidate);
 
1844
      break;
 
1845
    case GDK_Down:
 
1846
    case GDK_j:
 
1847
    case GDK_space:
 
1848
    case GDK_KP_Add:
 
1849
    case GDK_KP_Tab:
 
1850
      candidate_next(candidate);
 
1851
      break;
 
1852
    case GDK_Escape:
 
1853
      close_candidate_window(candidate->hangul_context);
 
1854
      break;
 
1855
    case GDK_0:
 
1856
      hanja = candidate_get_nth(candidate, 9);
 
1857
      break;
 
1858
    case GDK_1:
 
1859
    case GDK_2:
 
1860
    case GDK_3:
 
1861
    case GDK_4:
 
1862
    case GDK_5:
 
1863
    case GDK_6:
 
1864
    case GDK_7:
 
1865
    case GDK_8:
 
1866
    case GDK_9:
 
1867
      hanja = candidate_get_nth(candidate, event->keyval - GDK_1);
 
1868
      break;
 
1869
    default:
 
1870
      return FALSE;
 
1871
  }
 
1872
 
 
1873
  if (hanja != NULL)
 
1874
    im_hangul_candidate_commit(candidate->hangul_context,
 
1875
                               candidate->key, hanja);
 
1876
  return TRUE;
 
1877
}
 
1878
 
 
1879
static void
 
1880
candidate_on_expose (GtkWidget *widget,
 
1881
                     GdkEventExpose *event,
 
1882
                     gpointer data)
 
1883
{
 
1884
  GtkStyle *style;
 
1885
  GtkAllocation alloc;
 
1886
 
 
1887
  style = gtk_widget_get_style(widget);
 
1888
  alloc = GTK_WIDGET(widget)->allocation;
 
1889
  gdk_draw_rectangle(widget->window, style->black_gc,
 
1890
                     FALSE,
 
1891
                     0, 0, alloc.width - 1, alloc.height - 1);
 
1892
}
 
1893
 
 
1894
static void
 
1895
candidate_update_cursor(Candidate *candidate)
 
1896
{
 
1897
  GtkTreePath *path;
 
1898
 
 
1899
  if (candidate->treeview == NULL)
 
1900
    return;
 
1901
 
 
1902
  path = gtk_tree_path_new_from_indices(candidate->current - candidate->first,                                          -1);
 
1903
  gtk_tree_view_set_cursor(GTK_TREE_VIEW(candidate->treeview),
 
1904
                           path, NULL, FALSE);
 
1905
  gtk_tree_path_free(path);
 
1906
}
 
1907
 
 
1908
static void
 
1909
candidate_set_window_position (Candidate *candidate)
 
1910
{
 
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;
 
1915
 
 
1916
    if (candidate->parent == NULL)
 
1917
      return;
 
1918
 
 
1919
    gdk_window_get_origin (GDK_WINDOW(candidate->parent), &absx, &absy);
 
1920
    gdk_drawable_get_size (GDK_DRAWABLE(candidate->parent), &width, &height);
 
1921
 
 
1922
    root_w = gdk_screen_width();
 
1923
    root_h = gdk_screen_height();
 
1924
 
 
1925
    gtk_widget_size_request(GTK_WIDGET(candidate->window), &requisition);
 
1926
    cand_w = requisition.width;
 
1927
    cand_h = requisition.height;
 
1928
 
 
1929
    absx += candidate->cursor.x;
 
1930
    absy += (candidate->cursor.height < 0)? 
 
1931
            height : candidate->cursor.y + candidate->cursor.height;
 
1932
 
 
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);
 
1938
}
 
1939
 
 
1940
static void
 
1941
candidate_update_list(Candidate *candidate)
 
1942
{
 
1943
  int i;
 
1944
  GtkTreeIter iter;
 
1945
 
 
1946
  gtk_list_store_clear(candidate->store);
 
1947
  for (i = 0;
 
1948
       i < candidate->n_per_page && candidate->first + i < candidate->n;
 
1949
       i++)
 
1950
    {
 
1951
      const char* value;
 
1952
      const char* comment;
 
1953
      const Hanja* hanja;
 
1954
      
 
1955
      hanja = hanja_list_get_nth(candidate->list, candidate->first + i);
 
1956
      value = hanja_get_value(hanja);
 
1957
      comment = hanja_get_comment(hanja);
 
1958
 
 
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,
 
1964
              -1);
 
1965
    }
 
1966
  candidate_set_window_position (candidate);
 
1967
}
 
1968
 
 
1969
static void
 
1970
candidate_on_realize(GtkWidget* widget, gpointer data)
 
1971
{
 
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]);
 
1980
}
 
1981
 
 
1982
static void
 
1983
candidate_create_window(Candidate *candidate)
 
1984
{
 
1985
  GtkWidget *frame;
 
1986
  GtkWidget *treeview;
 
1987
  GtkTreeViewColumn *column;
 
1988
  GtkCellRenderer *renderer;
 
1989
 
 
1990
  candidate->window = gtk_window_new(GTK_WINDOW_POPUP);
 
1991
 
 
1992
  candidate_update_list(candidate);
 
1993
 
 
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);
 
1997
 
 
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);
 
2004
 
 
2005
  /* number column */
 
2006
  renderer = gtk_cell_renderer_text_new();
 
2007
  column = gtk_tree_view_column_new_with_attributes("No",
 
2008
                                                    renderer,
 
2009
                                                    "text", COLUMN_INDEX,
 
2010
                                                    NULL);
 
2011
  gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
 
2012
 
 
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",
 
2017
                                                    renderer,
 
2018
                                                    "text", COLUMN_VALUE,
 
2019
                                                    NULL);
 
2020
  gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
 
2021
 
 
2022
  /* comment column */
 
2023
  renderer = gtk_cell_renderer_text_new();
 
2024
  column = gtk_tree_view_column_new_with_attributes("Comment",
 
2025
                                                    renderer,
 
2026
                                                    "text", COLUMN_COMMENT,
 
2027
                                                    NULL);
 
2028
  gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
 
2029
 
 
2030
  candidate_update_cursor(candidate);
 
2031
 
 
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),
 
2051
                           candidate);
 
2052
 
 
2053
  gtk_widget_show_all(candidate->window);
 
2054
  gtk_grab_add(candidate->window);
 
2055
}
 
2056
 
 
2057
static Candidate*
 
2058
candidate_new(char *key,
 
2059
              int n_per_page,
 
2060
              HanjaList *list,
 
2061
              GdkWindow *parent,
 
2062
              GdkRectangle *area,
 
2063
              GtkIMContextHangul *hcontext)
 
2064
{
 
2065
  Candidate *candidate;
 
2066
 
 
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;
 
2079
 
 
2080
  if (n_per_page == 0)
 
2081
    candidate->n_per_page = candidate->n;
 
2082
 
 
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);
 
2086
 
 
2087
  return candidate;
 
2088
}
 
2089
 
 
2090
static void
 
2091
candidate_prev(Candidate *candidate)
 
2092
{
 
2093
  if (candidate == NULL)
 
2094
    return;
 
2095
 
 
2096
  if (candidate->current > 0)
 
2097
    candidate->current--;
 
2098
 
 
2099
  if (candidate->current < candidate->first)
 
2100
    {
 
2101
      candidate->first -= candidate->n_per_page;
 
2102
      candidate_update_list(candidate);
 
2103
    }
 
2104
  candidate_update_cursor(candidate);
 
2105
}
 
2106
 
 
2107
static void
 
2108
candidate_next(Candidate *candidate)
 
2109
{
 
2110
  if (candidate == NULL)
 
2111
    return;
 
2112
 
 
2113
  if (candidate->current < candidate->n - 1)
 
2114
    candidate->current++;
 
2115
 
 
2116
  if (candidate->current >= candidate->first + candidate->n_per_page)
 
2117
    {
 
2118
      candidate->first += candidate->n_per_page;
 
2119
      candidate_update_list(candidate);
 
2120
    }
 
2121
  candidate_update_cursor(candidate);
 
2122
}
 
2123
 
 
2124
static void
 
2125
candidate_prev_page(Candidate *candidate)
 
2126
{
 
2127
  if (candidate == NULL)
 
2128
    return;
 
2129
 
 
2130
  if (candidate->first - candidate->n_per_page >= 0)
 
2131
    {
 
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);
 
2137
    }
 
2138
  candidate_update_cursor(candidate);
 
2139
}
 
2140
 
 
2141
static void
 
2142
candidate_next_page(Candidate *candidate)
 
2143
{
 
2144
  if (candidate == NULL)
 
2145
    return;
 
2146
 
 
2147
  if (candidate->first + candidate->n_per_page < candidate->n)
 
2148
    {
 
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);
 
2154
    }
 
2155
  candidate_update_cursor(candidate);
 
2156
}
 
2157
 
 
2158
static const Hanja*
 
2159
candidate_get_current(Candidate *candidate)
 
2160
{
 
2161
  if (candidate == NULL)
 
2162
    return 0;
 
2163
 
 
2164
  return hanja_list_get_nth(candidate->list, candidate->current);
 
2165
}
 
2166
 
 
2167
static const Hanja*
 
2168
candidate_get_nth(Candidate *candidate, int index_)
 
2169
{
 
2170
  if (candidate == NULL)
 
2171
    return 0;
 
2172
 
 
2173
  index_ += candidate->first;
 
2174
  if (index_ < 0 || index_ >= candidate->n)
 
2175
    return 0;
 
2176
 
 
2177
  return hanja_list_get_nth(candidate->list, index_);
 
2178
}
 
2179
 
 
2180
static void
 
2181
candidate_delete(Candidate *candidate)
 
2182
{
 
2183
  if (candidate == NULL)
 
2184
    return;
 
2185
 
 
2186
  gtk_grab_remove(candidate->window);
 
2187
  gtk_widget_destroy(candidate->window);
 
2188
  hanja_list_delete(candidate->list);
 
2189
  g_free(candidate->key);
 
2190
  g_free(candidate);
 
2191
}
 
2192
 
 
2193
static size_t
 
2194
ucschar_strlen(const ucschar* s)
 
2195
{
 
2196
    const ucschar* p = s;
 
2197
    while (*p != 0)
 
2198
        p++;
 
2199
    return p - s;
 
2200
}
 
2201
 
 
2202
/* vim: set cindent sw=4 sts=4 ts=8 : */