레이블이 IME인 게시물을 표시합니다. 모든 게시물 표시
레이블이 IME인 게시물을 표시합니다. 모든 게시물 표시

2010년 3월 4일 목요일

notepad2 컴파일 삽질기 8 : 한글 IME 패치

by BLUEnLIVE | 2010/03/04 21:32

notepad2가 4.1.24로 업데이트 되었다. 이전 버전인 4.0.23으로 충분히 완벽해졌다고 생각했는데, Florian Balmer 님께선 아직 업뎃이 고픈가...


이전 포스트에서 얘기한 notepad2 4.1.24의 한글 IME 패치방법을 설명하기에 앞서, 몇 가지 작업을 해야한다.


0. 들어가기 전에

다음과 같은 내용은 모두 준비가 되어있다고 가정하고 설명함.

a. Visual C++ v6.0 및 Service Pack 6 설치
b. Platform SDK 설치 (Windows® Server 2003 R2 Platform SDK ISO Download)
c. Scintilla 2.03


1. Visual C++ 6.0을 위한 dsp 파일 생성

notepad2의 소스에는 VC6을 위한 dsp 파일이 없다. VC7을 기준으로 만들어졌기 때문이다.

얼마전까지(정확히는 4.0.22-beta5 까지) notepad2의 기능추가 패치를 제작하시던 Kai Liu님이 notepad2 패치 작업을 중단했는데, Kai Liu 님의 패치 중에 VC6을 위한 dsp 파일 생성이 포함되어 있었다.
따라서, VC6에서 notepad2를 컴파일하려면 dsp 파일을 다시 만들어야 한다.

아래 파일을 다운받아 압축을 푼 뒤에 notepad2 소스의 루트 폴더에 저장한다.



2. Edit.c 버그 수정

EditTitleCase() 함수에서 아래와 같은 부분을 찾는다.
if (!IsCharAlphaNumericW(pszTextW[i]) && !StrChr(L"'?,pszTextW[i])) {

이 부분을 아래와 같이 수정한다.
if (!IsCharAlphaNumericW(pszTextW[i]) && !StrChr(L"'",pszTextW[i])) {


이건 그냥 notepad2의 버그다. 제작자 Balmer 님께 문의를 했는데, 패치할 의지가 없다는 답장을 받았다. 헐~


3. ScintillaWin.cxx 수정 #1

짜잔~ 이제야 한글 IME 패치를 할 차례가 되었다.
수정할 곳은 이전 패치과 같이 두 군데다. 그 중 첫번째는 WndProc().

WM_IME_STARTCOMPOSITION: 을 찾아 아래와 같이 수정한다.
이 부분은 이전 패치 그대로이다.

case WM_IME_STARTCOMPOSITION:     // dbcs
    ImeStartComposition();
    // added from here-------------------------------------------------
    if (LOWORD(GetKeyboardLayout(0))==MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN)) {
        // if the current IME is the Korean IME, do not show the default IME window
        return 0;
    }
    // added to here-------------------------------------------------
    return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);


4. ScintillaWin.cxx 수정 #2

HandleComposition()을 찾아 아래 내용으로 대체한다.
이 부분은 이전 패치보다 좀 길어지고, 복잡해졌다.

sptr_t ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) {
#ifdef __DMC__
    // Digital Mars compiler does not include Imm library
    return 0;
#else
    static int cs    = -1;
    static int undo  = -1;
    static bool comp = false;
    static bool bEndOfLine, bOverstrike;
    static bool wasSelection = false;
    bool bKoreanIME = LOWORD(GetKeyboardLayout(0))==MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN);
 
    if (bKoreanIME && (lParam & GCS_COMPSTR)) {
        HIMC hIMC = ::ImmGetContext(MainHWND());
        if (hIMC) {
            const int maxLenInputIME = 200;
            wchar_t wcs[maxLenInputIME];
            LONG bytes = ::ImmGetCompositionStringW(hIMC, GCS_COMPSTR, wcs, (maxLenInputIME-1)*2);
            int wides = bytes / 2;

            int lastitem = sel.Count()-1;
            int selBegin = sel.Range(lastitem).End().Position();
            int selEnd = selBegin;

            if (bytes) {
                //comp==false 이면 최초 진입
                //undo를 마비시키기 전에 삭제할 글자/블럭 삭제
                if (!comp)
                {
                    FilterSelections();

                    {
                        UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
                        for (size_t r=0; r<sel.Count(); r++) {
                            if (!RangeContainsProtected(sel.Range(r).Start().Position(),
                                sel.Range(r).End().Position())) {
                                selBegin = sel.Range(r).Start().Position();
                                if (!sel.Range(r).Empty()) {
                                    if (sel.Range(r).Length()) {
                                        pdoc->DeleteChars(selBegin, sel.Range(r).Length());
                                        sel.Range(r).ClearVirtualSpace();
                                    } else {
                                        // Range is all virtual so collapse to start of virtual space
                                        sel.Range(r).MinimizeVirtualSpace();
                                    }
                                } else if (inOverstrike) {
                                    if (selBegin < pdoc->Length()) {
                                        if (!IsEOLChar(pdoc->CharAt(selBegin))) {
                                            pdoc->DelChar(selBegin);
                                            sel.Range(r).ClearVirtualSpace();
                                        }
                                    }
                                }
                                selBegin = InsertSpace(selBegin, sel.Range(r).caret.VirtualSpace());

                                sel.Range(r).ClearVirtualSpace();
                                // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
                                if (wrapState != eWrapNone) {
                                    AutoSurface surface(this);
                                    if (surface) {
                                        WrapOneLine(surface, pdoc->LineFromPosition(selBegin));
                                    }
                                }
                            }
                        }

                    }

                    bOverstrike = inOverstrike;
                }
               
                if (cs < 0 && !bOverstrike) {
                    cs = vs.caretStyle;
                    vs.caretStyle = CARETSTYLE_BLOCK;
                }
               
                if (undo < 0) {
                    undo = pdoc->IsCollectingUndo()?1:0;
                    pdoc->SetUndoCollection(false);
                }
               
                if (!comp) {
                    comp = true;
                } else {
                    DelChar();
                }
            } else {
                //조합 중 조합중인 글자를 다 지운 경우
                if (cs >= 0) {
                    vs.caretStyle = cs;
                    cs = -1;
                }
 
                if (comp) {
                    comp = false;
                   
                    DelChar();
                }

                if (undo >= 0) {
                    pdoc->SetUndoCollection(undo==1);
                    undo = -1;
                }
            }
           
            MovePositionTo(selBegin);
           
            inOverstrike = false;
            if (IsUnicodeMode()) {
                char utfval[maxLenInputIME * 3];
                unsigned int len = UTF8Length(wcs, wides);
                UTF8FromUTF16(wcs, wides, utfval, len);
                utfval[len] = '\0';
                AddCharUTF(utfval, len);
            } else {
                char dbcsval[maxLenInputIME * 2];
                int size = ::WideCharToMultiByte(InputCodePage(),
                    0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
                for (int i=0; i<size; i++)
                    AddChar(dbcsval[i]);
            }
            inOverstrike = bOverstrike;
   
            MovePositionTo(selBegin);
        }
    }
   
    if (lParam & GCS_RESULTSTR) {
        //앞의 if문 6개는 모두 한글 입력기에서만 동작
        //다른 언어 IME에서는 패스
        if (comp) {
            comp = false;
            DelChar();
        }
       
        if (cs >= 0) {
            vs.caretStyle = cs;
            cs = -1;
        }
        if (undo >= 0){
            pdoc->SetUndoCollection(undo==1);
            undo = -1;
        }

        //덮어쓰기에서 한글 조합 중 마지막에 숫자나 기호를 붙인 경우 한 글자 더 삭제(가9)
        bool bKoreanPlusOneMore = false;
        if (bKoreanIME && bOverstrike)
        {
            HIMC hIMC = ::ImmGetContext(MainHWND());
            if (hIMC) {
                const int maxLenInputIME = 200;
                wchar_t wcs[maxLenInputIME];
                LONG bytes = ::ImmGetCompositionStringW(hIMC,
                    GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
                int wides = bytes / 2;
               
                char dbcsval[maxLenInputIME * 2];
                int size = ::WideCharToMultiByte(InputCodePage(),
                    0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
   
                bKoreanPlusOneMore = (size==3);
                ::ImmReleaseContext(MainHWND(), hIMC);
            }
        }
        if (bKoreanPlusOneMore) DelChar();

        if (bKoreanIME) inOverstrike = false;

        HIMC hIMC = ::ImmGetContext(MainHWND());
        if (hIMC) {
            const int maxLenInputIME = 200;
            wchar_t wcs[maxLenInputIME];
            LONG bytes = ::ImmGetCompositionStringW(hIMC,
                GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
            int wides = bytes / 2;
           
            if (IsUnicodeMode()) {
                char utfval[maxLenInputIME * 3];
                unsigned int len = UTF8Length(wcs, wides);
                UTF8FromUTF16(wcs, wides, utfval, len);
                utfval[len] = '\0';
                AddCharUTF(utfval, len);
            } else {
                char dbcsval[maxLenInputIME * 2];
                int size = ::WideCharToMultiByte(InputCodePage(),
                    0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
                for (int i=0; i<size; i++)
                    AddChar(dbcsval[i]);
            }
           
            // Set new position after converted
            Point pos = LocationFromPosition(sel.Range(0).End().Position());
            COMPOSITIONFORM CompForm;
            CompForm.dwStyle = CFS_POINT;
            CompForm.ptCurrentPos.x = pos.x;
            CompForm.ptCurrentPos.y = pos.y;
            ::ImmSetCompositionWindow(hIMC, &CompForm);
           
            ::ImmReleaseContext(MainHWND(), hIMC);
        }
        if (bKoreanIME) inOverstrike = bOverstrike;
       
        return 0;
    }
   
    return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
#endif
}

이렇게 수정하면 아래와 같이 언제나 정상적으로 한글 입력이 가능한 notepad2를 만날 수 있다.

쿠헬헬~


notepad2 4.1.24 업데이트! 그런데!!!

notepad2가 4.1.24로 업데이트 되었다.
이전 버전인 4.0.23으로 충분히 완벽해졌다고 생각했는데, Florian Balmer 님께선 아직 업뎃이 고픈가보다.

이번 버전에서도 (이전과 같이) 커다란 변화가 있었다.

4.1.24에서 바뀐 내용 열기..



1. 업데이트된 Scintilla의 기능을 충실히 활용하지 못하는 notepad2

이 변화 중에 가장 주목해야 될 부분이 바로 Scintilla2.03으로 업데이트 되었다는 것이다.
Scintilla는 1.x에서 2.x로 업데이트되면서 커다란 변화가 생겼는데, 바로 다중입력 기능이 추가되었다는 것이다.
즉, 컬럼블럭(alt+마우스 선택)을 잡은 상태에서 글을 입력하면 여러 줄에 같은 내용이 동시에 입력된다.

하지만, notepad2는 Scintilla 2.03을 기반으로 하면서도 이 기능을 지원하지 않는다.

아마도 notepad2의 undo-redo 구조와 맞지 않은 것이 원인인 듯 하다.


2. 새롭게 발생한 오류


notepad2 4.1.24에서 alt+마우스 선택을 이용해서 컬럼블럭을 설정한 뒤 한글을 입력하면 아래와 같이 깨져나온다.
새롭게 추가된 기능을 제대로 활용하지도 않으면서 오류만 발생하는 슬픈(?) 현상이 발생하는 것이다. OTL

'쿠헬헬'을 입력했을 뿐인데...


※ 이 화면은 IME2002에서 동작하는 화면이며, IME2003에서는 조금 달라보이긴 하지만, 발생하는 오류는 동일함.


3. 결국 HandleComposition()을 다시 패치


외형적으로는 notepad2 4.0.23와 4.1.24는 비슷하게 동작하지만, 한글 IME 패치는 완전히 다시 작성해야 한다.
내부적으로 변경된 기능을 충실하게 반영하도록 수정해야 하는 것이다. OTL

결국... 결국... 며칠 간의 삽질 끝에 HandleComposition()을 패치하고야 말았다!

이 모습 보기가 결코 쉽지 않았다



패치하는 법은 다음 포스트에서 설명하도록 하겠다.

2009년 10월 9일 금요일

notepad2 컴파일 삽질기 7 : 한글 IME 패치 재작성

Notepad2에서 한글을 정상적으로 입력할 수 있도록 하기 위해 다음과 같은 패치를 공개했다.


이 패치의 가장 큰 문제는 너무 복잡하다는 점이다.
그런데, 또 하나의 문제가 있다. 바로 한글 IME 외의 IME에서 완벽하게 동작하지 않는다는 것...

사실, 모든 IME에서 완벽하게 동작하게 만들기 위해 이리저리 손을 많이 대었는데, 알고 보니 한글 IME만 엉망인 거다. OTL

한글 외의 일본어, 중국어 IME는 원래부터 정상적으로 동작했음


이 문제를 한꺼번에 해결하고, 좀 더 간결한 패치를 유지하기 위해 다시 패치를 해봤다.
참고로, 이 패치는 기본적으로 행복한고니 님의 패치[footnote]이 패치는 간결하긴 하지만, 덮어쓰기 모드에서 치명적인 버그가 둘이나 있다.
(뒤에 다시 언급됨)[/footnote]를 기본으로 작성했다.
이 패치를 하려면, 위의 세 패치는 안한 셈 치고 처음부터 다시 시작해야 한다.

패치할 대상은 ScintillaWin.cxx 하나다. (얏호! 드디어 정상적인 패치를 만든 거다!)


1. WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) 수정

WM_IME_STARTCOMPOSITION: 을 찾아 아래와 같이 수정한다.
이 부분은 정확히는 행복한고니 님이 참고한 sixman님의 패치(헥헥 길다)에서 그대로 가져온 것이다.

case WM_IME_STARTCOMPOSITION:     // dbcs
    ImeStartComposition();
    // added from here-------------------------------------------------
    if (LOWORD(GetKeyboardLayout(0))==MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN)) {
        // if the current IME is the Korean IME, do not show the default IME window
        return 0;
    }
    // added to here-------------------------------------------------
    return ::DefWindowProc(MainHWND(), iMessage, wParam, lParam);


2. ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) 대체

HandleComposition()을 찾아 아래 내용으로 대체한다.
삽입/덮어쓰기/행의 끝에서 덮어쓰기에서 각각 다르게 동작하도록 작성했다.
따라서, 행복한고니 님 패치의 버그[footnote]1. undo/redo가 정상동작하지 않음
2. 행의 끝에서 글을 입력하면 이상하게 동작함[/footnote]는 발생하지 않는다.

sptr_t ScintillaWin::HandleComposition(uptr_t wParam, sptr_t lParam) {
#ifdef __DMC__
    // Digital Mars compiler does not include Imm library
    return 0;
#else
    static int cs    = -1;
    static int undo  = -1;
    static bool comp = false;
    static bool bEndOfLine, bOverstrike;
    static bool wasSelection = false;
    bool bKoreanIME = LOWORD(GetKeyboardLayout(0))==MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN);
 
    if (bKoreanIME && (lParam & GCS_COMPSTR)) {
        HIMC hIMC = ::ImmGetContext(MainHWND());
        if (hIMC) {
            const int maxLenInputIME = 200;
            wchar_t wcs[maxLenInputIME];
            LONG bytes = ::ImmGetCompositionStringW(hIMC, GCS_COMPSTR, wcs, (maxLenInputIME-1)*2);
            int wides = bytes / 2;
            int selBegin = currentPos;

            if (bytes) {
                //comp==false 이면 최초 진입
                //undo를 마비시키기 전에 삭제할 글자/블럭 삭제
                if (!comp)
                {
                    wasSelection = currentPos != anchor;
                    if (wasSelection && anchor<currentPos) selBegin = anchor;
                    ClearSelection();
                    bEndOfLine = (selBegin == pdoc->Length()) || IsEOLChar(pdoc->CharAt(selBegin));
                    bOverstrike = inOverstrike;
                    if (bOverstrike && !wasSelection && !bEndOfLine && !RangeContainsProtected(selBegin, selBegin + 1)) DelChar();
                }
               
                if (cs < 0 && !bOverstrike) {
                    cs = vs.caretStyle;
                    vs.caretStyle = CARETSTYLE_BLOCK;
                }
               
                if (undo < 0) {
                    undo = pdoc->IsCollectingUndo()?1:0;
                    pdoc->SetUndoCollection(false);
                }
               
                if (!comp) {
                    comp = true;
                } else {
                    DelChar();
                }
            } else {
                //조합 중 조합중인 글자를 다 지운 경우
                if (cs >= 0) {
                    vs.caretStyle = cs;
                    cs = -1;
                }
 
                if (comp) {
                    comp = false;
                    DelChar();
                }

                if (undo >= 0) {
                    pdoc->SetUndoCollection(undo==1);
                    undo = -1;
                }
            }
           
            inOverstrike = false;
            if (IsUnicodeMode()) {
                char utfval[maxLenInputIME * 3];
                unsigned int len = UTF8Length(wcs, wides);
                UTF8FromUTF16(wcs, wides, utfval, len);
                utfval[len] = '\0';
                AddCharUTF(utfval, len);
            } else {
                char dbcsval[maxLenInputIME * 2];
                int size = ::WideCharToMultiByte(InputCodePage(),
                    0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
                for (int i=0; i<size; i++)
                    AddChar(dbcsval[i]);
            }
            inOverstrike = bOverstrike;
   
            MovePositionTo(selBegin);
        }
    }
   
    if (lParam & GCS_RESULTSTR) {
        //앞의 if문 6개는 모두 한글 입력기에서만 동작
        //다른 언어 IME에서는 패스
        if (comp) {
            comp = false;
            DelChar();
        }
       
        if (cs >= 0) {
            vs.caretStyle = cs;
            cs = -1;
        }
        if (undo >= 0){
            pdoc->SetUndoCollection(undo==1);
            undo = -1;
        }

        //덮어쓰기에서 한글 조합 중 마지막에 숫자나 기호를 붙인 경우 한 글자 더 삭제(가9)
        bool bKoreanPlusOneMore = false;
        if (bKoreanIME && bOverstrike)
        {
            HIMC hIMC = ::ImmGetContext(MainHWND());
            if (hIMC) {
                const int maxLenInputIME = 200;
                wchar_t wcs[maxLenInputIME];
                LONG bytes = ::ImmGetCompositionStringW(hIMC,
                    GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
                int wides = bytes / 2;
               
                char dbcsval[maxLenInputIME * 2];
                int size = ::WideCharToMultiByte(InputCodePage(),
                    0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
   
                bKoreanPlusOneMore = (size==3);
                ::ImmReleaseContext(MainHWND(), hIMC);
            }
        }
        if (bKoreanPlusOneMore) DelChar();

        if (bKoreanIME) inOverstrike = false;

        HIMC hIMC = ::ImmGetContext(MainHWND());
        if (hIMC) {
            const int maxLenInputIME = 200;
            wchar_t wcs[maxLenInputIME];
            LONG bytes = ::ImmGetCompositionStringW(hIMC,
                GCS_RESULTSTR, wcs, (maxLenInputIME-1)*2);
            int wides = bytes / 2;
           
            if (IsUnicodeMode()) {
                char utfval[maxLenInputIME * 3];
                unsigned int len = UTF8Length(wcs, wides);
                UTF8FromUTF16(wcs, wides, utfval, len);
                utfval[len] = '\0';
                AddCharUTF(utfval, len);
            } else {
                char dbcsval[maxLenInputIME * 2];
                int size = ::WideCharToMultiByte(InputCodePage(),
                    0, wcs, wides, dbcsval, sizeof(dbcsval) - 1, 0, 0);
                for (int i=0; i<size; i++)
                    AddChar(dbcsval[i]);
            }
           
            // Set new position after converted
            Point pos = LocationFromPosition(currentPos);
            COMPOSITIONFORM CompForm;
            CompForm.dwStyle = CFS_POINT;
            CompForm.ptCurrentPos.x = pos.x;
            CompForm.ptCurrentPos.y = pos.y;
            ::ImmSetCompositionWindow(hIMC, &CompForm);
           
            ::ImmReleaseContext(MainHWND(), hIMC);
        }
        if (bKoreanIME) inOverstrike = bOverstrike;
       
        return 0;
    }
   
    return ::DefWindowProc(MainHWND(), WM_IME_COMPOSITION, wParam, lParam);
#endif
}


덧1. 드디어 완전체를 만들었다는 생각에 뿌듯함도 잠시... Scintilla 라이브러리가 그 사이에 2.x로 업데이트 되었다.
바뀐 부분의 코드를 읽어보니... 다중선택시 입력을 하면 전체 선택영역에 똑같은 내용을 출력하는 기능이 있더라.
이건 대체 어케 패치해야 되는 거냐... OTL

덧2. 한글날이 지나가기 전에 패치를 공개하기 위해 고군분투했다.
패치의 버그를 찾기 위해 열심히 뛰어주신 okto 님께 감사드린다.

2009년 8월 7일 금요일

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에서 입력중인 모습