. 북마크 관련 함수

북마크 기능은 다른 편집기능과는 구별되는 다소 독립적인 기능이며 이 기능으로 인해 다른 함수들이 영향을 받는 부분은 거의 없다. 그래서 함수도 따로 한 세트를 준비해야 한다. 물론 이 함수들은 GetRCFromOff, pLine 배열 등 이미 만들어져 있는 유틸리티 함수나 정보를 적극 활용하고 있다. 이 함수들이 하는 주요한 일은 arMark 배열을 관리하는 것이다. 다음 다섯 개의 함수를 추가하도록 하자.

 

void ClearBookmark()

{

     int i;

     for (i=0;i<MAXBOOKMARK;i++) {

          arMark[i]=-1;

     }

     Invalidate(-1);

}

 

void ToggleBookmark(int Para, int Mark)

{

     int r,c;

     int i;

     int state;

 

     if (Para == -1) {

          GetRCFromOff(off,r,c);

          Para=pLine[r].nPara;

     }

    

     if (Mark <= 9) {

          if (arMark[Mark] == -1) {

              arMark[Mark]=Para;

          } else {

              if (arMark[Mark] == Para) {

                   arMark[Mark]=-1;

              } else {

                   arMark[Mark]=Para;

              }

          }

     } else {

          state=FindBookmark(Para);

          if (state == -1) {

              for (i=10;i<MAXBOOKMARK && arMark[i]!=-1;i++);

              if (i==MAXBOOKMARK) {

                   return;

              }

              arMark[i]=Para;

          } else {

              for (i=10;i<MAXBOOKMARK && arMark[i]!=Para;i++);

              arMark[i]=-1;

          }

     }

 

     Invalidate(-1);

}

 

void GotoBookmark(int Mark)

{

     int Para;

     int l,i;

     int r,c;

     int nowPara;

     int Min=1000000,Max=-1,Next=1000000,Prev=-1;

 

     if (Mark<=9) {

          Para=arMark[Mark];

          if (Para == -1) {

              return;

          }

     } else {

          GetRCFromOff(off,r,c);

          nowPara=pLine[r].nPara;

          for (i=10;i<MAXBOOKMARK;i++) {

              if (arMark[i] == -1)

                   continue;

              Min=min(arMark[i],Min);

              Max=max(arMark[i],Max);

              if (arMark[i] > nowPara) {

                   Next=min(arMark[i],Next);

              }

              if (arMark[i] < nowPara) {

                   Prev=max(arMark[i],Prev);

              }

          }

 

          if (Mark == 10) {

              if (Next == 1000000) {

                   Para=Min;

                   if (Para == 1000000) {

                        return;

                   }

              } else {

                   Para=Next;

              }

          } else {

              if (Prev == -1) {

                   Para=Max;

                   if (Para == -1) {

                        return;

                   }

              } else {

                   Para=Prev;

              }

          }

     }

 

     for (l=0;l<TotalLine;l++) {

          if (pLine[l].nPara == Para)

              break;

     }

 

     GotoLine(l);

}

 

void GotoLine(int Line)

{

     ClearSelection();

     off=GetOffFromRC(Line,0);

     SetCaret();

}

 

int FindBookmark(int Para)

{

     int i;

 

     for (i=0;i<MAXBOOKMARK;i++) {

          if (arMark[i] == Para) {

              return (i <= 9) ? i:100;

          }

     }

     return -1;

}

 

좀 양이 많은데 직접 입력하는 것보다는 CD-ROM에 있는 예제를 읽어와 복사해 넣도록 하자. 함수의 원형도 물론 추가해야 한다. 이 함수들이 어떻게 arMark 배열을 관리하고 arMark  배열로부터 이동할 문단번호를 구해 내는지 개별적으로 분석해보자.

ClearBookmark

모든 북마크를 리셋하여 초기화한다. , 모든 북마크를 다 지운다. arMark 배열요소는 북마크 지정이 없을 때 -1의 값을 가지도록 되어 있으므로 MAXBOOKMARK까지 arMark 배열을 모두 -1로 만들기만 하면 된다. 화면에 이미 출력되어 있는 북마크 표식을 지워야 하므로 Invalidate 함수를 호출해야 한다.

FindBookmark

인수로 전달된 Para 문단의 북마크 설정상태를 조사한다. 이름있는 북마크가 있다면 그 번호를 리턴하고 이름없는 북마크가 설정되어 있으면 100을 리턴한다. 아무 설정도 없으면 -1을 리턴한다. arMark 배열 전체를 뒤져 Para 문단이 있는지 조사해보고 있다면, 9 이하인지 아닌지 조사했다.

ToggleBookmark

arMark 배열을 관리하는 핵심 함수이며 두 개의 인수를 전달받는다. 첫 번째 인수 Para는 북마크를 토글할 문단번호인데 -1이면 현재 캐럿이 있는 위치의 문단이 대상이다. 두 번째 인수 Mark는 설정할 북마크의 이름이되 100이면 이름없는 북마크라는 뜻이다. 이 함수의 호출 예를 들어 보자.

 

ToggleBookmark(10,3);               // 10번 문단에 3번 북마크 토글

ToggleBookmark(12,100);       // 12번 문단에 이름없는 북마크 토글

ToggleBookmark(-1,8);               // 현재 문단에 8번 북마크 토글

 

이름에서도 알 수 있듯이 이 함수는 북마크를 설정하는 함수도 아니고 해제하는 함수도 아니며 토글하는 함수이다. 설정되어 있으면 해제하고 해제되어 있으면 설정하는 동작을 한 함수가 담당하는데 사용자들이 북마크의 설정과 해제 기능을 따로 두는 것보다는 토글방식을 선호하기 때문이다.

먼저 이름있는 북마크의 경우를 보자. arMark[Mark] -1이면, 즉 이 번호의 북마크가 설정되어 있지 않으면 여기다 Para 문단번호를 써 넣기만 하면 된다. 아주 간단하다. 이 번호의 북마크가 설정되어 있는 경우의 처리는 두 경우가 있는데 Para 문단이면 해제하고 다른 문단이라면 북마크를 이동해 와야 한다. 이름있는 북마크는 문서 전체를 통틀어 하나만 존재하기 때문에 두 문단이 동시에 같은 번호의 북마크를 가질 수 없다. 반대로 한 문단이 여러 개의 북마크를 가지는 것은 굳이 막을 필요가 없다.

이름없는 북마크는 단순히 토글만 시키면 된다. 이 문단이 이미 북마크를 가지고 있으면 해제하고 북마크가 없으면 설정한다. 이름없는 북마크는 arMark[10] ~ arMark[99] 사이에 기록되는데 가장 먼저 발견되는 빈 자리에 이 문단번호를 써 넣기만 하면 된다. 만약 arMark[99]까지 빈 자리를 찾았는데 발견되지 않는다면 90개까지 모든 북마크가 설정된 것이므로 더 이상 북마크를 지정할 수 없다.

북마크를 해제할 때는 arMark[10]~arMark[99]까지 순회하면서 Para 문단번호를 가진 배열요소를 찾아 -1로 바꾸면 된다. 북마크 설정이 바뀐 후에는 화면에 북마크 표시가 삭제되어야 하므로 작업영역을 다시 그리도록 하였다.

GotoLine

인수로 전달된 Line 행으로 이동하는 도우미 함수이다. GetOffFromRC로 줄의 오프셋을 찾고 SetCaret으로 캐럿을 옮겨 주었다. 그 전에 선택영역이 있다면 이동하기 전에 해제해야 한다. 설사 이동할 위치가 화면 밖에 안 보이는 곳에 있더라도 SetCaret이 알아서 스크롤하도록 되어 있어 이 함수가 특별히 할 일은 없다.

북마크는 문단번호에 대해 매겨지는데 비해 실제 이동은 줄단위로 해야 하기 때문에 이 함수가 필요하다. 만약 GetOffFromPara 함수를 미리 만들어 두었다면 굳이 이 함수가 필요치 않겠지만 문단 관련 함수는 한참 후에나 만들어진다.

GotoBookmark

북마크가 설정된 곳으로 이동한다. Mark 인수가 0~9인 경우는 이름있는 북마크로 이동하라는 뜻이고 10이면 다음 북마크로, 11은 이전 북마크로 이동하라는 뜻이다. 이름없는 북마크는 순서가 있기 때문에 현재 위치를 기준으로 이전, 이후 북마크를 찾을 수 있다.

이름있는 북마크를 찾는 것은 아주 쉽다. arMark[Mark]를 읽으면 문단번호를 바로 구할 수 있고 이 문단이 속해 있는 줄을 찾을 수 있다. 이 줄을 GotoLine으로 전달하기만 하면 된다. 물론 arMark[Mark] -1이면, 즉 이 번호에 북마크가 설정되어 있지 않으면 아무것도 할 필요가 없다.

이름없는 북마크는 현재 위치를 기준으로 이전 또는 이후 북마크를 찾아 이동하는데 직선 이동이 아니라 순회를 해야 하기 때문에 4가지 위치를 조사해두어야 한다. 마지막 북마크 위치에서 다음 북마크로 이동하면 첫 번째 북마크로 가야 하고 첫 번째 북마크에서 이전 북마크로 이동하면 마지막 위치로 가야 한다. 그래서 이전, 이후 북마크 위치는 물론이고 처음, 마지막 북마크 위치도 같이 조사할 필요가 있다.

10번부터 99번까지 루프를 돌며 arMark 배열 전체를 조사하여 처음(Min), 마지막(Max), 이전(Prev), 다음(Next) 위치를 조사하였다. 상황에 따라 이 값들 중 일부는 조사되지 않을 수도 있는데 예를 들어 현재 문단이 마지막 문단이라면 다음 북마크는 없다. 이렇게 조사된 값을 참조하여 이전, 이후 북마크 문단번호를 찾았다. 대상 문단번호를 찾았으면 줄번호를 찾고 GotoLine 함수로 이동하면 된다.