. 줄 정보 사용

이제 pLine 배열에서 줄의 시작과 끝 오프셋을 구할 수 있으므로 소스의 각 부분에 있는 GetLine 호출문은 필요가 없다. 이곳 저곳 고쳐야 할 곳이 많은데 대표적으로 GetRCFromOff를 같이 수정해보고 어떤 방식으로 줄 정보를 참조하는지 보도록 하자. 다음은 지금까지 작성한 GetRCFromOff 함수이다.

 

void GetRCFromOff(int nPos, int &r, int &c)

{

     int s,e;

     TCHAR *p=buf;

     r=0;

 

     if (nWrap==0) {

          while (p-buf != nPos) {

               if (*p==‘\r’)

                   r++;

               p++;

          }

          GetLine(r,s,e);

     } else {

          for (;;r++) {

               GetLine(r,s,e);

               if (nPos >= s && nPos < e)

                   break;

 

               if (nPos == e) {

                   if (buf[e] == 0 || buf[e] == ‘\r’ || bLineEnd == TRUE) {

                        break;

                   }

               }

          }

     }

 

     c=nPos-s;

}

 

GetLine 함수를 반복적으로 호출하며 nPos가 어느 줄에 있는지 알아 내는데 그럴 필요없이 pLine에서 원하는 줄의 정보를 바로 구할 수 있다. GetLine 호출문이 필요없으므로 제일 처음에 있는 int s,e 변수도 필요가 없다. 또한 줄 정보를 구하는 방법이 훨씬 더 효율적으로 수정되었으므로 자동개행 상태인 경우와 그렇지 않은 경우를 굳이 구분할 필요도 없어졌다. 수정한 결과는 다음과 같다.

 

void GetRCFromOff(int nPos, int &r, int &c)

{

     for (r=0;;r++) {

          if (nPos >= pLine[r].Start && nPos < pLine[r].End)

               break;

 

          if (nPos == pLine[r].End) {

               if (buf[pLine[r].End]==0 || buf[pLine[r].End]==‘\r’ || bLineEnd==TRUE) {

                   break;

               }

          }

     }

 

     c=nPos-pLine[r].Start;

}

 

자동개행 상태를 구분하지 않으므로 소스도 짧아졌고 GetLine 호출문도 사라졌다. 이런 식으로 GetLine 호출문을 먼저 제거하고 s,e 변수 선언문을 제거한다. 그리고 s pLine Start멤버로 바꾸고 e pLine End로 바꾸면 된다. 다음은 반대 함수인 GetOffFromRC를 수정해보자.

 

int GetOffFromRC(int r, int c)

{

     c=min(c,pLine[r].End-pLine[r].Start);

     return pLine[r].Start+c;

}

 

GetLine 함수 호출문이 사라졌으므로 단 두 줄로 짧아졌는데 마음만 먹으면 한 줄로 줄일 수도 있다. 정보를 구하는 방법만 달라졌을 뿐 근본적으로 논리가 바뀐 것은 아니다. 계속해서 GetXYFromOff, GetOffFromXY, GetXPosOnLine도 수정하자. int s,e선언문과 GetLine 호출문을 삭제하고 다음 바뀐 부분만 변경하면 된다. 모두 s e지역변수를 pLine Start End로 바꾼 것이다.

 

void GetXYFromOff(int nPos, int &x, int &y)

{

     int r,c;

     TCHAR *p;

 

     GetRCFromOff(nPos, r, c);

     y=r*LineHeight;

 

     x=0;

    for (p=buf+pLine[r].Start;p!=buf+nPos;) {

          if (*p == ‘\t’) {

               x = (x/TabSize+1)*TabSize;

               p++;

          } else {

               if (IsDBCS(p-buf)) {

                   x += GetCharWidth(p,2);

                   p+=2;

               } else {

                   x += GetCharWidth(p,1);

                   p++;

               }

          }

     }

}

 

int GetXPosOnLine(int r,int DestX)

{

     TCHAR *p;

     int len, acwidth;

 

     if (DestX == 0) {

        return pLine[r].Start;

     }

    for (p=buf+pLine[r].Start, acwidth=0;;) {

        if (p-buf == pLine[r].End)

               break;

 

          if (*p == ‘\t’) {

               len=1;

               acwidth =(acwidth/TabSize+1)*TabSize;

          } else {

               if (IsDBCS(p-buf)) {

                   len=2;

               } else {

                   len=1;

               }

               acwidth+=GetCharWidth(p,len);;

          }

 

          p+=len;

         

          if (acwidth >= DestX) {

               break;

          }

     }

 

    if (p-buf==pLine[r].End && buf[p-buf]!=‘\r’ && buf[off]!=0) {

          bLineEnd=TRUE;

     } else {

          bLineEnd=FALSE;

     }

     return p-buf;

}

 

int GetOffFromXY(int x, int y)

{

     int r,len;

     TCHAR *p;

     int chWidth;

     int acwidth;

 

     x=max(x,0);

     y=max(y,0);

 

     r=y/LineHeight;

     r=min(r,GetRowCount()-1);

 

    for (p=buf+pLine[r].Start,acwidth=0;;) {

        if (p-buf == pLine[r].End) {

               break;

          }

 

          if (*p == ‘\t’) {

               len=1;

               chWidth=(acwidth/TabSize+1)*TabSize-acwidth;

          } else {

               if (IsDBCS(p-buf)) {

                   len=2;

               } else {

                   len=1;

               }

               chWidth=GetCharWidth(p,len);

          }

 

          acwidth += chWidth;

          if (acwidth-chWidth/2 >= x) {

               break;

          }

 

          p+=len;

     }

 

    if (p-buf==pLine[r].End && buf[p-buf]!=‘\r’ && buf[off]!=0) {

          bLineEnd=TRUE;

     } else {

          bLineEnd=FALSE;

     }

     return p-buf;

}

 

GetRowCount 함수는 이제 함수라고 할 수도 없을 정도로 간단해진다. 없애버려도 될 것 같은데 그러면 이 함수를 호출하는 곳도 수정해야 하므로 그냥 두기로 했다.

 

int GetRowCount()

{

     return TotalLine;

}

 

UpdateLineInfo 함수가 줄 정보를 구하면서 이미 총 줄수를 구해 전역변수 TotalLine에 대입해놓았으므로 이 변수값을 리턴하기만 하면 된다. UpdateScrollInfo 함수에서도 수평스크롤 범위를 결정하기 위해 GetLine을 호출하는데 이 호출도 없애버리자.

 

void UpdateScrollInfo()

{

     ....

     if (nWrap == 0) {

          for (i=0,MaxLength=0;;i++) {

            if (pLine[i].Start==-1) {

                   break;

               }

            MaxLength=max(MaxLength,(pLine[i].End-pLine[i].Start));

          }

     ....

}

 

DrawLine도 출력할 줄의 범위를 GetLine 함수로 구했다. 역시 제거한다.

 

int DrawLine(HDC hdc, int Line)

{

     ....

    if (pLine[Line].Start == -1)

          return 0;

     ....

    nowoff=pLine[Line].Start;

     ....

 

            if (nowoff+len == pLine[Line].End) {

     ....

        DrawSegment(hdc,x,Line*LineHeight-yPos,nowoff,len,

            (nowoff+len==pLine[Line].End),fore,back);

 

          nowoff+=len;

        if (nowoff == pLine[Line].End)

               return 1;

     }

}

 

캐럿 이동 루틴인 OnKey VK_LEFT, VK_RIGHT GetLine을 제거한다. OnKey 함수의 지역변수인 s, e도 이제 불필요해졌으므로 삭제하도록 하자.

 

     case VK_LEFT:

     ....

               if (off==pLine[r].Start) {

     ....

     case VK_RIGHT:

     ....

               if (off==pLine[r].End) {

                   if (buf[pLine[r].End]==‘\r’) {

                             off=GetNextOff(off);

                        }

                        bLineEnd=FALSE;

                   } else {

                        off=GetNextOff(off);

                   if (off==pLine[r].End && buf[off]!=‘\r’) {

                             bLineEnd=TRUE;

                        } else {

                             bLineEnd=FALSE;

                        }

                   }   ....

 

마지막으로 선택중에 수평 자동 스크롤을 처리하는 OnMouseMove의 코드를 다음과 같이 수정한다.

 

void OnMouseMove(HWND hWnd, int x, int y, UINT keyFlags)

{

     ....

     if (nWrap == 0) {

          GetRCFromOff(SelEnd,r,c);

        if (x>crt.right && SelEnd != pLine[r].End) {

               SendMessage(hWnd, WM_HSCROLL, SB_LINERIGHT, 0L);

               bInstTimer=TRUE;

          }

        if (x<0 && SelEnd != pLine[r].Start) {

               SendMessage(hWnd, WM_HSCROLL, SB_LINELEFT, 0L);

               bInstTimer=TRUE;

          }

     }

     ....

 

GetLine 호출을 제거하기 위해 여기 저기 소스를 많이 수정하는 좀 귀찮은 작업을 했다. 이제 GetLine을 호출하는 유일한 함수는 UpdateLineInfo 함수 뿐이며 이 함수가 조사해놓은 줄 정보를 모든 함수가 사용한다. 여기까지 작업한 후 실행 시간을 다시 측정해보자.

 

테스트

1

2

3

4

5

6

최초

14.29

15.4

28.89

37.29

46.52

78.03

문자폭 계산

2.92

2.87

5.76

7.10

8.72

15.14

정보 작성

0.05

0.0008

0.0735

3.68

0.0037

7.63

 

대부분의 작업들이 소수점 이하의 시간 내로 처리될 정도로 속도가 개선되었다. 하지만 아직 4번과 6번은 굉장히 오랜 세월을 요구하는데 이 두 테스트는 문서를 전부 재정렬하기 때문이다. 문서 전체 정렬 알고리즘이 아직도 많이 개선되어야 할 것 같다.