summaryrefslogtreecommitdiff
path: root/hangul/hangulinputcontext.c
diff options
context:
space:
mode:
Diffstat (limited to 'hangul/hangulinputcontext.c')
-rw-r--r--hangul/hangulinputcontext.c312
1 files changed, 311 insertions, 1 deletions
diff --git a/hangul/hangulinputcontext.c b/hangul/hangulinputcontext.c
index ac5da5b..1f0cdfc 100644
--- a/hangul/hangulinputcontext.c
+++ b/hangul/hangulinputcontext.c
@@ -12,7 +12,6 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -29,6 +28,143 @@
#include "hangul.h"
#include "hangulinternals.h"
+/**
+ * @defgroup hangulic 한글 입력 기능 구현
+ *
+ * @section hangulicusage Hangul Input Context의 사용법
+ * 이 섹션에서는 한글 입력 기능을 구현하는 핵심 기능에 대해 설명한다.
+ *
+ * 먼저 preedit string과 commit string 이 두 용어에 대해서 설멍하겠다.
+ * 이 두가지 용어는 Unix 계열의 입력기 framework에서 널리 쓰이는 표현이다.
+ *
+ * preedit string은 아직 조합중으로 어플리케이션에 완전히 입력되지 않은
+ * 스트링을 가리킨다. 일반적으로 한글 입력기에서는 역상으로 보이고
+ * 일본 중국어 입력기에서는 underline이 붙어 나타난다. 아직 완성이 되지
+ * 않은 스트링이므로 어플리케이션에 전달이 되지 않고 사라질 수도 있다.
+ *
+ * commit string은 조합이 완료되어 어플리케이션에 전달되는 스트링이다.
+ * 이 스트링은 실제 어플리케이션의 텍스트로 인식이 되므로 이 이후에는
+ * 더이상 입력기가 관리할 수 있는 데이터가 아니다.
+ *
+ * 한글 입력과정은 다음과 같은 과정을 거치게 된다.
+ * 입력된 영문 키를 그에 해댱하는 한글 자모로 변환한후 한글 자모를 모아
+ * 하나의 음절을 만든다. 여기까지 이루어지는 과정을 preedit string 형태로
+ * 사용자에게 계속 보이게 하는 것이 필요하다.
+ * 그리고는 한글 음절이 완성되고나면 그 글자를 어플리케이션에 commit
+ * string 형태로 보내여 입력을 완료하는 것이다. 다음 키를 받게 되면
+ * 이 과정을 반복해서 수행한다.
+ *
+ * libhangul에서 한글 조합 기능은 @ref HangulInputContext를 이용해서 구현하게
+ * 되는데 기본 적인 방법은 @ref HangulInputContext에 사용자로부터의 입력을
+ * 순서대로 전달하면서 그 상태가 바뀜에 따라서 preedit 나 commit 스트링을
+ * 상황에 맞게 변화시키는 것이다.
+ *
+ * 입력 코드들은 GUI 코드와 밀접하게 붙어 있어서 키 이벤트를 받아서
+ * 처리하도록 구현하는 것이 보통이다. 그런데 유닉스에는 많은 입력 프레임웍들이
+ * 난립하고 있는 상황이어서 매 입력 프레임웍마다 한글 조합 루틴을 작성해서
+ * 넣는 것은 비효율적이다. 간단한 API를 구현하여 여러 프레임웍에서 바로
+ * 사용할 수 있도록 구현하는 편이 사용성이 높아지게 된다.
+ *
+ * 그래서 libhangul에서는 키 이벤트를 따로 재정의하지 않고 ASCII 코드를
+ * 직접 사용하는 방향으로 재정의된 데이터가 많지 않도록 하였다.
+ * 실제 사용 방법은 말로 설명하는 것보다 샘플 코드를 사용하는 편이
+ * 이해가 빠를 것이다. 그래서 대략적인 진행 과정을 샘플 코드로
+ * 작성하였다.
+ *
+ * 아래 예제는 실제로는 존재하지 않는 GUI 라이브러리 코드를 사용하였다.
+ * 실제 GUI 코드를 사용하면 코드가 너무 길어져서 설명이 어렵고 코드가
+ * 길어지면 핵심을 놓치기 쉽기 때문에 가공의 함수를 사용하였다.
+ * 또한 텍스트의 encoding conversion 관련된 부분도 생략하였다.
+ * 여기서 사용한 가공의 GUI 코드는 TWin으로 시작하게 하였다.
+ *
+ * @code
+
+ HangulInputContext* hic = hangul_ic_new("2");
+ ...
+
+ // 아래는 키 입력만 처리하는 이벤트 루프이다.
+ // 실제 GUI코드는 이렇게 단순하진 않지만
+ // 편의상 키 입력만 처리하는 코드로 작성하였다.
+
+ TWinKeyEvent event = TWinGetKeyEvent(); // 키이벤트를 받는 이런 함수가
+ // 있다고 치자
+ while (ascii != 0) {
+ bool res;
+ if (event.isBackspace()) {
+ // backspace를 ascii로 변환하기가 좀 꺼림직해서
+ // libhangul에서는 backspace 처리를 위한
+ // 함수를 따로 만들었다.
+ res = hangul_ic_backspace(hic);
+ } else {
+ // 키 입력을 해당하는 ascii 코드로 변환한다.
+ // libhangul에서는 이 ascii 코드가 키 이벤트
+ // 코드와 마찬가지다.
+ int ascii = event.getAscii();
+
+ // 키 입력을 받았으면 이것을 hic에 먼저 보낸다.
+ // 그래야 hic가 이 키를 사용할 것인지 아닌지를 판단할 수 있다.
+ // 함수가 true를 리턴하면 이 키를 사용했다는 의미이므로
+ // GUI 코드가 이 키 입력을 프로세싱하지 않도록 해야 한다.
+ // 그렇지 않으면 한 키입력이 두번 프로세싱된다.
+ res = hangul_ic_process(hic, ascii);
+ }
+
+ // hic는 한번 키입력을 받고 나면 내부 상태 변화가 일어나고
+ // 완성된 글자를 어플리케이션에 보내야 하는 상황이 있을 수 있다.
+ // 이것을 HangulInputContext에서는 commit 스트링이 있는지로
+ // 판단한다. commit 스트링을 받아봐서 스트링이 있다면
+ // 그 스트링으로 입력이 완료된 걸로 본다.
+ const ucschar commit;
+ commit = hangul_ic_get_commit_string(hic);
+ if (commit[0] != 0) { // 스트링의 길이를 재서 commit 스트링이 있는지
+ // 판단한다.
+ TWinInputUnicodeChars(commit);
+ }
+
+ // 키입력 후에는 preedit string도 역시 변화하게 되는데
+ // 입력기 프레임웍에서는 이 스트링을 화면에 보여주어야
+ // 조합중인 글자가 화면에 표시가 되는 것이다.
+ const ucschar preedit;
+ preedit = hangul_ic_get_preedit_string(hic);
+ // 이 경우에는 스트링의 길이에 관계없이 항상 업데이트를
+ // 해야 한다. 왜냐하면 이전에 조합중이던 글자가 있다가
+ // 조합이 완료되면서 조합중인 상태의 글자가 없어질 수도 있기 때문에
+ // 스트링의 길이에 관계없이 현재 상태의 스트링을 preedit
+ // 스트링으로 보여주면 되는 것이다.
+ TWinUpdatePreeditString(preedit);
+
+ // 위 두작업이 끝난후에는 키 이벤트를 계속 프로세싱해야 하는지
+ // 아닌지를 처리해야 한다.
+ // hic가 키 이벤트를 사용하지 않았다면 기본 GUI 코드에 계속해서
+ // 키 이벤트 프로세싱을 진행하도록 해야 한다.
+ if (!res)
+ TWinForwardKeyEventToUI(ascii);
+
+ ascii = GetKeyEvent();
+ }
+
+ hangul_ic_delete(hic);
+
+ * @endcode
+ */
+
+/**
+ * @file hangulinputcontext.c
+ */
+
+/**
+ * @ingroup hangulic
+ * @typedef HangulInputContext
+ * @brief 한글 입력 상태를 관리하기 위한 오브젝트
+ *
+ * libhangul에서 제공하는 한글 조합 루틴에서 상태 정보를 저장하는 opaque
+ * 데이타 오브젝트이다. 이 오브젝트에 키입력 정보를 순차적으로 보내주면서
+ * preedit 스트링이나, commit 스트링을 받아서 처리하면 한글 입력 기능을
+ * 손쉽게 구현할 수 있다.
+ * 내부의 데이터 멤버는 공개되어 있지 않다. 각각의 멤버는 accessor 함수로만
+ * 참조하여야 한다.
+ */
+
#ifndef TRUE
#define TRUE 1
#endif
@@ -1006,6 +1142,32 @@ flush:
return false;
}
+/**
+ * @ingroup hangulic
+ * @brief 키 입력을 처리하여 실제로 한글 조합을 하는 함수
+ * @param hic @ref HangulInputContext 오브젝트
+ * @param ascii 키 이벤트
+ * @return @ref HangulInputContext가 이 키를 사용했으면 true,
+ * 사용하지 않았으면 false
+ *
+ * ascii 값으로 주어진 키 이벤트를 받아서 내부의 한글 조합 상태를
+ * 변화시키고, preedit, commit 스트링을 저장한다.
+ *
+ * libhangul의 키 이벤트 프로세스는 ASCII 코드 값을 기준으로 처리한다.
+ * 이 키 값은 US Qwerty 자판 배열에서의 키 값에 해당한다.
+ * 따라서 유럽어 자판을 사용하는 경우에는 해당 키의 ASCII 코드를 직접
+ * 전달하면 안되고, 그 키가 US Qwerty 자판이었을 경우에 발생할 수 있는
+ * ASCII 코드 값을 주어야 한다.
+ * 또한 ASCII 코드 이므로 Shift 상태는 대문자로 전달이 된다.
+ * Capslock이 눌린 경우에는 대소문자를 뒤바꾸어 보내주지 않으면
+ * 마치 Shift가 눌린 것 처럼 동작할 수 있으므로 주의한다.
+ * preedit, commit 스트링은 hangul_ic_get_preedit_string(),
+ * hangul_ic_get_commit_string() 함수를 이용하여 구할 수 있다.
+ *
+ * 이 함수의 사용법에 대한 설명은 @ref hangulicusage 부분을 참조한다.
+ *
+ * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시킨다.
+ */
bool
hangul_ic_process(HangulInputContext *hic, int ascii)
{
@@ -1029,6 +1191,18 @@ hangul_ic_process(HangulInputContext *hic, int ascii)
return hangul_ic_process_romaja(hic, ascii, c);
}
+/**
+ * @ingroup hangulic
+ * @brief 현재 상태의 preedit string을 구하는 함수
+ * @param hic preedit string을 구하고자하는 입력 상태 object
+ * @return UCS4 preedit 스트링, 이 스트링은 @a hic 내부의 데이터이므로
+ * 수정하거나 free해서는 안된다.
+ *
+ * 이 함수는 @a hic 내부의 현재 상태의 preedit string을 리턴한다.
+ * 따라서 hic가 다른 키 이벤트를 처리하고 나면 그 내용이 바뀔 수 있다.
+ *
+ * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시키지 않는다.
+ */
const ucschar*
hangul_ic_get_preedit_string(HangulInputContext *hic)
{
@@ -1038,6 +1212,18 @@ hangul_ic_get_preedit_string(HangulInputContext *hic)
return hic->preedit_string;
}
+/**
+ * @ingroup hangulic
+ * @brief 현재 상태의 commit string을 구하는 함수
+ * @param hic commit string을 구하고자하는 입력 상태 object
+ * @return UCS4 commit 스트링, 이 스트링은 @a hic 내부의 데이터이므로
+ * 수정하거나 free해서는 안된다.
+ *
+ * 이 함수는 @a hic 내부의 현재 상태의 commit string을 리턴한다.
+ * 따라서 hic가 다른 키 이벤트를 처리하고 나면 그 내용이 바뀔 수 있다.
+ *
+ * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시키지 않는다.
+ */
const ucschar*
hangul_ic_get_commit_string(HangulInputContext *hic)
{
@@ -1047,6 +1233,20 @@ hangul_ic_get_commit_string(HangulInputContext *hic)
return hic->commit_string;
}
+/**
+ * @ingroup hangulic
+ * @brief @ref HangulInputContext를 초기상태로 되돌리는 함수
+ * @param hic @ref HangulInputContext를 가리키는 포인터
+ *
+ * 이 함수는 @a hic가 가리키는 @ref HangulInputContext의 상태를
+ * 처음 상태로 되돌린다. preedit 스트링, commit 스트링, flush 스트링이
+ * 없어지고, 입력되었던 키에 대한 기록이 없어진다.
+ * 영어 상태로 바뀌는 것이 아니다.
+ *
+ * 비교: hangul_ic_flush()
+ *
+ * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시킨다.
+ */
void
hangul_ic_reset(HangulInputContext *hic)
{
@@ -1071,6 +1271,23 @@ hangul_ic_flush_internal(HangulInputContext *hic)
hangul_buffer_clear(&hic->buffer);
}
+/**
+ * @ingroup hangulic
+ * @brief @ref HangulInputContext의 입력 상태를 완료하는 함수
+ * @param hic @ref HangulInputContext를 가리키는 포인터
+ * @return 조합 완료된 스트링, 스트링의 길이가 0이면 조합 완료된 스트링이
+ * 없는 것
+ *
+ * 이 함수는 @a hic가 가리키는 @ref HangulInputContext의 입력 상태를 완료한다.
+ * 조합중이던 스트링을 완성하여 리턴한다. 그리고 입력 상태가 초기 상태로
+ * 되돌아 간다. 조합중이던 글자를 강제로 commit하고 싶을때 사용하는 함수다.
+ * 보통의 경우 입력 framework에서 focus가 나갈때 이 함수를 불러서 마지막
+ * 상태를 완료해야 조합중이던 글자를 잃어버리지 않게 된다.
+ *
+ * 비교: hangul_ic_reset()
+ *
+ * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시킨다.
+ */
const ucschar*
hangul_ic_flush(HangulInputContext *hic)
{
@@ -1095,6 +1312,19 @@ hangul_ic_flush(HangulInputContext *hic)
return hic->flushed_string;
}
+/**
+ * @ingroup hangulic
+ * @brief @ref HangulInputContext가 backspace 키를 처리하도록 하는 함수
+ * @param hic @ref HangulInputContext를 가리키는 포인터
+ * @return @a hic가 키를 사용했으면 true, 사용하지 않았으면 false
+ *
+ * 이 함수는 @a hic가 가리키는 @ref HangulInputContext의 조합중이던 글자를
+ * 뒤에서부터 하나 지우는 기능을 한다. backspace 키를 눌렀을 때 발생하는
+ * 동작을 한다. 따라서 이 함수를 부르고 나면 preedit string이 바뀌므로
+ * 반드시 업데이트를 해야 한다.
+ *
+ * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시킨다.
+ */
bool
hangul_ic_backspace(HangulInputContext *hic)
{
@@ -1218,24 +1448,60 @@ hangul_ic_dvorak_to_qwerty(int qwerty)
return qwerty;
}
+/**
+ * @ingroup hangulic
+ * @brief @ref HangulInputContext가 조합중인 글자를 가지고 있는지 확인하는 함수
+ * @param hic @ref HangulInputContext를 가리키는 포인터
+ *
+ * @ref HangulInputContext가 조합중인 글자가 있으면 true를 리턴한다.
+ *
+ * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시키지 않는다.
+ */
bool
hangul_ic_is_empty(HangulInputContext *hic)
{
return hangul_buffer_is_empty(&hic->buffer);
}
+/**
+ * @ingroup hangulic
+ * @brief @ref HangulInputContext가 조합중인 초성을 가지고 있는지 확인하는 함수
+ * @param hic @ref HangulInputContext를 가리키는 포인터
+ *
+ * @ref HangulInputContext가 조합중인 글자가 초성이 있으면 true를 리턴한다.
+ *
+ * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시키지 않는다.
+ */
bool
hangul_ic_has_choseong(HangulInputContext *hic)
{
return hangul_buffer_has_choseong(&hic->buffer);
}
+/**
+ * @ingroup hangulic
+ * @brief @ref HangulInputContext가 조합중인 중성을 가지고 있는지 확인하는 함수
+ * @param hic @ref HangulInputContext를 가리키는 포인터
+ *
+ * @ref HangulInputContext가 조합중인 글자가 중성이 있으면 true를 리턴한다.
+ *
+ * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시키지 않는다.
+ */
bool
hangul_ic_has_jungseong(HangulInputContext *hic)
{
return hangul_buffer_has_jungseong(&hic->buffer);
}
+/**
+ * @ingroup hangulic
+ * @brief @ref HangulInputContext가 조합중인 종성을 가지고 있는지 확인하는 함수
+ * @param hic @ref HangulInputContext를 가리키는 포인터
+ *
+ * @ref HangulInputContext가 조합중인 글자가 종성이 있으면 true를 리턴한다.
+ *
+ * @remarks 이 함수는 @ref HangulInputContext의 상태를 변화 시키지 않는다.
+ */
bool
hangul_ic_has_jongseong(HangulInputContext *hic)
{
@@ -1304,6 +1570,27 @@ hangul_ic_set_keyboard(HangulInputContext *hic, const HangulKeyboard* keyboard)
hic->keyboard = keyboard;
}
+/**
+ * @ingroup hangulic
+ * @brief @ref HangulInputContext의 자판 배열을 바꾸는 함수
+ * @param hic @ref HangulInputContext 오브젝트
+ * @param id 선택하고자 하는 자판, 아래와 같은 값을 선택할 수 있다.
+ * @li "2" 두벌식 자판
+ * @li "32" 세벌식 자판으로 두벌식의 배열을 가진 자판.
+ * 두벌식 사용자가 쉽게 세벌식 테스트를 할 수 있다.
+ * shift를 누르면 자음이 종성으로 동작한다.
+ * @li "3f" 세벌식 최종
+ * @li "39" 세벌식 390
+ * @li "3s" 세벌식 순아래
+ * @li "3y" 세벌식 옛글
+ * @li "ro" 로마자 방식 자판
+ * @return 없음
+ *
+ * 이 함수는 @ref HangulInputContext의 자판을 @a id로 지정된 것으로 변경한다.
+ *
+ * @remarks 이 함수는 @ref HangulInputContext의 내부 조합 상태에는 영향을
+ * 미치지 않는다. 따라서 입력 중간에 자판을 변경하더라도 조합 상태는 유지된다.
+ */
void
hangul_ic_select_keyboard(HangulInputContext *hic, const char* id)
{
@@ -1361,6 +1648,18 @@ hangul_ic_set_combination(HangulInputContext *hic,
hic->combination = combination;
}
+/**
+ * @ingroup hangulic
+ * @brief @ref HangulInputContext 오브젝트를 생성한다.
+ * @param keyboard 사용하고자 하는 키보드, 사용 가능한 값에 대해서는
+ * hangul_ic_select_keyboard() 함수 설명을 참조한다.
+ * @return 새로 생성된 @ref HangulInputContext에 대한 포인터
+ *
+ * 이 함수는 한글 조합 기능을 제공하는 @ref HangulInputContext 오브젝트를
+ * 생성한다. 생성할때 지정한 자판은 나중에 hangul_ic_select_keyboard() 함수로
+ * 다른 자판으로 변경이 가능하다.
+ * 더이상 사용하지 않을 때에는 hangul_ic_delete() 함수로 삭제해야 한다.
+ */
HangulInputContext*
hangul_ic_new(const char* keyboard)
{
@@ -1390,6 +1689,17 @@ hangul_ic_new(const char* keyboard)
return hic;
}
+/**
+ * @ingroup hangulic
+ * @brief @ref HangulInputContext를 삭제하는 함수
+ * @param hic @ref HangulInputContext 오브젝트
+ *
+ * @a hic가 가리키는 @ref HangulInputContext 오브젝트의 메모리를 해제한다.
+ * hangul_ic_new() 함수로 생성된 모든 @ref HangulInputContext 오브젝트는
+ * 이 함수로 메모리해제를 해야 한다.
+ * 메모리 해제 과정에서 상태 변화는 일어나지 않으므로 마지막 입력된
+ * 조합중이던 내용은 사라지게 된다.
+ */
void
hangul_ic_delete(HangulInputContext *hic)
{