Notepad2 4.2.25 패치 준비#4: Mark Occurrences 기능 추가

구글 코드에서 진행되고 있는 Notepad2-mod 프로젝트에는 알려진 Notepad2의 대부분의 패치가 다 적용되어있다.
그 중 재미있는 패치를 하나 찾았는데, Mark Occurrences라는 기능이다.

블럭을 지정하면 그 블럭과 똑같은 내용을 찾아 표시해주는 기능이다.


의외로 유용할 것 같은 기능이라 적용하기로 했다.

이 패치를 적용하려면 다섯개의 파일을 수정해야 한다.
대상은 resource.h, Notepad2.rc, Edit.h, Edit.c, Notepad2.c 이며, 모두 src\ 폴더에 들어있다.

1. resource.h

아래와 같은 내용을 추가한다.
40445~40448이 이미 사용중이면 적절하게 다른 값으로 수정한다.

#define IDM_VIEW_MARKOCCURRENCES_RED    40445
#define IDM_VIEW_MARKOCCURRENCES_GREEN  40446
#define IDM_VIEW_MARKOCCURRENCES_BLUE   40447
#define IDM_VIEW_MARKOCCURRENCES_OFF    40448


2. notepad2.rc

아래와 같은 내용을 찾아서…

MENUITEM "Selection &Margin\tCtrl+Shift+M", IDM_VIEW_MARGIN

바로 다음에 아래와 같은 내용을 추가한다.

  POPUP "Mar&k Occurrences"
  BEGIN
      MENUITEM "&Red",                        IDM_VIEW_MARKOCCURRENCES_RED
      MENUITEM "&Green",                      IDM_VIEW_MARKOCCURRENCES_GREEN
      MENUITEM "&Blue",                       IDM_VIEW_MARKOCCURRENCES_BLUE
      MENUITEM "&Off",                        IDM_VIEW_MARKOCCURRENCES_OFF
  END


3. Edit.h

아래와 같은 내용을 적절한 위치에 추가한다.

void  EditMarkAll(HWND,int);


4. Edit.c

아래와 같은 내용을 적절한 위치에 추가한다.

//=============================================================================
//
// EditMarkAll()
// Mark all occurrences of the text currently selected (by Aleksandar Lekov)
//
void EditMarkAll(HWND hwnd, int iMarkOccurrences)
{
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;

// 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, SCFIND_MATCHCASE | SCFIND_WHOLEWORD, (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;
}


5. Notepad2.c

우선, 소스의 맨 앞부분에 아래와 같은 선언을 추가한다.
확장자가 cpp가 아니라 c라는 점을 고려해서 적절한 위치에 추가해야 한다.

int       iMarkOccurrences;


다음으로, void MsgInitMenu(HWND hwnd,WPARAM wParam,LPARAM lParam)에서 아래와 같은 부분을 찾아…

CheckCmd(hmenu,IDM_VIEW_MARGIN,bShowSelectionMargin);

바로 다음에 아래와 같은 내용을 추가한다.

  switch (iMarkOccurrences)
{
case 0: i = IDM_VIEW_MARKOCCURRENCES_OFF;break;
case 1: i = IDM_VIEW_MARKOCCURRENCES_RED;break;
case 2: i = IDM_VIEW_MARKOCCURRENCES_GREEN;break;
case 3: i = IDM_VIEW_MARKOCCURRENCES_BLUE;break;
}
CheckMenuRadioItem(hmenu,IDM_VIEW_MARKOCCURRENCES_RED,IDM_VIEW_MARKOCCURRENCES_OFF,i,MF_BYCOMMAND);


다음, LRESULT MsgCommand(HWND hwnd,WPARAM wParam,LPARAM lParam)의 case 중에 아래 내용을 추가한다.

  case IDM_VIEW_MARKOCCURRENCES_OFF:
iMarkOccurrences = 0;
// clear all marks
SendMessage(hwndEdit, SCI_SETINDICATORCURRENT, 1, 0);
SendMessage(hwndEdit, SCI_INDICATORCLEARRANGE, 0, (int)SendMessage(hwndEdit,SCI_GETLENGTH,0,0));
break;

case IDM_VIEW_MARKOCCURRENCES_RED:
iMarkOccurrences = 1;
EditMarkAll(hwndEdit, iMarkOccurrences);
break;

case IDM_VIEW_MARKOCCURRENCES_GREEN:
iMarkOccurrences = 2;
EditMarkAll(hwndEdit, iMarkOccurrences);
break;

case IDM_VIEW_MARKOCCURRENCES_BLUE:
iMarkOccurrences = 3;
EditMarkAll(hwndEdit, iMarkOccurrences);
break;


다음으로 LRESULT MsgNotify(HWND hwnd,WPARAM wParam,LPARAM lParam)에서 아래와 같은 내용을 찾아…

  // Brace Match
if (bMatchBraces)

바로 에 아래와 같은 내용을 추가한다.

  // mark occurrences of text currently selected
EditMarkAll(hwndEdit, iMarkOccurrences);


또, void LoadSettings()에서 아래와 같은 내용을 찾아…

  bViewWhiteSpace = IniSectionGetInt(pIniSection,L"ViewWhiteSpace",0);

바로 에 아래와 같은 내용을 추가한다.

  iMarkOccurrences = IniSectionGetInt(pIniSection,L"MarkOccurrences",3);


마지막으로, void SaveSettings(BOOL bSaveSettingsNow)에서 아래와 같은 부분을 찾아…

  IniSectionSetInt(pIniSection,L"ViewWhiteSpace",bViewWhiteSpace);

바로 다음에 아래와 같은 내용을 추가한다.

  IniSectionSetInt(pIniSection,L"MarkOccurrences",iMarkOccurrences);