Modified license in the spec file
[platform/core/uifw/libhangul.git] / hangul / hangulinputcontext.c
1 /* libhangul
2  * Copyright (C) 2004 - 2009 Choe Hwanjin
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
16  */
17
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21
22 #include <stdlib.h>
23 #include <string.h>
24 #include <strings.h>
25 #include <ctype.h>
26 #include <inttypes.h>
27 #include <limits.h>
28
29 #include "hangul-gettext.h"
30 #include "hangul.h"
31 #include "hangulinternals.h"
32
33 /**
34  * @defgroup hangulic 한글 입력 기능 구현
35  * 
36  * @section hangulicusage Hangul Input Context의 사용법
37  * 이 섹션에서는 한글 입력 기능을 구현하는 핵심 기능에 대해 설명한다.
38  *
39  * 먼저 preedit string과 commit string 이 두 용어에 대해서 설명하겠다.
40  * 이 두가지 용어는 Unix 계열의 입력기 framework에서 널리 쓰이는 표현이다.
41  *
42  * preedit string은 아직 조합중으로 어플리케이션에 완전히 입력되지 않은 
43  * 스트링을 가리킨다. 일반적으로 한글 입력기에서는 역상으로 보이고
44  * 일본 중국어 입력기에서는 underline이 붙어 나타난다. 아직 완성이 되지
45  * 않은 스트링이므로 어플리케이션에 전달이 되지 않고 사라질 수도 있다.
46  *
47  * commit string은 조합이 완료되어 어플리케이션에 전달되는 스트링이다.
48  * 이 스트링은 실제 어플리케이션의 텍스트로 인식이 되므로 이 이후에는
49  * 더이상 입력기가 관리할 수 있는 데이터가 아니다.
50  *
51  * 한글 입력과정은 다음과 같은 과정을 거치게 된다.
52  * 입력된 영문 키를 그에 해당하는 한글 자모로 변환한후 한글 자모를 모아
53  * 하나의 음절을 만든다. 여기까지 이루어지는 과정을 preedit string 형태로
54  * 사용자에게 계속 보이게 하는 것이 필요하다.
55  * 그리고는 한글 음절이 완성되고나면 그 글자를 어플리케이션에 commit 
56  * string 형태로 보내여 입력을 완료하는 것이다. 다음 키를 받게 되면 
57  * 이 과정을 반복해서 수행한다.
58  * 
59  * libhangul에서 한글 조합 기능은 @ref HangulInputContext 를 이용해서 구현하게
60  * 되는데 기본 적인 방법은 @ref HangulInputContext 에 사용자로부터의 입력을
61  * 순서대로 전달하면서 그 상태가 바뀜에 따라서 preedit 나 commit 스트링을
62  * 상황에 맞게 변화시키는 것이다.
63  * 
64  * 입력 코드들은 GUI 코드와 밀접하게 붙어 있어서 키 이벤트를 받아서
65  * 처리하도록 구현하는 것이 보통이다. 그런데 유닉스에는 많은 입력 프레임웍들이
66  * 난립하고 있는 상황이어서 매 입력 프레임웍마다 한글 조합 루틴을 작성해서
67  * 넣는 것은 비효율적이다. 간단한 API를 구현하여 여러 프레임웍에서 바로 
68  * 사용할 수 있도록 구현하는 편이 사용성이 높아지게 된다.
69  *
70  * 그래서 libhangul에서는 키 이벤트를 따로 재정의하지 않고 ASCII 코드를 
71  * 직접 사용하는 방향으로 재정의된 데이터가 많지 않도록 하였다.
72  * 실제 사용 방법은 말로 설명하는 것보다 샘플 코드를 사용하는 편이
73  * 이해가 빠를 것이다. 그래서 대략적인 진행 과정을 샘플 코드로 
74  * 작성하였다.
75  *
76  * 아래 예제는 실제로는 존재하지 않는 GUI 라이브러리 코드를 사용하였다.
77  * 실제 GUI 코드를 사용하면 코드가 너무 길어져서 설명이 어렵고 코드가
78  * 길어지면 핵심을 놓치기 쉽기 때문에 가공의 함수를 사용하였다.
79  * 또한 텍스트의 encoding conversion 관련된 부분도 생략하였다.
80  * 여기서 사용한 가공의 GUI 코드는 TWin으로 시작하게 하였다.
81  *    
82  * @code
83
84     HangulInputContext* hic = hangul_ic_new("2");
85     ...
86
87     // 아래는 키 입력만 처리하는 이벤트 루프이다.
88     // 실제 GUI코드는 이렇게 단순하진 않지만
89     // 편의상 키 입력만 처리하는 코드로 작성하였다.
90
91     TWinKeyEvent event = TWinGetKeyEvent(); // 키이벤트를 받는 이런 함수가
92                                             // 있다고 치자
93     while (ascii != 0) {
94         bool res;
95         if (event.isBackspace()) {
96             // backspace를 ascii로 변환하기가 좀 꺼림직해서
97             // libhangul에서는 backspace 처리를 위한 
98             // 함수를 따로 만들었다.
99             res = hangul_ic_backspace(hic);
100         } else {
101             // 키 입력을 해당하는 ascii 코드로 변환한다.
102             // libhangul에서는 이 ascii 코드가 키 이벤트
103             // 코드와 마찬가지다.
104             int ascii = event.getAscii();
105
106             // 키 입력을 받았으면 이것을 hic에 먼저 보낸다.
107             // 그래야 hic가 이 키를 사용할 것인지 아닌지를 판단할 수 있다.
108             // 함수가 true를 리턴하면 이 키를 사용했다는 의미이므로 
109             // GUI 코드가 이 키 입력을 프로세싱하지 않도록 해야 한다.
110             // 그렇지 않으면 한 키입력이 두번 프로세싱된다.
111             res = hangul_ic_process(hic, ascii);
112         }
113         
114         // hic는 한번 키입력을 받고 나면 내부 상태 변화가 일어나고
115         // 완성된 글자를 어플리케이션에 보내야 하는 상황이 있을 수 있다.
116         // 이것을 HangulInputContext에서는 commit 스트링이 있는지로
117         // 판단한다. commit 스트링을 받아봐서 스트링이 있다면 
118         // 그 스트링으로 입력이 완료된 걸로 본다.
119         const ucschar commit;
120         commit = hangul_ic_get_commit_string(hic);
121         if (commit[0] != 0) {   // 스트링의 길이를 재서 commit 스트링이 있는지
122                                 // 판단한다.
123             TWinInputUnicodeChars(commit);
124         }
125
126         // 키입력 후에는 preedit string도 역시 변화하게 되는데
127         // 입력기 프레임웍에서는 이 스트링을 화면에 보여주어야
128         // 조합중인 글자가 화면에 표시가 되는 것이다.
129         const ucschar preedit;
130         preedit = hangul_ic_get_preedit_string(hic);
131         // 이 경우에는 스트링의 길이에 관계없이 항상 업데이트를 
132         // 해야 한다. 왜냐하면 이전에 조합중이던 글자가 있다가
133         // 조합이 완료되면서 조합중인 상태의 글자가 없어질 수도 있기 때문에
134         // 스트링의 길이에 관계없이 현재 상태의 스트링을 preedit 
135         // 스트링으로 보여주면 되는 것이다.
136         TWinUpdatePreeditString(preedit);
137
138         // 위 두작업이 끝난후에는 키 이벤트를 계속 프로세싱해야 하는지 
139         // 아닌지를 처리해야 한다.
140         // hic가 키 이벤트를 사용하지 않았다면 기본 GUI 코드에 계속해서
141         // 키 이벤트 프로세싱을 진행하도록 해야 한다.
142         if (!res)
143             TWinForwardKeyEventToUI(ascii);
144
145         ascii = GetKeyEvent();
146     }
147
148     hangul_ic_delete(hic);
149      
150  * @endcode
151  */
152
153 /**
154  * @file hangulinputcontext.c
155  */
156
157 /**
158  * @ingroup hangulic
159  * @typedef HangulInputContext
160  * @brief 한글 입력 상태를 관리하기 위한 오브젝트
161  *
162  * libhangul에서 제공하는 한글 조합 루틴에서 상태 정보를 저장하는 opaque
163  * 데이타 오브젝트이다. 이 오브젝트에 키입력 정보를 순차적으로 보내주면서
164  * preedit 스트링이나, commit 스트링을 받아서 처리하면 한글 입력 기능을
165  * 손쉽게 구현할 수 있다.
166  * 내부의 데이터 멤버는 공개되어 있지 않다. 각각의 멤버는 accessor 함수로만
167  * 참조하여야 한다.
168  */
169
170 #ifndef TRUE
171 #define TRUE 1
172 #endif
173
174 #ifndef FALSE
175 #define FALSE 0
176 #endif
177
178 #define HANGUL_KEYBOARD_TABLE_SIZE 0x80
179
180 typedef void   (*HangulOnTranslate)  (HangulInputContext*,
181                                       int,
182                                       ucschar*,
183                                       void*);
184 typedef bool   (*HangulOnTransition) (HangulInputContext*,
185                                       ucschar,
186                                       const ucschar*,
187                                       void*);
188
189 typedef struct _HangulCombinationItem HangulCombinationItem;
190
191 struct _HangulKeyboard {
192     int type;
193     const char* id;
194     const char* name;
195     const ucschar* table;
196     const HangulCombination* combination;
197 };
198
199 struct _HangulCombinationItem {
200     uint32_t key;
201     ucschar code;
202 };
203
204 struct _HangulCombination {
205     int size;
206     HangulCombinationItem *table;
207 };
208
209 struct _HangulBuffer {
210     ucschar choseong;
211     ucschar jungseong;
212     ucschar jongseong;
213
214     ucschar stack[12];
215     int     index;
216 };
217
218 struct _HangulInputContext {
219     int type;
220
221     const HangulKeyboard*    keyboard;
222
223     HangulBuffer buffer;
224     int output_mode;
225
226     ucschar preedit_string[64];
227     ucschar commit_string[64];
228     ucschar flushed_string[64];
229
230     HangulOnTranslate   on_translate;
231     void*               on_translate_data;
232
233     HangulOnTransition  on_transition;
234     void*               on_transition_data;
235
236     unsigned int use_jamo_mode_only : 1;
237 };
238
239 #include "hangulkeyboard.h"
240
241 static const HangulCombination hangul_combination_default = {
242     N_ELEMENTS(hangul_combination_table_default),
243     (HangulCombinationItem*)hangul_combination_table_default
244 };
245
246 static const HangulCombination hangul_combination_romaja = {
247     N_ELEMENTS(hangul_combination_table_romaja),
248     (HangulCombinationItem*)hangul_combination_table_romaja
249 };
250
251 static const HangulCombination hangul_combination_full = {
252     N_ELEMENTS(hangul_combination_table_full),
253     (HangulCombinationItem*)hangul_combination_table_full
254 };
255
256 static const HangulCombination hangul_combination_ahn = {
257     N_ELEMENTS(hangul_combination_table_ahn),
258     (HangulCombinationItem*)hangul_combination_table_ahn
259 };
260
261 static const HangulKeyboard hangul_keyboard_2 = {
262     HANGUL_KEYBOARD_TYPE_JAMO,
263     "2", 
264     N_("Dubeolsik"), 
265     (ucschar*)hangul_keyboard_table_2,
266     &hangul_combination_default
267 };
268
269 static const HangulKeyboard hangul_keyboard_2y = {
270     HANGUL_KEYBOARD_TYPE_JAMO,
271     "2y", 
272     N_("Dubeolsik Yetgeul"), 
273     (ucschar*)hangul_keyboard_table_2y,
274     &hangul_combination_full
275 };
276
277 static const HangulKeyboard hangul_keyboard_32 = {
278     HANGUL_KEYBOARD_TYPE_JASO,
279     "32",
280     N_("Sebeolsik Dubeol Layout"),
281     (ucschar*)hangul_keyboard_table_32,
282     &hangul_combination_default
283 };
284
285 static const HangulKeyboard hangul_keyboard_390 = {
286     HANGUL_KEYBOARD_TYPE_JASO,
287     "39",
288     N_("Sebeolsik 390"),
289     (ucschar*)hangul_keyboard_table_390,
290     &hangul_combination_default
291 };
292
293 static const HangulKeyboard hangul_keyboard_3final = {
294     HANGUL_KEYBOARD_TYPE_JASO,
295     "3f",
296     N_("Sebeolsik Final"),
297     (ucschar*)hangul_keyboard_table_3final,
298     &hangul_combination_default
299 };
300
301 static const HangulKeyboard hangul_keyboard_3sun = {
302     HANGUL_KEYBOARD_TYPE_JASO,
303     "3s",
304     N_("Sebeolsik Noshift"),
305     (ucschar*)hangul_keyboard_table_3sun,
306     &hangul_combination_default
307 };
308
309 static const HangulKeyboard hangul_keyboard_3yet = {
310     HANGUL_KEYBOARD_TYPE_JASO,
311     "3y",
312     N_("Sebeolsik Yetgeul"),
313     (ucschar*)hangul_keyboard_table_3yet,
314     &hangul_combination_full
315 };
316
317 static const HangulKeyboard hangul_keyboard_romaja = {
318     HANGUL_KEYBOARD_TYPE_ROMAJA,
319     "ro",
320     N_("Romaja"),
321     (ucschar*)hangul_keyboard_table_romaja,
322     &hangul_combination_romaja
323 };
324
325 static const HangulKeyboard hangul_keyboard_ahn = {
326     HANGUL_KEYBOARD_TYPE_JASO,
327     "ahn",
328     N_("Ahnmatae"),
329     (ucschar*)hangul_keyboard_table_ahn,
330     &hangul_combination_ahn
331 };
332
333 static const HangulKeyboard* hangul_keyboards[] = {
334     &hangul_keyboard_2,
335     &hangul_keyboard_2y,
336     &hangul_keyboard_390,
337     &hangul_keyboard_3final,
338     &hangul_keyboard_3sun,
339     &hangul_keyboard_3yet,
340     &hangul_keyboard_32,
341     &hangul_keyboard_romaja,
342     &hangul_keyboard_ahn,
343 };
344
345
346 static void    hangul_buffer_push(HangulBuffer *buffer, ucschar ch);
347 static ucschar hangul_buffer_pop (HangulBuffer *buffer);
348 static ucschar hangul_buffer_peek(HangulBuffer *buffer);
349
350 static void    hangul_buffer_clear(HangulBuffer *buffer);
351 static int     hangul_buffer_get_string(HangulBuffer *buffer, ucschar*buf, int buflen);
352 static int     hangul_buffer_get_jamo_string(HangulBuffer *buffer, ucschar *buf, int buflen);
353
354 static void    hangul_ic_flush_internal(HangulInputContext *hic);
355
356 HangulKeyboard*
357 hangul_keyboard_new()
358 {
359     HangulKeyboard *keyboard = malloc(sizeof(HangulKeyboard));
360     if (keyboard != NULL) {
361         ucschar* table = malloc(sizeof(ucschar) * HANGUL_KEYBOARD_TABLE_SIZE);
362         if (table != NULL) {
363             int i;
364             for (i = 0; i < HANGUL_KEYBOARD_TABLE_SIZE; i++)
365                 table[i] = 0;
366
367             keyboard->table = table;
368             return keyboard;
369         }
370         free(keyboard);
371     }
372
373     return NULL;
374 }
375
376 static ucschar
377 hangul_keyboard_get_value(const HangulKeyboard *keyboard, int key)
378 {
379     if (keyboard != NULL) {
380         if (key >= 0 && key < HANGUL_KEYBOARD_TABLE_SIZE)
381             return keyboard->table[key];
382     }
383
384     return 0;
385 }
386
387 void
388 hangul_keyboard_set_value(HangulKeyboard *keyboard, int key, ucschar value)
389 {
390     if (keyboard != NULL) {
391         if (key >= 0 && key < HANGUL_KEYBOARD_TABLE_SIZE) {
392             ucschar* table = (ucschar*)keyboard->table;
393             table[key] = value;
394         }
395     }
396 }
397
398 static int
399 hangul_keyboard_get_type(const HangulKeyboard *keyboard)
400 {
401     int type = 0;
402     if (keyboard != NULL) {
403         type = keyboard->type;
404     }
405     return type;
406 }
407
408 void
409 hangul_keyboard_set_type(HangulKeyboard *keyboard, int type)
410 {
411     if (keyboard != NULL) {
412         keyboard->type = type;
413     }
414 }
415
416 void
417 hangul_keyboard_delete(HangulKeyboard *keyboard)
418 {
419     if (keyboard != NULL)
420         free(keyboard);
421 }
422
423 HangulCombination*
424 hangul_combination_new()
425 {
426     HangulCombination *combination = malloc(sizeof(HangulCombination));
427     if (combination != NULL) {
428         combination->size = 0;
429         combination->table = NULL;
430         return combination;
431     }
432
433     return NULL;
434 }
435
436 void
437 hangul_combination_delete(HangulCombination *combination)
438 {
439     if (combination != NULL) {
440         if (combination->table != NULL)
441             free(combination->table);
442         free(combination);
443     }
444 }
445
446 static uint32_t
447 hangul_combination_make_key(ucschar first, ucschar second)
448 {
449     return first << 16 | second;
450 }
451
452 bool
453 hangul_combination_set_data(HangulCombination* combination, 
454                             ucschar* first, ucschar* second, ucschar* result,
455                             unsigned int n)
456 {
457     if (combination == NULL)
458         return false;
459
460     if (n == 0 || n > ULONG_MAX / sizeof(HangulCombinationItem))
461         return false;
462
463     combination->table = malloc(sizeof(HangulCombinationItem) * n);
464     if (combination->table != NULL) {
465         int i;
466
467         combination->size = n;
468         for (i = 0; i < n; i++) {
469             combination->table[i].key = hangul_combination_make_key(first[i], second[i]);
470             combination->table[i].code = result[i];
471         }
472         return true;
473     }
474
475     return false;
476 }
477
478 static int 
479 hangul_combination_cmp(const void* p1, const void* p2)
480 {
481     const HangulCombinationItem *item1 = p1;
482     const HangulCombinationItem *item2 = p2;
483
484     /* key는 unsigned int이므로 단순히 빼서 리턴하면 안된다.
485      * 두 수의 차가 큰 경우 int로 변환하면서 음수가 될 수 있다. */
486     if (item1->key < item2->key)
487         return -1;
488     else if (item1->key > item2->key)
489         return 1;
490     else
491         return 0;
492 }
493
494 ucschar
495 hangul_combination_combine(const HangulCombination* combination,
496                            ucschar first, ucschar second)
497 {
498     HangulCombinationItem *res;
499     HangulCombinationItem key;
500
501     if (combination == NULL)
502         return 0;
503
504     key.key = hangul_combination_make_key(first, second);
505     res = bsearch(&key, combination->table, combination->size,
506                   sizeof(combination->table[0]), hangul_combination_cmp);
507     if (res != NULL)
508         return res->code;
509
510     return 0;
511 }
512
513 static bool
514 hangul_buffer_is_empty(HangulBuffer *buffer)
515 {
516     return buffer->choseong == 0 && buffer->jungseong == 0 &&
517            buffer->jongseong == 0;
518 }
519
520 static bool
521 hangul_buffer_has_choseong(HangulBuffer *buffer)
522 {
523     return buffer->choseong != 0;
524 }
525
526 static bool
527 hangul_buffer_has_jungseong(HangulBuffer *buffer)
528 {
529     return buffer->jungseong != 0;
530 }
531
532 static bool
533 hangul_buffer_has_jongseong(HangulBuffer *buffer)
534 {
535     return buffer->jongseong != 0;
536 }
537
538 static void
539 hangul_buffer_push(HangulBuffer *buffer, ucschar ch)
540 {
541     if (hangul_is_choseong(ch)) {
542         buffer->choseong = ch;
543     } else if (hangul_is_jungseong(ch)) {
544         buffer->jungseong = ch;
545     } else if (hangul_is_jongseong(ch)) {
546         buffer->jongseong = ch;
547     } else {
548     }
549
550     buffer->stack[++buffer->index] = ch;
551 }
552
553 static ucschar
554 hangul_buffer_pop(HangulBuffer *buffer)
555 {
556     return buffer->stack[buffer->index--];
557 }
558
559 static ucschar
560 hangul_buffer_peek(HangulBuffer *buffer)
561 {
562     if (buffer->index < 0)
563         return 0;
564
565     return buffer->stack[buffer->index];
566 }
567
568 static void
569 hangul_buffer_clear(HangulBuffer *buffer)
570 {
571     buffer->choseong = 0;
572     buffer->jungseong = 0;
573     buffer->jongseong = 0;
574
575     buffer->index = -1;
576     buffer->stack[0]  = 0;
577     buffer->stack[1]  = 0;
578     buffer->stack[2]  = 0;
579     buffer->stack[3]  = 0;
580     buffer->stack[4]  = 0;
581     buffer->stack[5]  = 0;
582     buffer->stack[6]  = 0;
583     buffer->stack[7]  = 0;
584     buffer->stack[8]  = 0;
585     buffer->stack[9]  = 0;
586     buffer->stack[10] = 0;
587     buffer->stack[11] = 0;
588 }
589
590 static int
591 hangul_buffer_get_jamo_string(HangulBuffer *buffer, ucschar *buf, int buflen)
592 {
593     int n = 0;
594
595     if (buffer->choseong || buffer->jungseong || buffer->jongseong) {
596         if (buffer->choseong) {
597             buf[n++] = buffer->choseong;
598         } else {
599             buf[n++] = HANGUL_CHOSEONG_FILLER;
600         }
601         if (buffer->jungseong) {
602             buf[n++] = buffer->jungseong;
603         } else {
604             buf[n++] = HANGUL_JUNGSEONG_FILLER;
605         }
606         if (buffer->jongseong) {
607             buf[n++] = buffer->jongseong;
608         }
609     }
610
611     buf[n] = 0;
612
613     return n;
614 }
615
616 static int
617 hangul_jaso_to_string(ucschar cho, ucschar jung, ucschar jong,
618                       ucschar *buf, int len)
619 {
620     ucschar ch = 0;
621     int n = 0;
622
623     if (cho) {
624         if (jung) {
625             /* have cho, jung, jong or no jong */
626             ch = hangul_jamo_to_syllable(cho, jung, jong);
627             if (ch != 0) {
628                 buf[n++] = ch;
629             } else {
630                 /* 한글 음절로 표현 불가능한 경우 */
631                 buf[n++] = cho;
632                 buf[n++] = jung;
633                 if (jong != 0)
634                     buf[n++] = jong;
635             }
636         } else {
637             if (jong) {
638                 /* have cho, jong */
639                 buf[n++] = cho;
640                 buf[n++] = HANGUL_JUNGSEONG_FILLER;
641                 buf[n++] = jong;
642             } else {
643                 /* have cho */
644                 ch = hangul_jamo_to_cjamo(cho);
645                 if (hangul_is_cjamo(ch)) {
646                     buf[n++] = ch;
647                 } else {
648                     buf[n++] = cho;
649                     buf[n++] = HANGUL_JUNGSEONG_FILLER;
650                 }
651             }
652         }
653     } else {
654         if (jung) {
655             if (jong) {
656                 /* have jung, jong */
657                 buf[n++] = HANGUL_CHOSEONG_FILLER;
658                 buf[n++] = jung;
659                 buf[n++] = jong;
660             } else {
661                 /* have jung */
662                 ch = hangul_jamo_to_cjamo(jung);
663                 if (hangul_is_cjamo(ch)) {
664                     buf[n++] = ch;
665                 } else {
666                     buf[n++] = HANGUL_CHOSEONG_FILLER;
667                     buf[n++] = jung;
668                 }
669             }
670         } else {
671             if (jong) { 
672                 /* have jong */
673                 ch = hangul_jamo_to_cjamo(jong);
674                 if (hangul_is_cjamo(ch)) {
675                     buf[n++] = ch;
676                 } else {
677                     buf[n++] = HANGUL_CHOSEONG_FILLER;
678                     buf[n++] = HANGUL_JUNGSEONG_FILLER;
679                     buf[n++] = jong;
680                 }
681             } else {
682                 /* have nothing */
683                 buf[n] = 0;
684             }
685         }
686     }
687     buf[n] = 0;
688
689     return n;
690 }
691
692 static int
693 hangul_buffer_get_string(HangulBuffer *buffer, ucschar *buf, int buflen)
694 {
695     return hangul_jaso_to_string(buffer->choseong,
696                                  buffer->jungseong,
697                                  buffer->jongseong,
698                                  buf, buflen);
699 }
700
701 static bool
702 hangul_buffer_backspace(HangulBuffer *buffer)
703 {
704     if (buffer->index >= 0) {
705         ucschar ch = hangul_buffer_pop(buffer);
706         if (ch == 0)
707             return false;
708
709         if (buffer->index >= 0) {
710             if (hangul_is_choseong(ch)) {
711                 ch = hangul_buffer_peek(buffer);
712                 buffer->choseong = hangul_is_choseong(ch) ? ch : 0;
713                 return true;
714             } else if (hangul_is_jungseong(ch)) {
715                 ch = hangul_buffer_peek(buffer);
716                 buffer->jungseong = hangul_is_jungseong(ch) ? ch : 0;
717                 return true;
718             } else if (hangul_is_jongseong(ch)) {
719                 ch = hangul_buffer_peek(buffer);
720                 buffer->jongseong = hangul_is_jongseong(ch) ? ch : 0;
721                 return true;
722             }
723         } else {
724             buffer->choseong = 0;
725             buffer->jungseong = 0;
726             buffer->jongseong = 0;
727             return true;
728         }
729     }
730     return false;
731 }
732
733 static inline bool
734 hangul_ic_push(HangulInputContext *hic, ucschar c)
735 {
736     ucschar buf[64] = { 0, };
737     if (hic->on_transition != NULL) {
738         ucschar cho, jung, jong;
739         if (hangul_is_choseong(c)) {
740             cho  = c;
741             jung = hic->buffer.jungseong;
742             jong = hic->buffer.jongseong;
743         } else if (hangul_is_jungseong(c)) {
744             cho  = hic->buffer.choseong;
745             jung = c;
746             jong = hic->buffer.jongseong;
747         } else if (hangul_is_jongseong(c)) {
748             cho  = hic->buffer.choseong;
749             jung = hic->buffer.jungseong;
750             jong = c;
751         } else {
752             hangul_ic_flush_internal(hic);
753             return false;
754         }
755
756         hangul_jaso_to_string(cho, jung, jong, buf, N_ELEMENTS(buf));
757         if (!hic->on_transition(hic, c, buf, hic->on_transition_data)) {
758             hangul_ic_flush_internal(hic);
759             return false;
760         }
761     } else {
762         if (!hangul_is_jamo(c)) {
763             hangul_ic_flush_internal(hic);
764             return false;
765         }
766     }
767
768     hangul_buffer_push(&hic->buffer, c);
769     return true;
770 }
771
772 static inline ucschar
773 hangul_ic_pop(HangulInputContext *hic)
774 {
775     return hangul_buffer_pop(&hic->buffer);
776 }
777
778 static inline ucschar
779 hangul_ic_peek(HangulInputContext *hic)
780 {
781     return hangul_buffer_peek(&hic->buffer);
782 }
783
784 static inline void
785 hangul_ic_save_preedit_string(HangulInputContext *hic)
786 {
787     if (hic->output_mode == HANGUL_OUTPUT_JAMO) {
788         hangul_buffer_get_jamo_string(&hic->buffer,
789                                       hic->preedit_string,
790                                       N_ELEMENTS(hic->preedit_string));
791     } else {
792         hangul_buffer_get_string(&hic->buffer,
793                                  hic->preedit_string,
794                                  N_ELEMENTS(hic->preedit_string));
795     }
796 }
797
798 static inline void
799 hangul_ic_append_commit_string(HangulInputContext *hic, ucschar ch)
800 {
801     int i;
802
803     for (i = 0; i < N_ELEMENTS(hic->commit_string); i++) {
804         if (hic->commit_string[i] == 0)
805             break;
806     }
807
808     if (i + 1 < N_ELEMENTS(hic->commit_string)) {
809         hic->commit_string[i++] = ch;
810         hic->commit_string[i] = 0;
811     }
812 }
813
814 static inline void
815 hangul_ic_save_commit_string(HangulInputContext *hic)
816 {
817     ucschar *string = hic->commit_string;
818     int len = N_ELEMENTS(hic->commit_string);
819
820     while (len > 0) {
821         if (*string == 0)
822             break;
823         len--;
824         string++;
825     }
826
827     if (hic->output_mode == HANGUL_OUTPUT_JAMO) {
828         hangul_buffer_get_jamo_string(&hic->buffer, string, len);
829     } else {
830         hangul_buffer_get_string(&hic->buffer, string, len);
831     }
832
833     hangul_buffer_clear(&hic->buffer);
834 }
835
836 static ucschar
837 hangul_ic_choseong_to_jongseong(HangulInputContext* hic, ucschar cho)
838 {
839     ucschar jong = hangul_choseong_to_jongseong(cho);
840     if (hangul_is_jongseong_conjoinable(jong)) {
841         return jong;
842     } else {
843         /* 옛글 조합 규칙을 사용하는 자판의 경우에는 종성이 conjoinable
844          * 하지 않아도 상관없다 */
845         if (hic->keyboard->combination == &hangul_combination_full) {
846             return jong;
847         }
848     }
849
850     return 0;
851 }
852
853 static bool
854 hangul_ic_process_jamo(HangulInputContext *hic, ucschar ch)
855 {
856     ucschar jong;
857     ucschar combined;
858
859     if (!hangul_is_jamo(ch) && ch > 0) {
860         hangul_ic_save_commit_string(hic);
861         hangul_ic_append_commit_string(hic, ch);
862         return true;
863     }
864
865     if (hic->buffer.jongseong) {
866         if (hangul_is_choseong(ch)) {
867             jong = hangul_ic_choseong_to_jongseong(hic, ch);
868             combined = hangul_combination_combine(hic->keyboard->combination,
869                                               hic->buffer.jongseong, jong);
870             if (hangul_is_jongseong(combined)) {
871                 if (!hangul_ic_push(hic, combined)) {
872                     if (!hangul_ic_push(hic, ch)) {
873                         return false;
874                     }
875                 }
876             } else {
877                 hangul_ic_save_commit_string(hic);
878                 if (!hangul_ic_push(hic, ch)) {
879                     return false;
880                 }
881             }
882         } else if (hangul_is_jungseong(ch)) {
883             ucschar pop, peek;
884             pop = hangul_ic_pop(hic);
885             peek = hangul_ic_peek(hic);
886
887             if (hangul_is_jongseong(peek)) {
888                 ucschar choseong = hangul_jongseong_get_diff(peek,
889                                                  hic->buffer.jongseong);
890                 if (choseong == 0) {
891                     hangul_ic_save_commit_string(hic);
892                     if (!hangul_ic_push(hic, ch)) {
893                         return false;
894                     }
895                 } else {
896                     hic->buffer.jongseong = peek;
897                     hangul_ic_save_commit_string(hic);
898                     hangul_ic_push(hic, choseong);
899                     if (!hangul_ic_push(hic, ch)) {
900                         return false;
901                     }
902                 }
903             } else {
904                 hic->buffer.jongseong = 0;
905                 hangul_ic_save_commit_string(hic);
906                 hangul_ic_push(hic, hangul_jongseong_to_choseong(pop));
907                 if (!hangul_ic_push(hic, ch)) {
908                     return false;
909                 }
910             }
911         } else {
912             goto flush;
913         }
914     } else if (hic->buffer.jungseong) {
915         if (hangul_is_choseong(ch)) {
916             if (hic->buffer.choseong) {
917                 jong = hangul_ic_choseong_to_jongseong(hic, ch);
918                 if (hangul_is_jongseong(jong)) {
919                     if (!hangul_ic_push(hic, jong)) {
920                         if (!hangul_ic_push(hic, ch)) {
921                             return false;
922                         }
923                     }
924                 } else {
925                     hangul_ic_save_commit_string(hic);
926                     if (!hangul_ic_push(hic, ch)) {
927                         return false;
928                     }
929                 }
930             } else {
931                 if (!hangul_ic_push(hic, ch)) {
932                     if (!hangul_ic_push(hic, ch)) {
933                         return false;
934                     }
935                 }
936             }
937         } else if (hangul_is_jungseong(ch)) {
938             combined = hangul_combination_combine(hic->keyboard->combination,
939                                                   hic->buffer.jungseong, ch);
940             if (hangul_is_jungseong(combined)) {
941                 if (!hangul_ic_push(hic, combined)) {
942                     return false;
943                 }
944             } else {
945                 hangul_ic_save_commit_string(hic);
946                 if (!hangul_ic_push(hic, ch)) {
947                     return false;
948                 }
949             }
950         } else {
951             goto flush;
952         }
953     } else if (hic->buffer.choseong) {
954         if (hangul_is_choseong(ch)) {
955             combined = hangul_combination_combine(hic->keyboard->combination,
956                                                   hic->buffer.choseong, ch);
957             if (!hangul_ic_push(hic, combined)) {
958                 if (!hangul_ic_push(hic, ch)) {
959                     return false;
960                 }
961             }
962         } else {
963             if (!hangul_ic_push(hic, ch)) {
964                 if (!hangul_ic_push(hic, ch)) {
965                     return false;
966                 }
967             }
968         }
969     } else {
970         if (!hangul_ic_push(hic, ch)) {
971             return false;
972         }
973     }
974
975     hangul_ic_save_preedit_string(hic);
976     return true;
977
978 flush:
979     hangul_ic_flush_internal(hic);
980     return false;
981 }
982
983 static bool
984 hangul_ic_process_jaso(HangulInputContext *hic, ucschar ch)
985 {
986     if (hangul_is_choseong(ch)) {
987         if (hic->buffer.choseong == 0) {
988             if (!hangul_ic_push(hic, ch)) {
989                 if (!hangul_ic_push(hic, ch)) {
990                     return false;
991                 }
992             }
993         } else {
994             ucschar choseong = 0;
995             if (hangul_is_choseong(hangul_ic_peek(hic))) {
996                 choseong = hangul_combination_combine(hic->keyboard->combination,
997                                                   hic->buffer.choseong, ch);
998             }
999             if (choseong) {
1000                 if (!hangul_ic_push(hic, choseong)) {
1001                     if (!hangul_ic_push(hic, choseong)) {
1002                         return false;
1003                     }
1004                 }
1005             } else {
1006                 hangul_ic_save_commit_string(hic);
1007                 if (!hangul_ic_push(hic, ch)) {
1008                     return false;
1009                 }
1010             }
1011         }
1012     } else if (hangul_is_jungseong(ch)) {
1013         if (hic->buffer.jungseong == 0) {
1014             if (!hangul_ic_push(hic, ch)) {
1015                 if (!hangul_ic_push(hic, ch)) {
1016                     return false;
1017                 }
1018             }
1019         } else {
1020             ucschar jungseong = 0;
1021             if (hangul_is_jungseong(hangul_ic_peek(hic))) {
1022                 jungseong = hangul_combination_combine(hic->keyboard->combination,
1023                                                  hic->buffer.jungseong, ch);
1024             }
1025             if (jungseong) {
1026                 if (!hangul_ic_push(hic, jungseong)) {
1027                     if (!hangul_ic_push(hic, jungseong)) {
1028                         return false;
1029                     }
1030                 }
1031             } else {
1032                 hangul_ic_save_commit_string(hic);
1033                 if (!hangul_ic_push(hic, ch)) {
1034                     if (!hangul_ic_push(hic, ch)) {
1035                         return false;
1036                     }
1037                 }
1038             }
1039         }
1040     } else if (hangul_is_jongseong(ch)) {
1041         if (hic->buffer.jongseong == 0) {
1042             if (!hangul_ic_push(hic, ch)) {
1043                 if (!hangul_ic_push(hic, ch)) {
1044                     return false;
1045                 }
1046             }
1047         } else {
1048             ucschar jongseong = 0;
1049             if (hangul_is_jongseong(hangul_ic_peek(hic))) {
1050                 jongseong = hangul_combination_combine(hic->keyboard->combination,
1051                                                    hic->buffer.jongseong, ch);
1052             }
1053             if (jongseong) {
1054                 if (!hangul_ic_push(hic, jongseong)) {
1055                     if (!hangul_ic_push(hic, jongseong)) {
1056                         return false;
1057                     }
1058                 }
1059             } else {
1060                 hangul_ic_save_commit_string(hic);
1061                 if (!hangul_ic_push(hic, ch)) {
1062                     if (!hangul_ic_push(hic, ch)) {
1063                         return false;
1064                     }
1065                 }
1066             }
1067         }
1068     } else if (ch > 0) {
1069         hangul_ic_save_commit_string(hic);
1070         hangul_ic_append_commit_string(hic, ch);
1071     } else {
1072         hangul_ic_save_commit_string(hic);
1073         return false;
1074     }
1075
1076     hangul_ic_save_preedit_string(hic);
1077     return true;
1078 }
1079
1080 static bool
1081 hangul_ic_process_romaja(HangulInputContext *hic, int ascii, ucschar ch)
1082 {
1083     ucschar jong;
1084     ucschar combined;
1085
1086     if (!hangul_is_jamo(ch) && ch > 0) {
1087         hangul_ic_save_commit_string(hic);
1088         hangul_ic_append_commit_string(hic, ch);
1089         return true;
1090     }
1091
1092     if (isupper(ascii)) {
1093         hangul_ic_save_commit_string(hic);
1094     }
1095
1096     if (hic->buffer.jongseong) {
1097         if (ascii == 'x' || ascii == 'X') {
1098             ch = 0x110c;
1099             hangul_ic_save_commit_string(hic);
1100             if (!hangul_ic_push(hic, ch)) {
1101                 return false;
1102             }
1103         } else if (hangul_is_choseong(ch) || hangul_is_jongseong(ch)) {
1104             if (hangul_is_jongseong(ch))
1105                 jong = ch;
1106             else
1107                 jong = hangul_ic_choseong_to_jongseong(hic, ch);
1108             combined = hangul_combination_combine(hic->keyboard->combination,
1109                                               hic->buffer.jongseong, jong);
1110             if (hangul_is_jongseong(combined)) {
1111                 if (!hangul_ic_push(hic, combined)) {
1112                     if (!hangul_ic_push(hic, ch)) {
1113                         return false;
1114                     }
1115                 }
1116             } else {
1117                 hangul_ic_save_commit_string(hic);
1118                 if (!hangul_ic_push(hic, ch)) {
1119                     return false;
1120                 }
1121             }
1122         } else if (hangul_is_jungseong(ch)) {
1123             if (hic->buffer.jongseong == 0x11bc) {
1124                 hangul_ic_save_commit_string(hic);
1125                 hic->buffer.choseong = 0x110b;
1126                 hangul_ic_push(hic, ch);
1127             } else {
1128                 ucschar pop, peek;
1129                 pop = hangul_ic_pop(hic);
1130                 peek = hangul_ic_peek(hic);
1131
1132                 if (hangul_is_jungseong(peek)) {
1133                     if (pop == 0x11aa) {
1134                         hic->buffer.jongseong = 0x11a8;
1135                         pop = 0x11ba;
1136                     } else {
1137                         hic->buffer.jongseong = 0;
1138                     }
1139                     hangul_ic_save_commit_string(hic);
1140                     hangul_ic_push(hic, hangul_jongseong_to_choseong(pop));
1141                     if (!hangul_ic_push(hic, ch)) {
1142                         return false;
1143                     }
1144                 } else {
1145                     ucschar choseong = 0, jongseong = 0; 
1146                     hangul_jongseong_decompose(hic->buffer.jongseong,
1147                                                &jongseong, &choseong);
1148                     hic->buffer.jongseong = jongseong;
1149                     hangul_ic_save_commit_string(hic);
1150                     hangul_ic_push(hic, choseong);
1151                     if (!hangul_ic_push(hic, ch)) {
1152                         return false;
1153                     }
1154                 }
1155             }
1156         } else {
1157             goto flush;
1158         }
1159     } else if (hic->buffer.jungseong) {
1160         if (hangul_is_choseong(ch)) {
1161             if (hic->buffer.choseong) {
1162                 jong = hangul_ic_choseong_to_jongseong(hic, ch);
1163                 if (hangul_is_jongseong(jong)) {
1164                     if (!hangul_ic_push(hic, jong)) {
1165                         if (!hangul_ic_push(hic, ch)) {
1166                             return false;
1167                         }
1168                     }
1169                 } else {
1170                     hangul_ic_save_commit_string(hic);
1171                     if (!hangul_ic_push(hic, ch)) {
1172                         return false;
1173                     }
1174                 }
1175             } else {
1176                 if (!hangul_ic_push(hic, ch)) {
1177                     if (!hangul_ic_push(hic, ch)) {
1178                         return false;
1179                     }
1180                 }
1181             }
1182         } else if (hangul_is_jungseong(ch)) {
1183             combined = hangul_combination_combine(hic->keyboard->combination,
1184                                                   hic->buffer.jungseong, ch);
1185             if (hangul_is_jungseong(combined)) {
1186                 if (!hangul_ic_push(hic, combined)) {
1187                     return false;
1188                 }
1189             } else {
1190                 hangul_ic_save_commit_string(hic);
1191                 hic->buffer.choseong = 0x110b;
1192                 if (!hangul_ic_push(hic, ch)) {
1193                     return false;
1194                 }
1195             }
1196         } else if (hangul_is_jongseong(ch)) {
1197             if (!hangul_ic_push(hic, ch)) {
1198                 if (!hangul_ic_push(hic, ch)) {
1199                     return false;
1200                 }
1201             }
1202         } else {
1203             goto flush;
1204         }
1205     } else if (hic->buffer.choseong) {
1206         if (hangul_is_choseong(ch)) {
1207             combined = hangul_combination_combine(hic->keyboard->combination,
1208                                                   hic->buffer.choseong, ch);
1209             if (combined == 0) {
1210                 hic->buffer.jungseong = 0x1173;
1211                 hangul_ic_flush_internal(hic);
1212                 if (!hangul_ic_push(hic, ch)) {
1213                     return false;
1214                 }
1215             } else {
1216                 if (!hangul_ic_push(hic, combined)) {
1217                     if (!hangul_ic_push(hic, ch)) {
1218                         return false;
1219                     }
1220                 }
1221             }
1222         } else if (hangul_is_jongseong(ch)) {
1223             hic->buffer.jungseong = 0x1173;
1224             hangul_ic_save_commit_string(hic);
1225             if (ascii == 'x' || ascii == 'X')
1226                 ch = 0x110c;
1227             if (!hangul_ic_push(hic, ch)) {
1228                 return false;
1229             }
1230         } else {
1231             if (!hangul_ic_push(hic, ch)) {
1232                 if (!hangul_ic_push(hic, ch)) {
1233                     return false;
1234                 }
1235             }
1236         }
1237     } else {
1238         if (ascii == 'x' || ascii == 'X') {
1239             ch = 0x110c;
1240         }
1241
1242         if (!hangul_ic_push(hic, ch)) {
1243             return false;
1244         } else {
1245             if (hic->buffer.choseong == 0 && hic->buffer.jungseong != 0)
1246                 hic->buffer.choseong = 0x110b;
1247         }
1248     }
1249
1250     hangul_ic_save_preedit_string(hic);
1251     return true;
1252
1253 flush:
1254     hangul_ic_flush_internal(hic);
1255     return false;
1256 }
1257
1258 /**
1259  * @ingroup hangulic
1260  * @brief 키 입력을 처리하여 실제로 한글 조합을 하는 함수
1261  * @param hic @ref HangulInputContext 오브젝트
1262  * @param ascii 키 이벤트
1263  * @return @ref HangulInputContext 가 이 키를 사용했으면 true,
1264  *           사용하지 않았으면 false
1265  *
1266  * ascii 값으로 주어진 키 이벤트를 받아서 내부의 한글 조합 상태를
1267  * 변화시키고, preedit, commit 스트링을 저장한다.
1268  *
1269  * libhangul의 키 이벤트 프로세스는 ASCII 코드 값을 기준으로 처리한다.
1270  * 이 키 값은 US Qwerty 자판 배열에서의 키 값에 해당한다.
1271  * 따라서 유럽어 자판을 사용하는 경우에는 해당 키의 ASCII 코드를 직접
1272  * 전달하면 안되고, 그 키가 US Qwerty 자판이었을 경우에 발생할 수 있는 
1273  * ASCII 코드 값을 주어야 한다.
1274  * 또한 ASCII 코드 이므로 Shift 상태는 대문자로 전달이 된다.
1275  * Capslock이 눌린 경우에는 대소문자를 뒤바꾸어 보내주지 않으면 
1276  * 마치 Shift가 눌린 것 처럼 동작할 수 있으므로 주의한다.
1277  * preedit, commit 스트링은 hangul_ic_get_preedit_string(),
1278  * hangul_ic_get_commit_string() 함수를 이용하여 구할 수 있다.
1279  * 
1280  * 이 함수의 사용법에 대한 설명은 @ref hangulicusage 부분을 참조한다.
1281  *
1282  * @remarks 이 함수는 @ref HangulInputContext 의 상태를 변화 시킨다.
1283  */
1284 bool
1285 hangul_ic_process(HangulInputContext *hic, int ascii)
1286 {
1287     ucschar c;
1288
1289     if (hic == NULL)
1290         return false;
1291
1292     hic->preedit_string[0] = 0;
1293     hic->commit_string[0] = 0;
1294
1295     c = hangul_keyboard_get_value(hic->keyboard, ascii);
1296     if (hic->on_translate != NULL)
1297         hic->on_translate(hic, ascii, &c, hic->on_translate_data);
1298
1299     if (hangul_keyboard_get_type(hic->keyboard) == HANGUL_KEYBOARD_TYPE_JAMO)
1300         return hangul_ic_process_jamo(hic, c);
1301     else if (hangul_keyboard_get_type(hic->keyboard) == HANGUL_KEYBOARD_TYPE_JASO)
1302         return hangul_ic_process_jaso(hic, c);
1303     else
1304         return hangul_ic_process_romaja(hic, ascii, c);
1305 }
1306
1307 /**
1308  * @ingroup hangulic
1309  * @brief 현재 상태의 preedit string을 구하는 함수
1310  * @param hic preedit string을 구하고자하는 입력 상태 object
1311  * @return UCS4 preedit 스트링, 이 스트링은 @a hic 내부의 데이터이므로 
1312  *         수정하거나 free해서는 안된다.
1313  * 
1314  * 이 함수는  @a hic 내부의 현재 상태의 preedit string을 리턴한다.
1315  * 따라서 hic가 다른 키 이벤트를 처리하고 나면 그 내용이 바뀔 수 있다.
1316  * 
1317  * @remarks 이 함수는 @ref HangulInputContext 의 상태를 변화 시키지 않는다.
1318  */
1319 const ucschar*
1320 hangul_ic_get_preedit_string(HangulInputContext *hic)
1321 {
1322     if (hic == NULL)
1323         return NULL;
1324
1325     return hic->preedit_string;
1326 }
1327
1328 /**
1329  * @ingroup hangulic
1330  * @brief 현재 상태의 commit string을 구하는 함수
1331  * @param hic commit string을 구하고자하는 입력 상태 object
1332  * @return UCS4 commit 스트링, 이 스트링은 @a hic 내부의 데이터이므로 
1333  *         수정하거나 free해서는 안된다.
1334  * 
1335  * 이 함수는  @a hic 내부의 현재 상태의 commit string을 리턴한다.
1336  * 따라서 hic가 다른 키 이벤트를 처리하고 나면 그 내용이 바뀔 수 있다.
1337  *
1338  * @remarks 이 함수는 @ref HangulInputContext 의 상태를 변화 시키지 않는다.
1339  */
1340 const ucschar*
1341 hangul_ic_get_commit_string(HangulInputContext *hic)
1342 {
1343     if (hic == NULL)
1344         return NULL;
1345
1346     return hic->commit_string;
1347 }
1348
1349 /**
1350  * @ingroup hangulic
1351  * @brief @ref HangulInputContext 를 초기상태로 되돌리는 함수
1352  * @param hic @ref HangulInputContext 를 가리키는 포인터
1353  * 
1354  * 이 함수는 @a hic가 가리키는 @ref HangulInputContext 의 상태를 
1355  * 처음 상태로 되돌린다. preedit 스트링, commit 스트링, flush 스트링이
1356  * 없어지고, 입력되었던 키에 대한 기록이 없어진다.
1357  * 영어 상태로 바뀌는 것이 아니다.
1358  *
1359  * 비교: hangul_ic_flush()
1360  *
1361  * @remarks 이 함수는 @ref HangulInputContext 의 상태를 변화 시킨다.
1362  */
1363 void
1364 hangul_ic_reset(HangulInputContext *hic)
1365 {
1366     if (hic == NULL)
1367         return;
1368
1369     hic->preedit_string[0] = 0;
1370     hic->commit_string[0] = 0;
1371     hic->flushed_string[0] = 0;
1372
1373     hangul_buffer_clear(&hic->buffer);
1374 }
1375
1376 /* append current preedit to the commit buffer.
1377  * this function does not clear previously made commit string. */
1378 static void
1379 hangul_ic_flush_internal(HangulInputContext *hic)
1380 {
1381     hic->preedit_string[0] = 0;
1382
1383     hangul_ic_save_commit_string(hic);
1384     hangul_buffer_clear(&hic->buffer);
1385 }
1386
1387 /**
1388  * @ingroup hangulic
1389  * @brief @ref HangulInputContext 의 입력 상태를 완료하는 함수
1390  * @param hic @ref HangulInputContext 를 가리키는 포인터
1391  * @return 조합 완료된 스트링, 스트링의 길이가 0이면 조합 완료된 스트링이 
1392  *        없는 것
1393  *
1394  * 이 함수는 @a hic가 가리키는 @ref HangulInputContext 의 입력 상태를 완료한다.
1395  * 조합중이던 스트링을 완성하여 리턴한다. 그리고 입력 상태가 초기 상태로 
1396  * 되돌아 간다. 조합중이던 글자를 강제로 commit하고 싶을때 사용하는 함수다.
1397  * 보통의 경우 입력 framework에서 focus가 나갈때 이 함수를 불러서 마지막 
1398  * 상태를 완료해야 조합중이던 글자를 잃어버리지 않게 된다.
1399  *
1400  * 비교: hangul_ic_reset()
1401  *
1402  * @remarks 이 함수는 @ref HangulInputContext 의 상태를 변화 시킨다.
1403  */
1404 const ucschar*
1405 hangul_ic_flush(HangulInputContext *hic)
1406 {
1407     if (hic == NULL)
1408         return NULL;
1409
1410     // get the remaining string and clear the buffer
1411     hic->preedit_string[0] = 0;
1412     hic->commit_string[0] = 0;
1413     hic->flushed_string[0] = 0;
1414
1415     if (hic->output_mode == HANGUL_OUTPUT_JAMO) {
1416         hangul_buffer_get_jamo_string(&hic->buffer, hic->flushed_string,
1417                                  N_ELEMENTS(hic->flushed_string));
1418     } else {
1419         hangul_buffer_get_string(&hic->buffer, hic->flushed_string,
1420                                  N_ELEMENTS(hic->flushed_string));
1421     }
1422
1423     hangul_buffer_clear(&hic->buffer);
1424
1425     return hic->flushed_string;
1426 }
1427
1428 /**
1429  * @ingroup hangulic
1430  * @brief @ref HangulInputContext 가 backspace 키를 처리하도록 하는 함수
1431  * @param hic @ref HangulInputContext 를 가리키는 포인터
1432  * @return @a hic가 키를 사용했으면 true, 사용하지 않았으면 false
1433  * 
1434  * 이 함수는 @a hic가 가리키는 @ref HangulInputContext 의 조합중이던 글자를
1435  * 뒤에서부터 하나 지우는 기능을 한다. backspace 키를 눌렀을 때 발생하는 
1436  * 동작을 한다. 따라서 이 함수를 부르고 나면 preedit string이 바뀌므로
1437  * 반드시 업데이트를 해야 한다.
1438  *
1439  * @remarks 이 함수는 @ref HangulInputContext 의 상태를 변화 시킨다.
1440  */
1441 bool
1442 hangul_ic_backspace(HangulInputContext *hic)
1443 {
1444     int ret;
1445
1446     if (hic == NULL)
1447         return false;
1448
1449     hic->preedit_string[0] = 0;
1450     hic->commit_string[0] = 0;
1451
1452     ret = hangul_buffer_backspace(&hic->buffer);
1453     if (ret)
1454         hangul_ic_save_preedit_string(hic);
1455     return ret;
1456 }
1457
1458 /**
1459  * @ingroup hangulic
1460  * @brief @ref HangulInputContext 가 조합중인 글자를 가지고 있는지 확인하는 함수
1461  * @param hic @ref HangulInputContext 를 가리키는 포인터
1462  *
1463  * @ref HangulInputContext 가 조합중인 글자가 있으면 true를 리턴한다.
1464  *
1465  * @remarks 이 함수는 @ref HangulInputContext 의 상태를 변화 시키지 않는다.
1466  */
1467 bool
1468 hangul_ic_is_empty(HangulInputContext *hic)
1469 {
1470     return hangul_buffer_is_empty(&hic->buffer);
1471 }
1472
1473 /**
1474  * @ingroup hangulic
1475  * @brief @ref HangulInputContext 가 조합중인 초성을 가지고 있는지 확인하는 함수
1476  * @param hic @ref HangulInputContext 를 가리키는 포인터
1477  *
1478  * @ref HangulInputContext 가 조합중인 글자가 초성이 있으면 true를 리턴한다.
1479  *
1480  * @remarks 이 함수는 @ref HangulInputContext 의 상태를 변화 시키지 않는다.
1481  */
1482 bool
1483 hangul_ic_has_choseong(HangulInputContext *hic)
1484 {
1485     return hangul_buffer_has_choseong(&hic->buffer);
1486 }
1487
1488 /**
1489  * @ingroup hangulic
1490  * @brief @ref HangulInputContext 가 조합중인 중성을 가지고 있는지 확인하는 함수
1491  * @param hic @ref HangulInputContext 를 가리키는 포인터
1492  *
1493  * @ref HangulInputContext 가 조합중인 글자가 중성이 있으면 true를 리턴한다.
1494  *
1495  * @remarks 이 함수는 @ref HangulInputContext 의 상태를 변화 시키지 않는다.
1496  */
1497 bool
1498 hangul_ic_has_jungseong(HangulInputContext *hic)
1499 {
1500     return hangul_buffer_has_jungseong(&hic->buffer);
1501 }
1502
1503 /**
1504  * @ingroup hangulic
1505  * @brief @ref HangulInputContext 가 조합중인 종성을 가지고 있는지 확인하는 함수
1506  * @param hic @ref HangulInputContext 를 가리키는 포인터
1507  *
1508  * @ref HangulInputContext 가 조합중인 글자가 종성이 있으면 true를 리턴한다.
1509  *
1510  * @remarks 이 함수는 @ref HangulInputContext 의 상태를 변화 시키지 않는다.
1511  */
1512 bool
1513 hangul_ic_has_jongseong(HangulInputContext *hic)
1514 {
1515     return hangul_buffer_has_jongseong(&hic->buffer);
1516 }
1517
1518 void
1519 hangul_ic_set_output_mode(HangulInputContext *hic, int mode)
1520 {
1521     if (hic == NULL)
1522         return;
1523
1524     if (!hic->use_jamo_mode_only)
1525         hic->output_mode = mode;
1526 }
1527
1528 void
1529 hangul_ic_connect_translate (HangulInputContext* hic,
1530                              HangulOnTranslate callback,
1531                              void* user_data)
1532 {
1533     if (hic != NULL) {
1534         hic->on_translate      = callback;
1535         hic->on_translate_data = user_data;
1536     }
1537 }
1538
1539 void
1540 hangul_ic_connect_transition(HangulInputContext* hic,
1541                              HangulOnTransition callback,
1542                              void* user_data)
1543 {
1544     if (hic != NULL) {
1545         hic->on_transition      = callback;
1546         hic->on_transition_data = user_data;
1547     }
1548 }
1549
1550 void hangul_ic_connect_callback(HangulInputContext* hic, const char* event,
1551                                 void* callback, void* user_data)
1552 {
1553     if (hic == NULL || event == NULL)
1554         return;
1555
1556     if (strcasecmp(event, "translate") == 0) {
1557         hic->on_translate      = (HangulOnTranslate)callback;
1558         hic->on_translate_data = user_data;
1559     } else if (strcasecmp(event, "transition") == 0) {
1560         hic->on_transition      = (HangulOnTransition)callback;
1561         hic->on_transition_data = user_data;
1562     }
1563 }
1564
1565 void
1566 hangul_ic_set_keyboard(HangulInputContext *hic, const HangulKeyboard* keyboard)
1567 {
1568     if (hic == NULL || keyboard == NULL)
1569         return;
1570
1571     hic->keyboard = keyboard;
1572 }
1573
1574 static const HangulKeyboard*
1575 hangul_ic_get_keyboard_by_id(const char* id)
1576 {
1577     unsigned i;
1578     unsigned n;
1579
1580     /* hangul_keyboards 테이블은 id 순으로 정렬되어 있지 않으므로
1581      * binary search를 할수 없고 linear search를 한다. */
1582     n = hangul_ic_get_n_keyboards();
1583     for (i = 0; i < n; ++i) {
1584         const HangulKeyboard* keyboard = hangul_keyboards[i];
1585         if (strcmp(id, keyboard->id) == 0) {
1586             return keyboard;
1587         }
1588     }
1589
1590     return NULL;
1591 }
1592
1593 /**
1594  * @ingroup hangulic
1595  * @brief @ref HangulInputContext 의 자판 배열을 바꾸는 함수
1596  * @param hic @ref HangulInputContext 오브젝트
1597  * @param id 선택하고자 하는 자판, 아래와 같은 값을 선택할 수 있다.
1598  *   @li "2"   @ref layout_2 자판
1599  *   @li "2y"  @ref layout_2y 자판
1600  *   @li "3f"  @ref layout_3f 자판
1601  *   @li "39"  @ref layout_390 자판
1602  *   @li "3s"  @ref layout_3s 자판
1603  *   @li "3y"  @ref layout_3y 자판
1604  *   @li "32"  @ref layout_32 자판
1605  *   @li "ro"  @ref layout_ro 자판
1606  *
1607  *   libhangul이 지원하는 자판에 대한 정보는 @ref hangulkeyboards 페이지를
1608  *   참조하라.
1609  * @return 없음
1610  * 
1611  * 이 함수는 @ref HangulInputContext 의 자판을 @a id로 지정된 것으로 변경한다.
1612  * 
1613  * @remarks 이 함수는 @ref HangulInputContext 의 내부 조합 상태에는 영향을
1614  * 미치지 않는다.  따라서 입력 중간에 자판을 변경하더라도 조합 상태는 유지된다.
1615  */
1616 void
1617 hangul_ic_select_keyboard(HangulInputContext *hic, const char* id)
1618 {
1619     const HangulKeyboard* keyboard;
1620
1621     if (hic == NULL)
1622         return;
1623
1624     if (id == NULL)
1625         id = "2";
1626
1627     keyboard = hangul_ic_get_keyboard_by_id(id);
1628     if (keyboard != NULL) {
1629         hic->keyboard = keyboard;
1630     } else {
1631         hic->keyboard = &hangul_keyboard_2;
1632     }
1633 }
1634
1635 void
1636 hangul_ic_set_combination(HangulInputContext *hic,
1637                           const HangulCombination* combination)
1638 {
1639 }
1640
1641 /**
1642  * @ingroup hangulic
1643  * @brief @ref HangulInputContext 오브젝트를 생성한다.
1644  * @param keyboard 사용하고자 하는 키보드, 사용 가능한 값에 대해서는
1645  *      hangul_ic_select_keyboard() 함수 설명을 참조한다.
1646  * @return 새로 생성된 @ref HangulInputContext 에 대한 포인터
1647  * 
1648  * 이 함수는 한글 조합 기능을 제공하는 @ref HangulInputContext 오브젝트를 
1649  * 생성한다. 생성할때 지정한 자판은 나중에 hangul_ic_select_keyboard() 함수로
1650  * 다른 자판으로 변경이 가능하다.
1651  * 더이상 사용하지 않을 때에는 hangul_ic_delete() 함수로 삭제해야 한다.
1652  */
1653 HangulInputContext*
1654 hangul_ic_new(const char* keyboard)
1655 {
1656     HangulInputContext *hic;
1657
1658     hic = malloc(sizeof(HangulInputContext));
1659     if (hic == NULL)
1660         return NULL;
1661
1662     hic->preedit_string[0] = 0;
1663     hic->commit_string[0] = 0;
1664     hic->flushed_string[0] = 0;
1665
1666     hic->on_translate      = NULL;
1667     hic->on_translate_data = NULL;
1668
1669     hic->on_transition      = NULL;
1670     hic->on_transition_data = NULL;
1671
1672     hic->use_jamo_mode_only = FALSE;
1673
1674     hangul_ic_set_output_mode(hic, HANGUL_OUTPUT_SYLLABLE);
1675     hangul_ic_select_keyboard(hic, keyboard);
1676
1677     hangul_buffer_clear(&hic->buffer);
1678
1679     return hic;
1680 }
1681
1682 /**
1683  * @ingroup hangulic
1684  * @brief @ref HangulInputContext 를 삭제하는 함수
1685  * @param hic @ref HangulInputContext 오브젝트
1686  * 
1687  * @a hic가 가리키는 @ref HangulInputContext 오브젝트의 메모리를 해제한다.
1688  * hangul_ic_new() 함수로 생성된 모든 @ref HangulInputContext 오브젝트는
1689  * 이 함수로 메모리해제를 해야 한다.
1690  * 메모리 해제 과정에서 상태 변화는 일어나지 않으므로 마지막 입력된 
1691  * 조합중이던 내용은 사라지게 된다.
1692  */
1693 void
1694 hangul_ic_delete(HangulInputContext *hic)
1695 {
1696     if (hic == NULL)
1697         return;
1698
1699     free(hic);
1700 }
1701
1702 unsigned int
1703 hangul_ic_get_n_keyboards()
1704 {
1705     return N_ELEMENTS(hangul_keyboards);
1706 }
1707
1708 const char*
1709 hangul_ic_get_keyboard_id(unsigned index_)
1710 {
1711     if (index_ < N_ELEMENTS(hangul_keyboards)) {
1712         return hangul_keyboards[index_]->id;
1713     }
1714
1715     return NULL;
1716 }
1717
1718 const char*
1719 hangul_ic_get_keyboard_name(unsigned index_)
1720 {
1721 #ifdef ENABLE_NLS
1722     static bool isGettextInitialized = false;
1723     if (!isGettextInitialized) {
1724         isGettextInitialized = true;
1725         bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
1726         bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
1727     }
1728 #endif
1729
1730     if (index_ < N_ELEMENTS(hangul_keyboards)) {
1731         return _(hangul_keyboards[index_]->name);
1732     }
1733
1734     return NULL;
1735 }
1736
1737 /**
1738  * @ingroup hangulic
1739  * @brief 주어진 hic가 transliteration method인지 판별
1740  * @param hic 상태를 알고자 하는 HangulInputContext 포인터
1741  * @return hic가 transliteration method인 경우 true를 리턴, 아니면 false
1742  *
1743  * 이 함수는 @a hic 가 transliteration method인지 판별하는 함수다.
1744  * 이 함수가 false를 리턴할 경우에는 process 함수에 keycode를 넘기기 전에
1745  * 키보드 자판 배열에 독립적인 값으로 변환한 후 넘겨야 한다.
1746  * 그렇지 않으면 유럽어 자판과 한국어 자판을 같이 쓸때 한글 입력이 제대로
1747  * 되지 않는다.
1748  */
1749 bool
1750 hangul_ic_is_transliteration(HangulInputContext *hic)
1751 {
1752     int type;
1753
1754     if (hic == NULL)
1755         return false;
1756
1757     type = hangul_keyboard_get_type(hic->keyboard);
1758     if (type == HANGUL_KEYBOARD_TYPE_ROMAJA)
1759         return true;
1760
1761     return false;
1762 }