. 줄 찾아가기

이 기능은 사용자가 이동할 줄번호를 정확하게 알고 있을 때 직접 찾아가는 기능이다. 지정한 줄로 위치를 옮기는 GotoLine 함수는 이미 북마크 기능을 작성할 때 만들어 둔 적이 있다. 하지만 이 함수가 찾아가는 줄은 정렬된 결과인 pLine에서의 줄, 즉 화면상의 물리적인 줄이다. 사용자에게 줄이라는 개념은 개행코드로 분리된 문단의 개념이므로 이 함수를 확장하여 문단을 찾아가는 기능을 추가한다. 물론 함수의 원형에 변화가 없도록 하여 기존 코드는 영향을 받지 않도록 해야 한다. GotoLine 함수를 다음과 같이 작성하고 헤더 파일의 원형도 수정하자.

 

 

void CApiEdit::GotoLine(int Line, int Col/*=0*/, int Type/*=0*/)

{

     int tLine;

     ClearSelection();

 

     switch (Type) {

     case 0:

          tLine=min(Line,TotalLine-1);

          off=GetOffFromRC(Line,Col);

          break;

     case 1:

          for (tLine=0;tLine<TotalLine-1;tLine++) {

              if (pLine[tLine].nPara == Line)

                   break;

          }

          off=pLine[tLine].Start+Col;

          break;

     }

     SetCaret();

}

 

함수의 기능이 확장되어 줄만 찾아가는 것이 아니라 지정한 줄의 지정한 칸까지 찾아가도록 하였다. Type 0이면 줄을 찾는 것이고 1이면 문단을 찾는 것이다. 추가된 두 개의 인수 Col Type은 모두 디폴트값 0을 가진다. 줄을 찾아갈 때는 GetOffFromRC 함수로 간단하게 이동할 수 있으나 문단 찾기는 별도의 함수가 없으므로 일단 선형 검색하였다. 이 부분은 차후에 좀 더 효율적인 코드로 수정될 것이다.

줄 찾기 대화상자는 현재 캐럿이 있는 곳의 줄번호를 보여주고 사용자가 줄번호를 편집하면 변경된 줄로 이동하도록 할 것이다. 그래서 이 대화상자를 사용하면 현재 위치에서 상대적으로 100줄 뒤, 100줄 이전 등으로 이동할 수도 있다. 아직 현재 줄을 조사할 수 있는 함수는 제공되지 않으므로 함수를 하나 만들도록 하자. 먼저 헤더 파일에 다음 열거형을 선언한다.

 

enum { AE_INFO_ROW, AE_INFO_COL, AE_INFO_PARA, AE_INFO_LINE, AE_INFO_PARACOL };

 

특정 오프셋으로부터 어떤 정보를 구할 것인지를 지정하는 열거형이다. 구하는 정보의 종류가 많기 때문에 정수형을 직접 쓰는 것보다 열거형을 쓰는 것이 더 좋을 것 같다. 다음 멤버함수를 ApiEdit.cpp에 추가한다.

 

int CApiEdit::GetInfoFromOff(int Type, int nPos/*=-1*/)

{

     int r,c;

     int ret;

 

     if (nPos==-1) {

          nPos=off;

     }

 

     GetRCFromOff(nPos,r,c);

 

     switch (Type) {

     case AE_INFO_ROW:

          ret=r;

          break;

     case AE_INFO_COL:

          ret=c;

          break;

     case AE_INFO_PARA:

          ret=pLine[r].nPara;

          break;

     case AE_INFO_LINE:

          ret=pLine[r].nLine;

          break;

     case AE_INFO_PARACOL:

          while (pLine[r].nLine != 0) r--;

          return nPos-pLine[r].Start;

     }

 

     return ret;

}

 

이 함수는 현재 위치로부터 열, , 문단, 문단 내의 줄번호, 문단 내의 칸 번호 등의 다양한 값을 조사한다. 줄 이동 대화상자는 정수값 하나를 입력받으며 다음과 같이 디자인되어 있다.

업다운 컨트롤이 에디트의 버디로 지정되어 있어 마우스로 이동할 줄을 선택할 수 있도록 하였으며 에디트는 숫자만 입력받을 수 있으므로 ES_NUMBER 속성을 주었다. 이 대화상자의 메시지 처리 함수는 다음과 같다.

 

BOOL CALLBACK GotoLineDlgProc(HWND hDlg,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

     HWND hActive;

     SInfo *pSi;

     int nPara;

 

     switch(iMessage)

     {

     case WM_INITDIALOG:

          MoveToParentCenter(hDlg);

          hActive=(HWND)SendMessage(g_hMDIClient,WM_MDIGETACTIVE,0,NULL);

          pSi=(SInfo *)GetWindowLong(hActive,0);

          nPara=pSi->Ae.GetInfoFromOff(AE_INFO_PARA)+1;

          SetDlgItemInt(hDlg,IDC_GOLINENUM,nPara,FALSE);

          SendDlgItemMessage(hDlg,IDC_GOTOLINESPIN,UDM_SETRANGE,0,MAKELPARAM(UD_MAXVAL,1));

          return TRUE;

     case WM_COMMAND:

          switch (LOWORD(wParam))

          {

          case IDOK:

              EndDialog(hDlg,GetDlgItemInt(hDlg,IDC_GOLINENUM,NULL,FALSE));

              return TRUE;

          case IDCANCEL:

              EndDialog(hDlg,-1);

              return TRUE;

          }

          return FALSE;

     }

     return FALSE;

}

 

WM_INITDIALOG에서 활성 차일드의 현재 문단번호를 구해 에디트 컨트롤에 대입하였다. 업다운 컨트롤은 디폴트로 위쪽 버튼이 감소, 아래쪽 버튼이 증가이며 우리의 직관적인 생각과는 반대로 되어 있다. 업다운 컨트롤의 범위가 반대로 되어 있는 이유가 좀 이상하게 생각되겠지만 스크롤바도 사실 반대로 되어 있어 아래쪽 버튼이 증가이고 위쪽 버튼이 감소이다. 지금 우리는 업다운 컨트롤을 스크롤 용도로 쓰는 것이 아니므로 이 범위를 다시 반대로 뒤집어 주었다. 즉 위쪽 버튼이 증가이고 아래쪽 버튼이 감소이다.

사용자는 에디트 컨트롤에 이동하고자 하는 줄번호를 입력하거나 업다운 컨트롤로 이동할 줄번호를 증감한 후 확인 버튼을 클릭한다. 이때 EndDialog의 두 번째 인수인 종료코드에 사용자가 선택한 줄번호를 리턴하였다. 메뉴에서 이 대화상자를 부르고 대화상자의 리턴값으로 전달된 문단으로 이동한다. 단 취소했을 경우에는 -1이 넘어 오는데 이때는 아무 것도 할 필요가 없다.

 

void OnCommand(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

     ....

     case IDM_SEARCH_GOTOLINE:

          s=DialogBox(g_hInst,MAKEINTRESOURCE(IDD_GOTOLINE),hWnd,GotoLineDlgProc);

          if (s != -1) {

              pSi->Ae.GotoLine(s-1,0,1);

          }

          break;

 

사용자가 가고 싶은 줄번호에서 1을 뺀 줄로 이동하는데 사람들은 자연수를 쓰지만 ApiEdit의 문단번호는 제로 베이스(Zero Base)임을 유의해야 한다. 사용자가 5번 줄로 이동하고 싶다면 실제로는 4번 문단으로 이동해야 한다. 줄 찾기 기능은 단축키 <Ctrl+G>로 정의되어 있다. 다음 함수는 대화상자를 부모 윈도우의 중앙으로 이동시키는데 Util.cpp에 추가한다.

 

void MoveToParentCenter(HWND hWnd)

{

     RECT wrt,crt;

 

     GetWindowRect(GetParent(hWnd),&wrt);

     GetWindowRect(hWnd,&crt);

     SetWindowPos(hWnd,HWND_NOTOPMOST,wrt.left+(wrt.right-wrt.left)/2-(crt.right-crt.left)/2,

          wrt.top+(wrt.bottom-wrt.top)/2-(crt.bottom-crt.top)/2,0,0,SWP_NOSIZE);

}

 

줄 찾기 대화상자의 WM_INITDIALOG에서 이 함수를 호출하여 메인 윈도우의 중앙에 열리도록 하였다. 앞으로도 많은 대화상자를 열게 될 텐데 그때마다 부모 윈도우의 중앙 좌표를 계산하자면 번거로우므로 아예 유틸리티 함수를 만들어 두었다. 이 함수는 부모 윈도우의 좌표, 크기 그리고 자신의 크기를 고려하여 정 중앙으로 대화상자를 옮겨준다.