. 선택시 무효영역 관리

문서 편집시에는 글자의 삽입, 삭제에 따라 뒷부분이 밀려 나거나 당겨지기 때문에 화면 끝까지 무효화를 해야 했다. 하지만 선택은 문서 내용을 바꾸는 것이 아니기 때문에 좀 더 정확하게 무효영역을 설정할 수 있으며 확장된 선택영역만 다시 그리는 것이 가능하다. 먼저 마우스로 선택할 때의 처리 루틴인 OnMouseMove를 수정해보자.

 

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

{

     BOOL bInstTimer;

     RECT crt;

     int r,c;

    int OldOff;

 

     if (bCapture == FALSE) {

          return;

     }

 

    OldOff=off;

     off=SelEnd=GetOffFromXY(x+xPos,y+yPos);

     SetCaret();

    Invalidate(min(OldOff,off),max(OldOff,off));

     ....

 

선택이 확장되기 전의 오프셋을 OldOff에 저장해두고 확장된 선택영역 off까지를 무효화하였다. 이때 Invalidate 함수는 첫 번째 인수가 두 번째 인수보다 반드시 작을 것을 요구하므로 정규화를 해야 한다. 이 처리에 의해 선택영역이 확장될 때는 정확하게 확장된 부분만 다시 그려지며 나머지 부분은 손도 대지 않는다.

ApiEdit가 채택한 다시 그리기 선택법은 반전 선택법에 비해 화면 처리가 느린 단점이 있는데 이렇게 무효영역을 잘 관리하면 속도상의 불이익을 상당 수준 극복할 수 있다. 다음은 키보드 선택 루틴에서 사용하는 함수들을 수정해보자.

 

void ClearSelection()

{

     if (SelStart != SelEnd) {

        Invalidate(min(SelStart,SelEnd),max(SelStart,SelEnd));

          SelStart=SelEnd=0;

     }

}

 

선택영역을 제거하는 ClearSelection 함수도 아주 쉽다. 선택영역이 속한 부분만 무효화하면 된다. 이때도 물론 정규화가 필요하다. 선택영역을 확장하는 ExpandSelection 함수는 조금 복잡하다.

 

void ExpandSelection(int Start, int End)

{

    int OldSelEnd;

 

     if (SelStart==SelEnd) {

          SelStart=Start;

          SelEnd=End;

        Invalidate(min(SelStart,SelEnd),max(SelStart,SelEnd));

     } else {

        OldSelEnd=SelEnd;

          SelEnd=End;

        Invalidate(min(OldSelEnd,SelEnd),max(OldSelEnd,SelEnd));

     }

}

 

새로 선택영역이 만들어질 때는 선택영역이 속한 부분 전체를 무효화한다. 선택영역이 확장될 때는 확장된 부분만 무효화하는데 이를 위해 확장전의 오프셋은 OldSelEnd에 저장해두었다. 두 코드 모두 정규화는 필요하다.

무효영역 최소화를 위해 소스 전체에서 InvalidateRect(hWnd,NULL,TRUE)문장을 많이 제거하였다. 하지만 무효영역을 항상 화면 전체로 해야 하는 경우도 여전히 있다. 포커스가 이동될 때나 전체 선택, SetWrap, <PgUp>, <PgDn> 등을 처리할 때는 화면 전체를 무효화해야 한다. 물론 이 경우도 좀 더 정밀하게 계산한다면 어느 정도 무효영역을 줄일 수는 있겠지만 약간의 효율 향상을 위해 코드가 훨씬 더 복잡해져야 하므로 이 이상 구두쇠 짓을 할 필요는 없다.

InvalidateRect(hWnd,NULL,TRUE)를 그대로 두는 것이 좀 마음에 안 들면 Invalidate(-1)로 수정해도 되나 두 문장은 완전한 등가의 명령이므로 굳이 수정할 필요는 없다. 하지만 일관성을 위해 수정하기로 하자. 단순한 치환이므로 찾기/바꾸기로 바꿔버리면 된다. 치환 후 InvalidateRect 함수 호출문이 Invalidate에만 있으면 된다.

여기까지 무효영역 최소화를 위해 많은 코드를 수정했는데 그 효과는 당장 체감하기 어렵다. 왜냐하면 컴퓨터가 워낙 빠르기 때문에 통째로 다 그리는 시간과 일부만 그리는 시간의 차이를 느끼기가 어렵기 때문이다. 만약 무효영역 최소화의 효과를 눈으로 확인하고 싶다면 OnPaint에 다음 임시 코드를 삽입해보아라.

 

void OnPaint(HWND hWnd)

{

     ....

     e=min(e,TotalLine-1);

 

    TCHAR Mes[256];

    wsprintf(Mes,"s=%d, e=%d, top=%d, bottom=%d",s,e,ps.rcPaint.top,ps.rcPaint.bottom);

    SetWindowText(hWnd,Mes);

 

무효영역과 출력 시작줄, 끝 줄을 타이틀바에 찍어 확인해보도록 하였다. 입력할 때나 삭제할 때, 선택을 바꿀 때 다시 그려지는 줄을 확인할 수 있다. 또한 다른 프로그램에 의해 가려질 때 어떤 부분이 다시 그려지는지도 볼 수 있을 것이다. 확실히 다시 그리는 영역은 좁아졌고 그만큼 더 빨라진다.