. 설정값 조사 및 변경

설정값을 저장하는 모든 멤버변수들도 private 액세스 속성을 가지기 때문에 호스트가 직접 액세스할 수 없다. 그래서 ApiEdit는 이 값을 조사 및 변경할 수 있는 public 멤버함수를 제공해야 한다. 설정값이 많으므로 함수들도 좀 많으며 변수 하나당 Get, Set 함수가 각각 필요하다. 다음 함수들을 CApiEdit 클래스에 추가한다.

 

class CApiEdit

{

     ....

     BOOL GetShowState(int nIndex);

     void SetShowState(int nIndex, BOOL aValue);

     int GetLineRatio() { return LineRatio; }

     void SetLineRatio(int aLineHeight);

     int GetWrap() { return nWrap; }

     int GetShowCurLine() { return nShowCurLine; }

     void SetShowCurLine(int aShowCurLine);

     int GetTabWidth() { return TabWidth; }

     void SetTabWidth(int aTabWidth);

     int GetHideSelType() { return HideSelType; }

     void SetHideSelType(int aHideSelType);

 

대부분 Get/Set 쌍으로 작성되어 있는데 GetWrap 함수와 대응되는SetWrap 함수가 없다. 왜냐하면 SetWrap 함수는 전반적으로 중요한 변화를 많이 유발하므로 이미 한참 전에 미리 만들어 놓았기 때문이다. Get(Set)ShowState 함수는 bShowLineNum, bShowEnter, bShowTab, bShowSpace, MarginWidth 등 여러 멤버를 변경하고 설정한다. 처리가 모두 비슷하기 때문에 같은 함수에서 조사 및 변경하되 인덱스값으로 대상 멤버를 지정하도록 하였다. 인덱스는 다음과 같이 매크로로 정의하였다. 컨트롤 외부에서 이 상수를 사용해야 하므로 ApiEdit.h 헤더 파일에 매크로를 정의해야 한다.

 

#define SHOWMARGIN 0

#define SHOWENTER 1

#define SHOWTAB 2

#define SHOWSPACE 3

#define SHOWLINENUM 4

 

이 변수들은 모두 BOOL형이기 때문에 같은 함수에서 비슷한 방법으로 다룰 수 있다. 그런데 MarginWidth수형임에도 불구하고 이 함수에서 같이 취급하도록 되어 있는데 마진폭 자체는 ApiEdit가 문서 길이에 따라 계산하도록 되어 있지만 호스트 입장에서는 마진을 보일 것인가 숨길 것인가만 문제가 되므로 외부 인터페이스는 역시 BOOL형이다. 그래서 같은 함수에서 MarginWidth를 다루도록 하였다.

GetShowState 함수 외에 나머지 Get 함수들은 단순히 멤버값을 읽어주기만 하면 되므로 모두 인라인으로 작성되어 있다. ApiEdit.cpp에 설정 조사 및 변경 함수를 작성한다.

 

BOOL CApiEdit::GetShowState(int nIndex)

{

     switch (nIndex) {

     case SHOWMARGIN:

          return (MarginWidth==0 ? FALSE:TRUE);

     case SHOWENTER:

          return bShowEnter;

     case SHOWTAB:

          return bShowTab;

     case SHOWSPACE:

          return bShowSpace;

     case SHOWLINENUM:

          return bShowLineNum;

     }

     return TRUE;

}

 

void CApiEdit::SetShowState(int nIndex, BOOL aValue)

{

     switch (nIndex) {

     case SHOWMARGIN:

          if (aValue==TRUE) {

              MarginWidth=25;

          } else {

              MarginWidth=0;

          }

          SendMessage(hWnd,WM_SIZE,SIZE_RESTORED,(LPARAM)0);

          Invalidate(-1);

          break;

     case SHOWENTER:

          bShowEnter=aValue;

          Invalidate(-1);

          break;

     case SHOWTAB:

          bShowTab=aValue;

          Invalidate(-1);

          break;

     case SHOWSPACE:

          bShowSpace=aValue;

          Invalidate(-1);

          break;

     case SHOWLINENUM:

          bShowLineNum=aValue;

          Invalidate(-1);

          break;

     }

}

 

이 두 함수는 bShow* 변수와 MarginWidth 변수를 취급하는데 인수로 전달된 nIndex 값에 따라 대상 변수를 찾아 조사 및 변경한다. MarginWidth 0이 아니면 마진이 있는 것으로 조사되며 마진폭을 변경하면 정렬 및 비트맵 재생성, 스크롤 정보 갱신 등을 해야 하므로 WM_SIZE 메시지를 보내 마진폭 변경에 따른 처리를 하도록 했다. bShow* 변수는 단순히 멤버변수값을 리턴하여 조사할 수 있으며 변경시 인수로 전달된 값으로 멤버변수를 변경하고 화면을 다시 그리기만 하면 된다.

설정 변경 함수를 만들어 두었으므로 이제 호스트에서 SetShowState 함수만 호출하면 여러 가지 보기 상태를 실행중에 곧바로 변경할 수 있다. 없던 마진이 나타나기도 하고 안 보이던 제어코드를 한꺼번에 보이도록 할 수도 있다. 나머지 설정 변경 함수도 직관적으로 이해할 수 있을 만큼 쉽게 작성되어 있다.

 

void CApiEdit::SetLineRatio(int aLineHeight)

{

     int FirstRow;

 

     FirstRow=yPos/LineHeight;

     LineRatio=aLineHeight;

     LineHeight=int(FontHeight*LineRatio/100);

     yPos=FirstRow*LineHeight;

     if (hBit) {

          DeleteObject(hBit);

          hBit=NULL;

     }

     UpdateScrollInfo();

     Invalidate(-1);

     SetCaret();

}

 

void CApiEdit::SetShowCurLine(int aShowCurLine)

{

     nShowCurLine=aShowCurLine;

     Invalidate(-1);

}

 

void CApiEdit::SetTabWidth(int aTabWidth)

{

     TabWidth=aTabWidth;

     TabSize=FontWidth*TabWidth;

     UpdateLineInfo();

     UpdateScrollInfo();

     Invalidate(-1);

     SetCaret();

}

 

void CApiEdit::SetHideSelType(int aHideSelType)

{

     HideSelType=aHideSelType;

     if (HideSelType == 1) {

          SelFore=RGB(0,0,0);

          SelBack=RGB(192,192,192);

     } else {

          SelFore=GetSysColor(COLOR_HIGHLIGHTTEXT);

          SelBack=GetSysColor(COLOR_HIGHLIGHT);

     }

     if (SelStart != SelEnd) {

          Invalidate(-1);

     }

}

 

줄간을 변경하는 SetLineRatio 함수는 나름대로 복잡하다. 줄의 높이가 바뀌면 더블 버퍼링 비트맵을 다시 생성해야 하고 스크롤 정보를 갱신해야 하며 화면을 다시 그려야 한다. 줄간을 바꾼다고 해서 정렬 상태가 바뀌지는 않기 때문에 그다지 많은 처리가 필요치는 않지만 줄의 높이가 바뀜으로 해서 몇 가지 불일치가 발생할 수 있다. 그래서 SetLineRatio 함수는 다음 세 원칙을 철저하게 준수해야 할 의무가 있다.

 

오프셋은 변경되지 말아야 한다. 즉 캐럿은 줄간을 바꾸기 전의 위치에 그대로 있어야 하며 또 줄간을 바꾼 후에도 캐럿이 화면에 보여야 한다. SetLineRatio 함수는 off 변수를 절대로 건드리지 않으며 SetCaret 함수에 의해 캐럿 위치는 줄간을 바꾼 후에도 화면에 보인다. 만약 캐럿이 화면에 보이지 않는 상태에서 줄간을 변경하면 캐럿이 있는 곳으로 스크롤된다.

화면상의 첫 줄은 가급적이면 유지해야 한다. 예를 들어 첫 줄에 안녕하세요 문장이 보이는 상태였다면 줄간이 변경된 후에도 이 줄이 여전히 첫 줄이어야 한다. 하지만 첫 번째 원칙이 더 우선이므로 줄간이 늘어나 캐럿이 화면 아래로 숨으면 어쩔 수 없이 첫 줄이 바뀔 수도 있다.

yPos는 반드시 줄간의 배수여야 한다. 줄간이 20에서 30으로 바뀌었으면 yPos도 줄간에 따라 적절히 조정되어야 한다. 예를 들어 화면상의 첫 줄이 11번째 줄일 때 줄간 20이라면 yPos 220일 것이다. 이 상태에서 줄간이 30으로 변경되면 yPos 210이 되거나 240이 되어야 한다. 이 원칙을 어기면 캐럿이 글자 위치에 제대로 나타나지 않는다.

 

SetLineRatio는 첫 줄 유지를 위해 줄간을 변경하기 전의 줄번호를 FirstRow 변수에 조사해놓았다. 그리고 줄간 변경 후 yPos를 다시 첫 줄에 맞춤으로써 2번 원칙과 3번 원칙을 동시에 만족시켰다. 1번 원칙은 off 변수만 건드리지 않으면 특별히 신경쓰지 않아도 된다. 줄간이 늘어나 캐럿이 화면 아래쪽으로 숨었을 때 다시 보이도록 하는 작업은 SetCaret이 담당하도록 되어 있다. 몇 줄 안되는 짧은 코드지만 하는 일이 많다. 이처럼 설정값이 바뀌면 ApiEdit가 추가적인 처리를 할 필요가 있기 때문에 호스트에서 직접 멤버변수를 다루지 못하도록 금지하는 것이다. 호스트는 오로지 어떤 설정을 어떻게 바꾼다는 의사 표시만 하면 된다.

탭의 폭이 변경되면 정렬 조건이 바뀌므로 재정렬하도록 했으며 캐럿 위치가 바뀔 수 있으므로 SetCaret을 호출하였다. 선택영역 숨기기 옵션이 변경되면 SelFore, SelBack 변수값을 변경하여 선택영역의 색상을 변경하고 화면을 다시 그린다. , 선택영역이 없으면 다시 그릴 필요가 없다. HideSelType 변수만 바꿔 놓으면 다음 번 OnSetFocus에서 SelFore, SelBack을 재설정하도록 되어 있지만 포커스를 받기 전에 변경상태를 적용해야 할 경우도 있기 때문에 이 함수에서 먼저 SelFore, SelBack을 바꿔 놓고 다시 그리기를 해야 한다.

줄간과 탭폭이 변경되면 SetCaret이 호출되는데 포커스가 없는 상태에서도 캐럿이 나타나게 되는 문제가 있다. 큰 문제는 아니지만 일단 보기에 좋지 않으므로 SetCaret 함수를 약간 수정하여 포커스가 있을 때만 캐럿을 보이도록 한다.

 

void CApiEdit::SetCaret(BOOL bUpdatePrevX/*=TRUE*/, BOOL bScrollToCaret/*=TRUE*/)

{

     ....

     CreateCaret(hWnd,NULL,caretwidth,FontHeight);

    if (GetFocus()==hWnd) {

        ShowCaret(hWnd);

    }

     ....

 

포커스가 없는 상태, 예를 들어 설정 대화상자가 떠 있는 상태에서 설정을 변경할 경우는 캐럿의 위치를 옮기더라도 당장 화면에 표시할 필요는 없다. 대화상자가 닫히고 ApiEdit가 포커스를 받게 되면 OnSetFocus에서 SetCaret을 다시 호출하므로 그때 캐럿을 표시하면 된다.