. 그룹 정보의 사용

편집 레코드를 그룹으로 묶는 근본적인 이유는 취소/재실행시에 이 명령들을 한꺼번에 실행하기 위해서이다. 레코드의 그룹 표식은 결국 Undo, Redo 함수에서 사용되며 오로지 이 함수들만이 tick값을 참조한다. 먼저 그룹에 속한 레코드를 한꺼번에 취소하도록 Undo 함수를 다음과 같이 수정한다.

 

void CApiEdit::Undo()

{

     int from, to, len, dest;

    int tick;

 

     if (pUR[nowur].action == UR_NONE || pUR[nowur].status==UR_CANCELED) {

          nowur--;

     }

 

     SelStart=SelEnd=0;

    tick=pUR[nowur].tick;

 

    for (;;) {

          switch (pUR[nowur].action) {

              ....

          }

          pUR[nowur].status=UR_CANCELED;

        if (tick==0 || nowur==0)

           break;

        if (tick!=pUR[nowur-1].tick)

           break;

        nowur--;

    }

 

     ArrangeModified();

}

 

이 명령을 받았을 때 nowur 위치의 레코드 하나만 취소하는 것이 아니라 nowur 이후 같은 그룹에 속한 모든 레코드를 한꺼번에 취소해야 한다. 취소할 레코드의 틱값을 먼저 조사하고 루프를 돌며 이 레코드와 같은 틱값을 가지는 모든 레코드를 연속적으로 취소한다. 취소 루프가 종료될 조건은 다음 세 가지다.

첫 번째로 nowur 위치의 레코드가 그룹에 속하지 않는 단독 레코드일 때다. 레코드의 tick값이 0이면 단독 레코드임을 쉽게 알 수 있다. 이 경우도 최소한 한 번은 취소를 해야 하므로 일단 루프로 진입은 해야 하며 그래서 조건 점검문이 루프 끝에 있다. 이 조건에 의해 단독 레코드일 때는 그 레코드 하나만 취소된다.

두 번째는 연속적으로 취소를 하다가 첫 번째 레코드를 만난 경우이다. 첫 번째 레코드가 그룹이면 더 이전의 레코드가 없으므로 언더 플로우 방지를 위해 루프를 빠져 나가야 한다. 이때 이 레코드가 정말 그룹의 선두인지 아닌지는 정확히 알 수 없으며 알 필요도 없다. 상한선 제한에 의해 그룹의 허리가 잘려 나갔더라도 남아 있는 레코드까지만 취소하면 된다.

세 번째는 이전 레코드의 그룹 ID가 현재 그룹의 ID와 다를 때이다. 이때는 연속된 두 개의 그룹이 인접해 있거나 아니면 이전 레코드가 단독 레코드일 것이다. 어쨌든 이 조건이 만족되면 그룹내의 모든 레코드를 취소했으므로 무사히 임무를 완료한 것이다. nowur이 현재 작성중인 레코드 또는 방금 취소된 레코드를 가리키도록 정의되어 있으므로 nowur-- 보다 더 앞에서 이 조건 점검을 해야 한다. 앞에서도 한 번 강조를 했지만 nowur을 어떻게 정의하는가에 따라 이 두 코드의 순서가 결정되며 이 순서가 틀리면 곳곳에서 오류가 발생한다.

Redo의 코드도 거의 유사하다. 다음과 같이 수정하도록 하자.

 

void CApiEdit::Redo()

{

     int from, to, len;

    int tick;

 

     SelStart=SelEnd=0;

    tick=pUR[nowur].tick;

 

    for (;;) {

          switch (pUR[nowur].action) {

              ....

          }

          pUR[nowur].status=UR_MAKING;

          NextRecord();

        if (tick==0)

           break;

        if (tick!=pUR[nowur].tick)

           break;

     }

 

     ArrangeModified();

}

 

단독 레코드일 때는 한 번만 재실행하고 그룹일 때는 같은 그룹에 속한 모든 레코드를 다 재실행한다. Undo의 경우와는 달리 nowur의 위치를 정확하게 유지하기 위해 NextRecord 호출이 루프 종료 조건 점검보다 먼저 있어야 한다. 취소 레코드 배열의 제일 끝에는 항상 빈 레코드가 하나 있으며 이 레코드의 tick값은 0이기 때문에 오버 플로우 점검은 하지 않아도 된다.

 

이상으로 취소와 재실행에 관련된 모든 실습을 마친다. 예제를 실행해보면 삽입, 삭제, 이동동작을 <Ctrl+Z>로 취소할 수 있으며 선택영역을 대체했을 때도 한 번의 취소로 원래대로 돌릴 수 있을 것이다. 이 장의 코드는 조금 난이도가 있지만 나름대로 연구해 볼만한 가치가 있으며 관련 이론들에 대해 잘 정리해놓으면 언젠가는 실무에 크게 도움이 될 것이다.