. 행렬 계산

캐럿이 개행된 줄로도 이동하려면 현재 위치 off가 화면상의 어디쯤에 해당하는지 좌표를 찾을 수 있어야 한다. 또한 커서이동키로 캐럿을 이동하려면 오프셋으로부터 줄과 칸 번호를 찾을 수도 있어야 한다. 문제는 메모리상에서는 칸과 줄(또는 행과 열)의 개념이 없다는 것이다. buf는 오프셋만으로 위치를 기억하는 일차원적인 구조인데 비해 화면은 메모리와는 달리 줄과 칸의 개념이 있는 이차원적인 행렬 구조로 되어 있다. 그래서 오프셋으로부터 행렬값을 구할 수 있어야 하고 반대로 행렬값으로부터 대응되는 오프셋을 구할 수도 있어야 하는데 다음 두 함수가 이 계산을 한다.

 

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

{

     int s,e;

     TCHAR *p=buf;

     r=0;

 

     while (p-buf != nPos) {

          if (*p==‘\r’)

               r++;

          p++;

     }

     GetLine(r,s,e);

     c=nPos-s;

}

 

int GetOffFromRC(int r, int c)

{

     int s,e;

 

     GetLine(r,s,e);

 

     c=min(c,e-s);

     return s+c;

}

 

GetRCFromOff 함수는 오프셋을 인수로 주면 이 위치가 몇 번째 줄(Row), 몇 번째 칸(Column)인지 조사한다. 버퍼의 처음부터 시작해서 nPos에 이를 때까지 개행문자의 개수를 세어 보면 이 개수가 바로 줄번호가 된다. 줄번호를 구했으면 GetLine 함수로 이 줄의 범위를 구할 수 있다. 열번호는 현재 위치에서 줄의 시작위치인 s를 빼면 간단하게 구해진다. 다음 문서에서 nPos 위치의 행렬값을 구하는 과정을 보자. 현재 위치가 마지막 줄 이웃들에게자라고 하자.

자에 이를 때까지 개행코드를 세어 보면 4개 발견된다. 따라서 이 위치는 4번째 줄임을 쉽게 알 수 있다. 4번째 줄의 시작위치는 GetLine 함수로 구할 수 있는데 nPos에서 시작위치 s를 빼면 몇 번째 칸인지도 조사된다. 조사결과 자는 4 6열에 위치한다는 것을 알 수 있으며 이 결과를 인수로 전달된 레퍼런스 변수 r, c에 리턴한다.

이 함수는 인수로 전달되는 nPos 오프셋이 항상 유효한 것으로 가정하고 있다. 만약 버퍼의 길이보다 더 긴 오프셋 값이 전달되면 이 함수의 동작은 예측할 수 없다. 즉 에러 처리를 하지 않는다. 그래서 이 함수를 호출하는 쪽에서 nPos가 반드시 유효하도록 보장해야 한다. 왜 에러 처리를 하지 않는가 하면 불필요한 계산을 하지 않음으로써 속도를 조금이라도 높이기 위해서이다.

GetOffFromRC는 반대로 r, c 값으로부터 대응되는 오프셋 위치를 구하는데 코드는 훨씬 더 간단하다. GetLine으로 r 줄의 범위를 조사하면 원하는 오프셋은 s~e 사이에 있다. 조사된 시작위치 s 값에서 c를 더하면 이 행렬 위치에 대응되는 오프셋이 구해진다. 단 구해진 오프셋이 반드시 s~e 사이에 있도록 조정해야 할 필요가 있다. c가 줄 범위 밖에 있더라도 강제로 줄 안쪽으로 넣어주어야 한다. 다음 그림을 보자.

GetOffFromRC(0,4)를 호출한 경우 시작위치 s 4를 더한 4가 결과 오프셋이 된다. 그러나 0 20열을 요청했다면 줄의 제일 끝인 17 오프셋 위치를 대신 리턴해야 한다. 왜냐하면 0 20열에 대응되는 오프셋은 없기 때문이다. 그래서 c s에 더하기 전에 줄 길이보다 더 길다면 줄 길이와 같도록 만드는 c=min(c,e-s) 코드가 필요하다.