Sablog Models/자작자작

Notepad2 패치에 발을 담그다 - (2) Mark Occurrences, anywhere for non-ASCII

어­리 2011. 6. 12. 03:32
Notepad2 패치에 발을 담그다 - (1) Mark Occurrences, Case insensitively
http://un-i.tistory.com/entry/Notepad2-Custom-Patch-1-Mark-Occurrences-Case-insensitively
위 링크에 이어지는 글. 위의 패치가 적용되었다고 가정한다.

주의: 이 방법은 ANSI나 UTF-8 인코딩에서만 정상적으로 작동하는 것으로 확인되었다.
UCS-16에서도 한글이나 가나 문자에는 잘 적용되지만, 특정 한자에는 적용되지 않을 수 있다.



Mark Occurrences 기능은 원래 대소문자가 일치하는 전체 단어에 대해서만 강조하게 되어 있다.
이 중 대소문자 일치 여부를 메뉴에서 선택하게 한 것이 패치 (1)이다.


메뉴를 이용해 bOccurrencesCaseSensitive를 결정,

파라미터를 함수로 전달해서 SCI_FINDTEXT에 SCFIND_MATCHCASE를 사용할지 정한 것이다.

한편 SCFIND_WHOLEWORD라는 것이 있는데, 단어 전체의 일치에 대해서만 강조가 작동한다.
위 사진에서 보다시피 boy를 선택하면 boys가 강조되지 않는다.
유럽어권 문장의 특징으로 비추어 보건대 단어 전체 일치만 따지지 않을 필요가 별로 없기 때문이다.

그러나 이게 한국어나 일본어 사용자들에게는 좀 곤란한 게, 조사나 어미가 붙기 때문이다.
심지어 한자 원문의 경우에는 띄어쓰기가 없다. -_-
이걸 위해 소스를 좀 더 건드렸다. 사실 커스텀 패치를 시작한 이유가 이건데 여태 끌어 온 거다. ㄲㄲ
역시 시험 기간이 되면 머리가 잘 돌아가지. 특히 시험 외의 영역에서...

고칠 곳은 한 줄뿐이다.
1. Edit.c

void EditMarkAll(HWND,int,BOOL)에서 다음 내용을 찾아서,

  while ((iPos = (int)SendMessage(hwnd, SCI_FINDTEXT, (bMarkOccurrencesCaseSensitive ? (SCFIND_MATCHCASE | SCFIND_WHOLEWORD) : SCFIND_WHOLEWORD), (LPARAM)&ttf)) != -1
      && ++iMatchesCount < 1000)
다음과 같이 바꾼다.
  while ((iPos = (int)SendMessage(hwnd, SCI_FINDTEXT, (bMarkOccurrencesCaseSensitive ? SCFIND_MATCHCASE | NULL) | (!(pszText[0] >> 7) ? SCFIND_WHOLEWORD : NULL), (LPARAM)&ttf)) != -1
      && ++iMatchesCount < 1000)

다음 글에서는 사용성을 높이기 위한 스마트한(...-_-) 스트링 처리를 해 보도록 한다.



원래 새 글로 올리려 했지만 양심에 찔려서 그냥 추가한다.
드래그 영역 앞뒤의 공백을 선택적으로 지우고,
ASCII 영역에서도 앞의 공백을 포함하며 3글자가 넘으면 단어 첫머리의 일치를 강조한다.
EditMarkAll()의 나름 개궁극판이다.ㅋㅋ....ㅋ..ㅋ.....ㅠㅠㅠㅠㅠ으헝
//=============================================================================
//
//    EditMarkAll()
//    Mark all occurrences of the text currently selected (by Aleksandar Lekov / slightly modified by Un-i-que)
//
void EditMarkAll(HWND hwnd, int iMarkOccurrences, BOOL bMarkOccurrencesCaseSensitive)
{
    struct TextToFind ttf;
    int iPos;
    char  *pszText;
    int iTextLen;
    int iSelStart;
    int iSelEnd;
    int iSelCount;
    int iMatchesCount;

    // feature is off
    if (!iMarkOccurrences)
    {
        return;
    }

    iTextLen = (int)SendMessage(hwnd,SCI_GETLENGTH,0,0);

    // get current selection
    iSelStart = (int)SendMessage(hwnd,SCI_GETSELECTIONSTART,0,0);
    iSelEnd = (int)SendMessage(hwnd,SCI_GETSELECTIONEND,0,0);
    iSelCount = iSelEnd - iSelStart;

    // clear existing indicator
    SendMessage(hwnd, SCI_SETINDICATORCURRENT, 1, 0);
    SendMessage(hwnd, SCI_INDICATORCLEARRANGE, 0, iTextLen);

    // if nothing selected or multiple lines are selected exit
    if (iSelCount == 0 ||
        (int)SendMessage(hwnd, SCI_LINEFROMPOSITION, iSelStart, 0) !=
        (int)SendMessage(hwnd, SCI_LINEFROMPOSITION, iSelEnd, 0))
    {
        return;
    }

    pszText = LocalAlloc(LPTR,iSelCount + 1);
    (int)SendMessage(hwnd,SCI_GETSELTEXT,0,(LPARAM)pszText);

    ZeroMemory(&ttf,sizeof(ttf));

    ttf.chrg.cpMin = 0;
    ttf.chrg.cpMax = iTextLen;
    ttf.lpstrText = pszText;

    if (ttf.lpstrText[1] != '\0' && ttf.lpstrText[0] == ' ' && ttf.lpstrText[1] != ' ')
    {
    iSelCount--;
        ttf.chrg.cpMax--;
        ttf.lpstrText++;
    }
    if (strlen(ttf.lpstrText) > 1 && ttf.lpstrText[strlen(ttf.lpstrText) - 1] == ' ' && ttf.lpstrText[strlen(ttf.lpstrText) - 2] != ' ')
    {
        iSelCount--;
        ttf.chrg.cpMax--;
    }
    ttf.lpstrText[iSelCount] = '\0';

    // set style, green should be greener not to confuse with bookmarks' style
    SendMessage(hwnd, SCI_INDICSETALPHA, 1, iMarkOccurrences == 2 ? 100 : 30);
    SendMessage(hwnd, SCI_INDICSETFORE, 1, 0xff << ((iMarkOccurrences - 1) << 3));
    SendMessage(hwnd, SCI_INDICSETSTYLE, 1, INDIC_ROUNDBOX);

    iMatchesCount = 0;
    while ((iPos = (int)SendMessage(hwnd, SCI_FINDTEXT, (bMarkOccurrencesCaseSensitive ? SCFIND_MATCHCASE : NULL) | (!(ttf.lpstrText[0] >> 7) ? (strlen(ttf.lpstrText) > 2 && !(pszText[0] - ' ') ? SCFIND_WORDSTART : (strlen(ttf.lpstrText) == 1 || ttf.lpstrText[0] - ' ' && ttf.lpstrText[strlen(ttf.lpstrText) - 1] - ' ' ? SCFIND_WHOLEWORD : NULL)) : NULL), (LPARAM)&ttf)) != -1
        && ++iMatchesCount < 1000)
    {
        // mark this match
        SendMessage(hwnd, SCI_INDICATORFILLRANGE, iPos, iSelCount);
        ttf.chrg.cpMin = ttf.chrgText.cpMin + iSelCount;
        if (ttf.chrg.cpMin == ttf.chrg.cpMax)
        break;
    }

    localFree(pszText);
    return;
}