Sablog Models/자작자작

Notepad2 패치에 발을 담그다 - (1) Mark Occurrences, Case insensitively

어­리 2011. 5. 25. 01:42
블루앤라이브 님의 Notepad2 4.2.25 패치 중 Notepad2-mod에서 차용된 것이 있으니,
Notepad2 4.2.25 패치 준비#4: Mark Occurrences 기능 추가
링크된 블로그 글에도 있다시피, 다음과 같이 선택된 부분과 일치하는 단어를 강조해 주는 기능이다.
(Notepad2 4,2,25 original w/ Scintilla 2.25 + 'Mark Occurrences' original)
이 기능을 조금 바꾸어, 아래와 같이 대소문자에 무관하게 동작할 수 있도록 했다.
어셈블리어 소스 코드라든지, 같은 단어가 대소문자를 달리해 나타나는 보통의 글 등에서 쓸 수 있다.
물론 메뉴에서 적용 여부를 선택 가능하며, 기본값은 대소문자 구분이다.
참고로, 대부분의 언어에서 식별자는 대소문자를 구분하기 때문에, 이를 빼 버리는 건 미친 짓이다.



블루앤라이브 님과 같은 순서로 설명하며, 기존 패치와 달라진 줄을 강조했다.

1. resource.h

적당한 위치에 아래와 같은 내용을 추가한다. 40445~40449가 사용 중이면 적당히 수정한다.

#define IDM_VIEW_MARKOCCURRENCES_CASE   40445
#define IDM_VIEW_MARKOCCURRENCES_RED    40446
#define IDM_VIEW_MARKOCCURRENCES_GREEN  40447
#define IDM_VIEW_MARKOCCURRENCES_BLUE   40448
#define IDM_VIEW_MARKOCCURRENCES_OFF    40449
* 적당한 위치: 번호 순으로 정렬되어 있으므로 번호 순서에 맞게 넣어야 위험하지 않다.
* 적당히 수정: 사용되지 않는 번호.


2. Notepad2.rc

다음과 같은 내용을 찾아

MENUITEM "Selection &Margin\tCtrl+Shift+M", IDM_VIEW_MARGIN
바로 아래에 다음 내용을 추가한다.
  POPUP "Mar&k Occurrences"
  BEGIN
      MENUITEM "Matc&h case",                 IDM_VIEW_MARKOCCURRENCES_CASE
      MENUITEM SEPARATOR
      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,BOOL);


4. Edit.c

다음과 같은 내용을 적당한 위치에 추가한다.

//=============================================================================
//
//  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;
 
  // 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 | SCFIND_WHOLEWORD) : 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;
}
* Scintilla에 넘기는 인자가 bMarkOccurrencesCaseSensitive에 따라 바뀐다.


5. Notepad2.c

소스의 앞부분, 다른 전역변수들과 함께 다음 두 변수 선언을 추가한다.

int       iMarkOccurrences;
BOOL      bMarkOccurrencesCaseSensitive;

void MsgInitMenu(HWND hwnd,WPARAM wParam,LPARAM lParam) 내의 다음 두 줄 사이에,
  CheckCmd(hmenu,IDM_VIEW_MARGIN,bShowSelectionMargin);
  CheckCmd(hmenu,IDM_VIEW_SHOWWHITESPACE,bViewWhiteSpace);
다음과 같은 내용을 추가한다.
  CheckCmd(hmenu,IDM_VIEW_MARKOCCURRENCES_CASE,bMarkOccurrencesCaseSensitive);
  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 IDM_VIEW_MARGIN: ~ break;과 case IDM_VIEW_SHOWWHITESPACE: ~ break; 사이에 다음과 같은 내용을 추가한다.
    case IDM_VIEW_MARKOCCURRENCES_CASE:
      bMarkOccurrencesCaseSensitive = (bMarkOccurrencesCaseSensitive) ? FALSE : TRUE;
      // clear all marks and remark with changed case sensitivity
      SendMessage(hwndEdit, SCI_SETINDICATORCURRENT, 1, 0);
      SendMessage(hwndEdit, SCI_INDICATORCLEARRANGE, 0, (int)SendMessage(hwndEdit,SCI_GETLENGTH,0,0));
      EditMarkAll(hwndEdit, iMarkOccurrences, bMarkOccurrencesCaseSensitive);
      break;
    
    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, bMarkOccurrencesCaseSensitive);
      break;
 
    case IDM_VIEW_MARKOCCURRENCES_GREEN:
      iMarkOccurrences = 2;
      EditMarkAll(hwndEdit, iMarkOccurrences, bMarkOccurrencesCaseSensitive);
      break;
 
    case IDM_VIEW_MARKOCCURRENCES_BLUE:
      iMarkOccurrences = 3;
      EditMarkAll(hwndEdit, iMarkOccurrences, bMarkOccurrencesCaseSensitive);
      break;

LRESULT MsgNotify(HWND hwnd,WPARAM wParam,LPARAM lParam) 내에서 다음 내용을 찾아서,
// Brace Match
if (bMatchBraces)
이 바로 위에 다음과 같은 내용을 추가한다.
// mark occurrences of text currently selected
EditMarkAll(hwndEdit, iMarkOccurrences, bMarkOccurrencesCaseSensitive);

void LoadSettings() 내에서 다음 내용을 찾아서,
bViewWhiteSpace = IniSectionGetInt(pIniSection,L"ViewWhiteSpace",0);
이 바로 위에 다음과 같은 내용을 추가한다.
  bMarkOccurrencesCaseSensitive = IniSectionGetInt(pIniSection,L"MarkOccurrencesCaseSensitively",1);
  if (bMarkOccurrencesCaseSensitive) bMarkOccurrencesCaseSensitive = 1;

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

마지막으로 void SaveSettings() 내의 다음 두 줄 사이에,
  IniSectionSetInt(pIniSection,L"ShowLineNumbers",bShowLineNumbers);
  IniSectionSetInt(pIniSection,L"ViewWhiteSpace",bViewWhiteSpace);
다음 두 줄을 추가한다.
  IniSectionSetInt(pIniSection,L"MarkOccurrencesCaseSensitively",bMarkOccurrencesCaseSensitive);
  IniSectionSetInt(pIniSection,L"MarkOccurrences",iMarkOccurrences);


시험적으로 구상한 패치 소스 패치(!)였지만 놀랍게도 성공적으로 작동했다.(?)
나도 이제 소스 패치 변태야 엉엉 ㅠㅠ 하라는 과제는 안 하고

앞으로 몇 가지 패치를 더 만들어 낼지도 모르겠습니다. :)


다음 글. 한글은 전영역에서 찾는다든지 하는 옵션이 추가되어 있는데 정신 건강에 좋지는 않습니다.