Notepad2 4.2.25 패치 준비#2: 북마크 기능 추가 (2/2)

4. Notepad2.c (src\)

파일 앞부분(선언부)에 다음과 같은 내용을 추가한다.

//Graphics for bookmark indicator
/* XPM */
static char * bookmark_pixmap[] = {
"11 11 44 1",
" \tc #EBE9ED",
".\tc #E5E3E7",
"+\tc #767C6D",
"@\tc #2A3120",
"#\tc #1B2312",
"$\tc #333B28",
"%\tc #E3E1E5",
"&\tc #D8D6DA",
"*\tc #444D38",
"=\tc #3F5C19",
"-\tc #63AD00",
";\tc #73C900",
">\tc #64AF00",
",\tc #3D5718",
"'\tc #3E4634",
")\tc #7B8172",
"!\tc #42601A",
"~\tc #74CB00",
"{\tc #71C600",
"]\tc #3A5317",
"^\tc #707668",
"/\tc #3F4931",
"(\tc #262C1D",
"_\tc #2F3A1E",
":\tc #72C700",
"<\tc #74CA00",
"[\tc #0E1109",
"}\tc #3C462F",
"|\tc #62AC00",
"1\tc #21271A",
"2\tc #7A8071",
"3\tc #405D19",
"4\tc #3D5A18",
"5\tc #D9D7DB",
"6\tc #4E5841",
"7\tc #72C800",
"8\tc #63AC00",
"9\tc #3F5B19",
"0\tc #3D4533",
"a\tc #DFDDE0",
"b\tc #353E29",
"c\tc #29331B",
"d\tc #7B8272",
"e\tc #DDDBDF",
" ",
" .+@#$+% ",
" &*=-;>,' ",
" )!~~~~{]^ ",
" /-~~~~~>( ",
" _:~~~~~<[ ",
" }|~~~~~|1 ",
" 23~~~~;4+ ",
" 56=|7890 ",
" a2bc}de ",
" "};


다음으로 아래와 같은 내용을 찾아서

LRESULT MsgCommand(HWND hwnd,WPARAM wParam,LPARAM lParam)
{
  switch(LOWORD(wParam))
  {

아래와 같은 case문 4개를 추가한다.

// Main Bookmark Functions
case BME_EDIT_BOOKMARKNEXT:
{
    int iPos = (int)SendMessage( hwndEdit , SCI_GETCURRENTPOS , 0 , 0);
    int iLine = (int)SendMessage( hwndEdit , SCI_LINEFROMPOSITION , iPos , 0 );

    int bitmask = 1;
    int iNextLine = (int)SendMessage( hwndEdit , SCI_MARKERNEXT , iLine+1 , bitmask );
    if( iNextLine == -1 )
    {
        iNextLine = (int)SendMessage( hwndEdit , SCI_MARKERNEXT , 0 , bitmask );
    }

    if( iNextLine != -1 )
    {
        SciCall_EnsureVisible(iNextLine);
        SendMessage( hwndEdit , SCI_GOTOLINE , iNextLine , 0 );
        SciCall_SetYCaretPolicy(CARET_SLOP|CARET_STRICT|CARET_EVEN,10);
        SciCall_ScrollCaret();
        SciCall_SetYCaretPolicy(CARET_EVEN,0);
    }
    break;
}

case BME_EDIT_BOOKMARKPREV:
{
    int iPos = (int)SendMessage( hwndEdit , SCI_GETCURRENTPOS , 0 , 0);
    int iLine = (int)SendMessage( hwndEdit , SCI_LINEFROMPOSITION , iPos , 0 );

    int bitmask = 1;
    int iNextLine = (int)SendMessage( hwndEdit , SCI_MARKERPREVIOUS , iLine-1 , bitmask );
    if( iNextLine == -1 )
    {
        int nLines = (int)SendMessage( hwndEdit , SCI_GETLINECOUNT , 0 , 0 );
        iNextLine = (int)SendMessage( hwndEdit , SCI_MARKERPREVIOUS , nLines , bitmask );
    }

    if( iNextLine != -1 )
    {
        SciCall_EnsureVisible(iNextLine);
        SendMessage( hwndEdit , SCI_GOTOLINE , iNextLine , 0 );
        SciCall_SetYCaretPolicy(CARET_SLOP|CARET_STRICT|CARET_EVEN,10);
        SciCall_ScrollCaret();
        SciCall_SetYCaretPolicy(CARET_EVEN,0);
    }

    break;
}

case BME_EDIT_BOOKMARKTOGGLE:
{
    int iPos = (int)SendMessage( hwndEdit , SCI_GETCURRENTPOS , 0 , 0);
    int iLine = (int)SendMessage( hwndEdit , SCI_LINEFROMPOSITION , iPos , 0 );

    int bitmask = (int)SendMessage( hwndEdit , SCI_MARKERGET , iLine , 0 );
    if( bitmask & 1 )
    {
        // unset
        SendMessage( hwndEdit , SCI_MARKERDELETE , iLine , 0 );
    }
    else
    {
        if( bShowSelectionMargin )
        {
            SendMessage( hwndEdit , SCI_MARKERDEFINEPIXMAP , 0 , (LPARAM)bookmark_pixmap );
        }
        else
        {
            SendMessage( hwndEdit , SCI_MARKERSETBACK , 0 , 216 | (255 << 8) | (216 << 16) );
            SendMessage( hwndEdit , SCI_MARKERDEFINE , 0 , SC_MARK_BACKGROUND );
        }

        SendMessage( hwndEdit , SCI_MARKERADD , iLine , 0 );
    }

    break;
}

case BME_EDIT_BOOKMARKCLEAR:
{
    SendMessage( hwndEdit , SCI_MARKERDELETEALL , -1 , 0 );

    break;
}


다음으로 아래와 같은 내용을 찾아서

case IDM_VIEW_MARGIN:
bShowSelectionMargin = (bShowSelectionMargin) ? FALSE : TRUE;
SendMessage(hwndEdit,SCI_SETMARGINWIDTHN,1,(bShowSelectionMargin)?16:0);

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

//Depending on if the margin is visible or not, choose different bookmark indication
if( bShowSelectionMargin )
{
SendMessage( hwndEdit , SCI_MARKERDEFINEPIXMAP , 0 , (LPARAM)bookmark_pixmap );
}
else
{
SendMessage( hwndEdit , SCI_MARKERSETBACK , 0 , 216 | (255 << 8) | (216 << 16) );
SendMessage( hwndEdit , SCI_MARKERDEFINE , 0 , SC_MARK_BACKGROUND );
}


다음으로 아래와 같은 내용을 찾아서

int iCurPos    = (int)SendMessage(hwndEdit,SCI_GETCURRENTPOS,0,0);
//int iAnchorPos = (int)SendMessage(hwndEdit,SCI_GETANCHOR,0,0);
int iCurLine = (int)SendMessage(hwndEdit,SCI_LINEFROMPOSITION,(WPARAM)iCurPos,0);
//int iLineLength = (int)SendMessage(hwndEdit,SCI_LINELENGTH,iCurLine,0);
//int iIndentBefore = (int)SendMessage(hwndEdit,SCI_GETLINEINDENTATION,(WPARAM)iCurLine-1,0);

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

// Move bookmark along with line if inserting lines (pressing return at beginning of line) because Scintilla does not do this for us
if( iCurLine > 0 )
{
int iPrevLineLength = (int)SendMessage(hwndEdit,SCI_GETLINEENDPOSITION,iCurLine-1,0) - (int)SendMessage(hwndEdit,SCI_POSITIONFROMLINE,iCurLine-1,0) ;
if( iPrevLineLength == 0 )
{
int bitmask = (int)SendMessage( hwndEdit , SCI_MARKERGET , iCurLine-1 , 0 );
if( bitmask & 1 )
{
SendMessage( hwndEdit , SCI_MARKERDELETE , iCurLine-1 , 0 );
SendMessage( hwndEdit , SCI_MARKERADD , iCurLine , 0 );
}
}
}


다음으로 void UpdateStatusbar() 함수를 찾아서 선언부에 다음과 같은 내용을 추가한다.

int iSelStart;
int iSelEnd;
int iLineStart;
int iLineEnd;
int iStartOfLinePos;
int iLinesSelected;
WCHAR tchLinesSelected[32];


마지막으로 아래와 같은 내용을 찾아서

if (!bMarkLongLines)
FormatString(tchDocPos,COUNTOF(tchDocPos),IDS_DOCPOS,tchLn,tchLines,tchCol,tchSel);
else
FormatString(tchDocPos,COUNTOF(tchDocPos),IDS_DOCPOS2,tchLn,tchLines,tchCol,tchCols,tchSel);

이렇게 수정한다.

// Print number of lines selected lines in statusbar
iSelStart = (int)SendMessage( hwndEdit , SCI_GETSELECTIONSTART , 0 , 0 );
iSelEnd = (int)SendMessage( hwndEdit , SCI_GETSELECTIONEND , 0 , 0 );
iLineStart = (int)SendMessage( hwndEdit , SCI_LINEFROMPOSITION , iSelStart , 0 );
iLineEnd = (int)SendMessage( hwndEdit , SCI_LINEFROMPOSITION , iSelEnd , 0 );
iStartOfLinePos = (int)SendMessage( hwndEdit , SCI_POSITIONFROMLINE , iLineEnd , 0 );
iLinesSelected = iLineEnd - iLineStart;
if( iSelStart != iSelEnd && iStartOfLinePos != iSelEnd ) iLinesSelected += 1;
wsprintf(tchLinesSelected,L"%i",iLinesSelected);
FormatNumberStr(tchLinesSelected);

if (!bMarkLongLines)
FormatString(tchDocPos,COUNTOF(tchDocPos),IDS_DOCPOS,tchLn,tchLines,tchCol,tchSel,tchLinesSelected);
else
FormatString(tchDocPos,COUNTOF(tchDocPos),IDS_DOCPOS2,tchLn,tchLines,tchCol,tchCols,tchSel,tchLinesSelected);


5. Notepad2.rc (src\)


아래와 같은 내용을 찾아서

  MENUITEM "Select To Pre&vious\tShift+F2", IDM_EDIT_SELTOPREV
END

그 뒤에 아래와 같은 내용을 추가한다.

POPUP "Bookmarks"
BEGIN
MENUITEM "Toggle\tAlt+F2", BME_EDIT_BOOKMARKTOGGLE
MENUITEM SEPARATOR
MENUITEM "Goto Next\tCtrl+F2", BME_EDIT_BOOKMARKNEXT
MENUITEM "Goto Previous\tCtrl+Shift+F2", BME_EDIT_BOOKMARKPREV
MENUITEM SEPARATOR
MENUITEM "Clear All\tCtrl+Alt+F2", BME_EDIT_BOOKMARKCLEAR
END


다음으로 아래와 같은 내용을 찾아서

VK_F12,         IDM_VIEW_FONT,          VIRTKEY, ALT, NOINVERT
VK_F12, IDM_VIEW_USE2NDDEFAULT, VIRTKEY, SHIFT, NOINVERT

그 뒤에 아래와 같은 내용을 추가한다.

VK_F2,          BME_EDIT_BOOKMARKNEXT,  VIRTKEY, CONTROL, NOINVERT
VK_F2, BME_EDIT_BOOKMARKTOGGLE, VIRTKEY, ALT, NOINVERT
VK_F2, BME_EDIT_BOOKMARKCLEAR, VIRTKEY, CONTROL, ALT, NOINVERT
VK_F2, BME_EDIT_BOOKMARKPREV, VIRTKEY, SHIFT, CONTROL, NOINVERT


다음으로 아래와 같은 내용을 찾아서

CONTROL         "Regular &expression search",IDC_FINDREGEXP,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,7,73,101,10
CONTROL "&Transform backslashes",IDC_FINDTRANSFORMBS,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,7,85,89,10

아래와 같이 수정한다.

CONTROL         "Regular &expression search",IDC_FINDREGEXP,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,7,73,96,10
CONTROL "&Transform backslashes",IDC_FINDTRANSFORMBS,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,7,85,85,10


다음으로 아래와 같은 내용을 찾아서

PUSHBUTTON      "Close",IDCANCEL,215,41,50,14
CONTROL "<a>Goto Replace (Ctrl+H)</a>",IDC_TOGGLEFINDREPLACE,
"SysLink",0x0,125,85,74,10

그 뒤에 아래의 내용을 추가한다.

CONTROL         "<a>(?)</a>",IDC_BACKSLASHHELP,"SysLink",0x0,96,85,14,10
CONTROL "<a>(?)</a>",IDC_REGEXPHELP,"SysLink",0x0,106,73,14,10
CONTROL "W&ildcard Search",IDC_WILDCARDSEARCH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,125,61,63,10
CONTROL "<a>(?)</a>",IDC_WILDCARDHELP,"SysLink",0x0,191,61,14,10


다음 수정할 내용은 앞과 비슷하지만, 잘 보면 다르다. (찾기와 바꾸기가 따로 구성되어 있음)
아래와 같은 내용을 찾아서

CONTROL         "Regular &expression search",IDC_FINDREGEXP,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,7,102,101,10
CONTROL "&Transform backslashes",IDC_FINDTRANSFORMBS,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,7,114,89,10

아래와 같이 수정한다.

CONTROL         "Regular &expression search",IDC_FINDREGEXP,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,7,102,97,10
CONTROL "&Transform backslashes",IDC_FINDTRANSFORMBS,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,7,114,86,10


다음으로 아래와 같은 내용을 찾아서

PUSHBUTTON      "Close",IDCANCEL,215,92,50,14
CONTROL "<a>Goto Find (Ctrl+F)</a>",IDC_TOGGLEFINDREPLACE,
"SysLink",0x0,125,114,74,100

그 뒤에 아래의 내용을 추가한다.

CONTROL         "<a>(?)</a>",IDC_BACKSLASHHELP,"SysLink",0x0,94,114,14,10
CONTROL "<a>(?)</a>",IDC_REGEXPHELP,"SysLink",0x0,107,102,14,10
CONTROL "W&ildcard Search",IDC_WILDCARDSEARCH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,125,90,63,10
CONTROL "<a>(?)</a>",IDC_WILDCARDHELP,"SysLink",0x0,191,90,14,10


다음으로 아래와 같은 내용을 찾아서

IDS_DOCPOS              "Ln %s : %s   Col %s   Sel %s"
IDS_DOCPOS2 "Ln %s : %s Col %s : %s Sel %s"

아래와 같이 수정한다.

IDS_DOCPOS              "Ln %s / %s    Col %s   Sel %s   Sel Ln %s"
IDS_DOCPOS2 "Ln %s / %s Col %s / %s Sel %s Sel Ln %s"


마지막으로 다음으로 같은 내용을 찾아서

  IDS_LINKDESCRIPTION     "Edit with Notepad2"
IDS_FILTER_ALL "All files (*.*)|*.*|"
IDS_FILTER_EXE "Executable files (*.exe;*.com;*.bat;*.cmd;*.lnk;*.pif)|*.exe;*.com;*.bat;*.cmd;*.lnk;*.pif|All files (*.*)|*.*|"
END

그 뒤에 아래의 내용을 추가한다.

STRINGTABLE 
BEGIN
IDS_BACKSLASHHELP "Backslash Transformations\n\n\\a\tAlert (BEL, Ascii 7)\n\\b\tBackspace (BS, Ascii 8)\n\\f\tFormfeed (FF, Ascii 12)\n\\n\tNewline (LF, Ascii 10)\n\\r\tCarriage return (CR, Ascii 13)\n\\t\tHorizontal Tab (HT, Ascii 9)\n\\v\tVertical Tab (VT, Ascii 11)\n\\ooo\tOctal Value\n\\xhh\tHexadecimal Value\n\\\\\tBackslash"
IDS_REGEXPHELP "RegExp Syntax (Single Lines Only)\n\n.\tAny character\n^\tStart of a line\n$\tEnd of a line\n\\<\tStart of a word\n\\>\tEnd of a word\n[...]\tA set of chars ([abc]) or a range ([a-z])\n[^...]\tChars NOT in the set or range\n\\d\tAny decimal digit\n\\D\tAny non-digit char\n\\s\tAny whitespace char\n\\S\tNot a whitespace char\n\\w\tAny ""word"" char\n\\W\tAny ""non-word"" char\n\\xHH\tChar with hex code HH\n*\tMatches preceding 0 or more times\n+\tMatches preceding 1 or more times\n\\(\tStart of a region\n\\)\tEnd of a region\n\\n\tRefers to a region when replacing (n is 1-9)\n"
IDS_WILDCARDHELP "Wildcard Search\n\n*\tMatches zero or more characters.\n?\tMatches exactly one character. "
END


이렇게 수정하면 북마크 기능을 Notepad2에서 사용할 수 있다.

덧. 본 포스트의 내용은 직접 작성한 것이 아니라 Google Code의 Notepad-mod에 공개된 코드를 분석한 것임.