notepad2 컴파일 삽질기 6 : 일본어 IME도 정상적으로 사용이 가능하도록 수정

삽질기2: IME 메시지 처리삽질기3: undo/redo 정상동작토록 수정에서 발견된 IME 관련 버그를 수정하는 포스트임


step6. 일본어 IME 메시지도 잘 처리하도록 수정

모 포럼에서 notepad2를 일본어 윈도우에서 돌려보니 IME 쪽에서 문제가 발생했다는 버그 리포트가 들어왔다.
(notepad2를 일본어 윈도우에서 돌려볼 생각도 해본 적이 없었음)

한글의 경우 한 순간에 조합되고 있는 글자의 수가 무조건 하나인데, 수정된 방식은 이 상황에 최적화되어있어 발생한 버그이다.
하지만, 일본어의 경우 아주 많은 글자가 동시에 입력되는 상황이 발생할 수 있다는 것이다.

일본어 윈도우에서도 notepad2를 무난하게 사용하려면 아래와 같이 수정하면 된다.
(일본어 윈도우를 쓸 일이 없다면 이 수정은 당연히 무의미하다)

수정 대상 파일은 ScintillaWin.cxx 하나이다.


1. ScintillaWin 클래스 선언부에서 메쏘드 및 변수 추가

ScintillaWin 클래스의 선언부에 아래와 같이 메쏘드 및 변수를 추가한다.
선언부 맨 끝부분에 추가하면 된다.

private:
void DelCharBacks(int iCount);
int GetCompositioningStrLen(DWORD index);
long lCompositioningLenOld, lCompositioningLen;



2. HandleComposition() 메쏘드 수정

IME에서 글자를 입력중일 때 메시지를 처리하는 메쏘드가 바로 HandleComposition() 이다.
따라서, 뭘 바꾸든 이 메쏘드가 바뀔 가능성은 백푸로다.

이번에는(에효... 또냐... ㅡ.ㅡ;) 아래와 같은 내용으로 통째로 바꿔준다.

sptr_t ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) {
#ifdef __DMC__
// Digital Mars compiler does not include Imm library
return 0;
#else
ClearSelection();
if (!inOverstrike) pdoc->cb.uh.EnableUndoAction(false);

if (lParam & GCS_COMPSTR)
{
if (inComposition) DelCharBacks(lCompositioningLenOld);

if (inOverstrike) bCompositioning = true;

inComposition = !(AddImeCompositionString(GCS_COMPSTR) == 0);
}
else if (lParam & GCS_RESULTSTR)
{
if (inComposition) DelCharBacks(lCompositioningLenOld);

if (inOverstrike) bCompositioning = false;
else pdoc->cb.uh.EnableUndoAction(true);

AddImeCompositionString(GCS_RESULTSTR);
inComposition = FALSE;
lCompositioningLenOld = 0;
}

UpdateSystemCaret();
return 0;
#endif
}



3. ImeStartComposition() 메쏘드 수정

ImeStartComposition() 메쏘드를 찾는다.
이 메쏘드의 앞부분은 아래와 같은 모양이다.

void ScintillaWin::ImeStartComposition() {
#ifndef __DMC__
// Digital Mars compiler does not include Imm library
if (caret.active) {
// Move IME Window to current caret position
HIMC hIMC = ::ImmGetContext(MainHWND());

여기에 한 줄을 추가해서 아래와 같이 바꿔준다.

void ScintillaWin::ImeStartComposition() {
#ifndef __DMC__
// Digital Mars compiler does not include Imm library
lCompositioningLenOld = 0;
if (caret.active) {
// Move IME Window to current caret position
HIMC hIMC = ::ImmGetContext(MainHWND());

중간에 한 줄(4행)이 추가된 것 외엔 바뀐 부분은 없다.



4. AddImeCompositionString() 메쏘드 수정

ImeStartComposition() 메쏘드를 찾는다.
이 메쏘드를 아래의 내용으로 통째로 바꿔준다.

내부적으로만 사용되던 문자의 개수(wides)를 멤버변수(lCompositionLenOld)로 바꿔주는 것이다.

LONG ScintillaWin::AddImeCompositionString(DWORD index)
{
const int maxImeBuf = 200;
wchar_t wcs[maxImeBuf];

LONG bytes = GetCompositionString(index, wcs, (maxImeBuf-1)*2);
lCompositioningLenOld = bytes/2;

if(bytes == 0)
return bytes;

if (IsUnicodeMode()) {
char utfval[maxImeBuf * 3];
unsigned int len = UTF8Length(wcs, lCompositioningLenOld);
UTF8FromUTF16(wcs, lCompositioningLenOld, utfval, len);
utfval[len] = '\0';
AddCharUTF(utfval, len);
} else {
char dbcsval[maxImeBuf * 2];
int size = ::WideCharToMultiByte(InputCodePage()
, 0
, wcs
, lCompositioningLenOld
 , dbcsval
, sizeof(dbcsval) - 1
, 0
, 0);
AddCharUTF(dbcsval, size);
}

return bytes;
}



5. GetCaretSize() 메쏘드 수정

GetCaretSize() 메쏘드에서 아래와 같은 부분을 찾는다.

    if(inComposition)
{
Point end = LocationFromPosition(currentPos);
Point start = LocationFromPosition(currentPos-2);
cs.x = end.x - start.x;
cs.y = vs.lineHeight;
}

위에 하일라이트된 4행을 아래와 같이 수정한다.

    if(inComposition)
{
Point end = LocationFromPosition(currentPos);
Point start = LocationFromPosition(currentPos - lCompositioningLen);
cs.x = end.x - start.x;
cs.y = vs.lineHeight;
}

한글이란 가정 하에 길이를 무조건 2로 설정하지 않고, 조합중인 글자 수로 설정하는 수정이다.



6. 메쏘드 2개 추가

수정중인 ScintillaWin.cxx의 맨 끝부분에 아래와 같이 새로운 메쏘드 2개를 추가한다.

int ScintillaWin::GetCompositioningStrLen(DWORD index)
{
HIMC hIMC = ::ImmGetContext(MainHWND());
if (hIMC) {
const int maxLenInputIME = 200;
wchar_t wcs[maxLenInputIME];
LONG bytes = ::ImmGetCompositionStringW(hIMC,
index, wcs, (maxLenInputIME-1)*2);
int wides = bytes / 2;
return wides;
}
return 0;
}

void ScintillaWin::DelCharBacks(int iCount)
{
DestroySystemCaret();
while (iCount--) DelCharBack(false);
CreateSystemCaret();
}


이렇게 수정하면 일본어 IME에서도 정상적인 문자의 입력이 가능하다.

사용자 삽입 이미지

XPMode + 일본어 Win XP에서 입력중인 모습