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

Notepad2는 1.x 시절에는 북마크 기능이 있었다가 2.x로 업데이트되면서 사라졌다.
그런데, 이 기능은 의외로 쓸만하다.

북마크 기능을 사용하기 위해서는 아래와 같은 방법으로 수정하면 된다.

1. resource.h (src\)

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

#define IDC_BACKSLASHHELP               151
#define IDC_REGEXPHELP 152
#define IDC_WILDCARDHELP 153
#define IDC_WILDCARDSEARCH 154
#define IDS_BACKSLASHHELP 10019
#define IDS_REGEXPHELP 10020
#define IDS_WILDCARDHELP 10021
#define IDM_EDIT_BOOKMARKTOGGLE         40250
#define IDM_EDIT_BOOKMARKNEXT           40251
#define IDM_EDIT_BOOKMARKCLEAR          40252
#define IDM_EDIT_BOOKMARKPREV           40253
#define BME_EDIT_BOOKMARKTOGGLE         40254
#define BME_EDIT_BOOKMARKNEXT           40255
#define BME_EDIT_BOOKMARKCLEAR          40256
#define BME_EDIT_BOOKMARKPREV           40257


2. Edit.h (src\)

typedef struct _editfindreplace 내에 BOOL bWildcardSearch 를 추가한다.
추가 위치는 마지막 요소인 HWND hwnd; 바로  다음이다.
아래와 같은 모양이 된다.

typedef struct _editfindreplace
{
/* 중간 생략 */
HWND hwnd;
  BOOL bWildcardSearch; // 추가된 행


3. Edit.c (src\)

void EditTitleCase(HWND hwnd)의 선언부에 BOOL bPrevWasSpace 를 추가하고 초기화한다.
아래와 같은 내용을 추가하면 된다.

BOOL bPrevWasSpace = FALSE;


다음으로 INT_PTR CALLBACK EditFindReplaceDlgProcW() 에서 아래와 같은 내용을 찾아

case WM_INITDIALOG:
  {
    int cchSelection;
    char *lpszSelection;
    char *lpsz;

뒤에 한 줄을 추가한다. 아래와 같은 모습이 된다.

case WM_INITDIALOG:
  {
   int cchSelection;
   char *lpszSelection;
   char *lpsz;
    static BOOL bFirstTime = TRUE; // 추가된 행


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

if (cchSelection <= 500)
{
cchSelection = (int)SendMessage(lpefr->hwnd,SCI_GETSELTEXT,0,0);
lpszSelection = GlobalAlloc(GPTR,cchSelection+2);
SendMessage(lpefr->hwnd,SCI_GETSELTEXT,0,(LPARAM)lpszSelection);

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

// First time you bring up find/replace dialog, copy content from clipboard to find box (but only if nothing is selected in the editor)
if (lstrcmpA( lpszSelection , "" ) == 0 && bFirstTime )
{
char *pClip = EditGetClipboardText(hwndEdit);
if( lstrlenA( pClip ) > 0 && lstrlenA( pClip ) <= 500 )
{
GlobalFree(lpszSelection);
lpszSelection = GlobalAlloc(GPTR,lstrlenA( pClip )+2);
lstrcpynA( lpszSelection , pClip , 500 );
}
}
bFirstTime = FALSE;


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

if (lpefr->bTransformBS)
CheckDlgButton(hwnd,IDC_FINDTRANSFORMBS,BST_CHECKED);

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

if (lpefr->bWildcardSearch) CheckDlgButton(hwnd,IDC_WILDCARDSEARCH,BST_CHECKED);


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

case IDC_FINDREGEXP:
if (IsDlgButtonChecked(hwnd,IDC_FINDREGEXP) == BST_CHECKED)
CheckDlgButton(hwnd,IDC_FINDTRANSFORMBS,BST_UNCHECKED);
break;

아래와 같이 수정한다.

case IDC_FINDREGEXP:
if (IsDlgButtonChecked(hwnd,IDC_FINDREGEXP) == BST_CHECKED)
{
CheckDlgButton(hwnd,IDC_FINDTRANSFORMBS,BST_UNCHECKED);
CheckDlgButton(hwnd,IDC_WILDCARDSEARCH,BST_UNCHECKED); // Can not use wildcard search together with regexp
}
break;


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

case IDC_FINDTRANSFORMBS:
if (IsDlgButtonChecked(hwnd,IDC_FINDTRANSFORMBS) == BST_CHECKED)
CheckDlgButton(hwnd,IDC_FINDREGEXP,BST_UNCHECKED);
break;

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

// handle wildcard search checkbox
case IDC_WILDCARDSEARCH:
CheckDlgButton(hwnd,IDC_FINDREGEXP,BST_UNCHECKED);
//if (IsDlgButtonChecked(hwnd,IDC_FINDWILDCARDS) == BST_CHECKED)
// CheckDlgButton(hwnd,IDC_FINDREGEXP,BST_UNCHECKED);
break;


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

if (GetDlgItem(hwnd,IDC_REPLACETEXT))
GetDlgItemTextA2W(uCPEdit,hwnd,IDC_REPLACETEXT,lpefr->szReplace,COUNTOF(lpefr->szReplace));

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

lpefr->bWildcardSearch = (IsDlgButtonChecked(hwnd,IDC_WILDCARDSEARCH) == BST_CHECKED) ? TRUE : FALSE;


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

  case IDC_REPLACEINSEL:
bReplaceInitialized = TRUE;
EditReplaceAllInSelection(lpefr->hwnd,lpefr,TRUE);
break;
}

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

// Wildcard search will enable regexp, so I turn it off again otherwise it will be on in the gui
if( lpefr->bWildcardSearch && (lpefr->fuFlags & SCFIND_REGEXP) ) lpefr->fuFlags ^= SCFIND_REGEXP;


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

case NM_RETURN:
if (pnmhdr->idFrom == IDC_TOGGLEFINDREPLACE) {
if (GetDlgItem(hwnd,IDC_REPLACE))
PostMessage(GetParent(hwnd),WM_COMMAND,MAKELONG(IDM_EDIT_FIND,1),0);
else
PostMessage(GetParent(hwnd),WM_COMMAND,MAKELONG(IDM_EDIT_REPLACE,1),0);
}

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

  // Display help messages in the find/replace windows
else if (pnmhdr->idFrom == IDC_BACKSLASHHELP)
{
MsgBox(MBINFO,IDS_BACKSLASHHELP);
}
else if (pnmhdr->idFrom == IDC_REGEXPHELP)
{
MsgBox(MBINFO,IDS_REGEXPHELP);
}
else if (pnmhdr->idFrom == IDC_WILDCARDHELP)
{
MsgBox(MBINFO,IDS_WILDCARDHELP);
}


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

//=============================================================================
//
// EditFindNext()
//

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

// Wildcard search uses the regexp engine to perform a simple search with * ? as wildcards instead of more advanced and user-unfriendly regexp syntax
void EscapeWildcards( char* szFind2 , LPCEDITFINDREPLACE lpefr )
{
char szWildcardEscaped[512];
int iSource = 0;
int iDest = 0;

lpefr->fuFlags |= SCFIND_REGEXP;

while( szFind2[iSource] )
{
char c = szFind2[iSource];
if( c == '*' )
{
szWildcardEscaped[iDest++] = '.'; szWildcardEscaped[iDest] = '*';
}
else if( c == '?' )
{
szWildcardEscaped[iDest] = '.';
}
else
{
if( c == '.' || c == '^' || c == '$' || c == '\\' || c == '[' || c == ']' || c == '+' ) szWildcardEscaped[iDest++] = '\\';
szWildcardEscaped[iDest] = c;
}
iSource++;
iDest++;
}
szWildcardEscaped[iDest] = (char)NULL;
lstrcpynA(szFind2,szWildcardEscaped,COUNTOF(szWildcardEscaped));
}


마지막으로 아래와 같은 내용을 찾는다.
총 5번 나온다.

if (lstrlenA(szFind2) == 0)
{
InfoBox(0,L"MsgNotFound",IDS_NOTFOUND);
return FALSE;
}

바로 뒤에 아래와 같은 내용을 추가한다. (5번 모두 추가함)

if( lpefr->bWildcardSearch ) EscapeWildcards( szFind2 , lpefr );

to be continued…