. 옵션 초기화

SOption은 생성자, 파괴자를 비롯한 몇 개의 멤버함수들을 가지고 있는데 이 함수들은 Util.cpp에 작성하도록 하자. 다음 코드를 작성한다.

 

void SOption::Init()

{

     StartAction=2;

     bExplorerPopup=TRUE;

     bAllowMulti=FALSE;

     bMaxFirstChild=TRUE;

     MaxMru=1;

     lstrcpy(DefExt,"txt");

     Hangul=TRUE;

     bShowOutput=FALSE;

     OutputHeight=100;

     bInitFind=TRUE;

     bInitNextFind=FALSE;

     bShowToolBar=TRUE;

     bShowStatus=TRUE;

     bShowFileWnd=FALSE;

     FileWndWidth=200;

     bShowFileTab=FALSE;

     bShowHidden=FALSE;

     FilterIndex=0;

     bToolBarBig=FALSE;

     bToolBarText=FALSE;

     bBrowseMode=FALSE;

     bSoundFindFail=TRUE;

     bWatchChange=TRUE;

     bReloadNoAsk=FALSE;

     bReloadProject=TRUE;

 

     LineRatio=120;

     bWrap=TRUE;

     nWrap=2;

     RightWrap=0;

     ColMark=0;

     HideSelType=1;

     nShowCurLine=0;

     logfont.lfHeight=0;

     bShowMargin=TRUE;

     bShowLineNum=FALSE;

     bShowEnter=FALSE;

     bShowTab=FALSE;

     bShowSpace=FALSE;

     TabWidth=4;

     bNoFirstSpace=FALSE;

     MarColor1=0;

     MarColor2=0;

     NumColor=0;

     MarkColor=0;

     ShowTabType=1;

     ShowEnterType=1;

     ShowSpaceType=0;

     CodeColor=0;

     CurColor=0;

     cBack=0;

     cFore=0;

     cSelFore=0;

     cSelBack=0;

     bUseLineEnd=TRUE;

     bAllowDrag=TRUE;

     CaretWidth=2;

     bHideCurLine=TRUE;

     bCalcTabWithAvg=TRUE;

     UndoLimit=2;

     bSpaceForTab=FALSE;

     bAutoIndent=TRUE;

     bBlockIndentWithTab=TRUE;

     bHomeToFirstChar=TRUE;

     FindDlgPos=1;

}

 

int SOption::GetMaxMru()

{

     int arMaxMru[]={5,10,15,20,25,30};

     return arMaxMru[MaxMru];

}

 

int SOption::GetUndoLimit()

{

     int arUndoLimit[]={1024*10,1024*100,1048576,1048576*5,1048576*10};

     return arUndoLimit[UndoLimit];

}

 

void SOption::Load(TCHAR *Key)

{

}

 

void SOption::Save(TCHAR *Key)

{

}

 

Init 함수는 SOption 구조체의 멤버를 초기화하며 이 함수는 SOption의 생성자에서 호출된다. Init 함수에서 대입하는 디폴트값은 ApiEdit의 값들과 일단은 동일하다. 하지만 경우에 따라서는 다르게 초기화할 수도 있다. ApiEdit는 현재줄 표시를 보여주지 않는 상태가 디폴트이지만 호스트는 보여주는 것을 디폴트로 만들 수도 있다. 컨트롤의 디폴트와 호스트의 디폴트는 보통 다를 수밖에 없다. 예를 들어 에디트 컨트롤의 디폴트 스타일은 한 줄밖에 입력받지 못하는 것으로 되어 있지만 호스트는 ES_MULTILINE 스타일을 주어 여러 줄을 입력받을 수 있도록 에디트를 초기화할 수 있다. ApiEdit Dangeun의 디폴트 설정값이 일치하는 이유는 아마도 이 두 모듈을 만든 사람들이 친한 사이라서 그럴 것이다.

Init의 초기값은 Dangeun이 정의하는 디폴트 설정값이며 최초 프로그램이 실행되면 이 상태대로 실행된다. 대부분의 사용자들은 별도의 옵션 조정없이 주어진 디폴트를 그냥 받아 들이고 사용한다. 디폴트가 실질적으로 영원한 값이 되는 경우가 많으므로 이 값은 아주 중요하며 심사숙고 끝에 잘 결정해야 한다. bShowMargin=FALSE라고 초기화하면 절반 이상의 사용자는 마진의 존재 자체를 모르게 되고 애써 작성한 북마크, 줄번호 표기, 마진선택 같은 기능은 묻혀버리게 될 것이다.

디폴트 설정값을 어떻게 정하는가는 제품의 성공 여부와 직결되는 중요한 문제이며 그래서 상용 제품들은 자신이 가진 모든 재주를 한눈에 자랑할 수 있도록 디폴트를 설정한다. 글꼴도 가장 예쁜 상태로 선택해두고 배색도 신경써서 차별화된 모양으로 만들고 모든 화면 요소를 다 보이는 상태로 만드는데 성미급한 사용자들의 눈을 사로잡기 위해서는 어쩔 수 없는 일이다. 하지만 Dangeun은 교육용 예제이기 때문에 이런 것보다는 테스트의 편리를 최우선적으로 고려하여 디폴트를 설정했으며 폰트나 색상은 대부분 시스템 값으로 초기화되어 있다.

GetMaxMru, GetUndoLimit 함수는 옵션값 조사를 위한 일종의 보조 함수이다. MaxMru, UndoLimit 변수는 실제 옵션값을 저장하지 않으며 설정 대화상자의 콤보박스 인덱스를 저장하도록 되어 있으므로 이 인덱스로부터 실제값을 찾아야 한다. GetMaxMru, GetUndoLimit 함수는 콤보박스의 인덱스로부터 실제 적용할 옵션값을 찾아주는 역할을 하는 간단한 함수이다. Load, Save 함수는 설정상태를 레지스트리에 저장하고 읽어오는 함수인데 잠시 후에 직접 만들어 볼 것이다.

SOption 구조체 자체를 이해하는 것은 그리 어렵지 않은데 과연 이런 큰 구조체를 만들어야 할 필요가 있을까라는 의문이 드는 사람도 분명히 있을 것이다. ApiEdit의 옵션값들은 ApiEdit에 있고 Dangeun의 옵션은 몇 개의 전역변수로 기억하면 될 것을 왜 이렇게 불필요하게 구조체를 만들어야 할까? SOption에 있는 멤버들은 ApiEdit가 모두 가지고 있는 것들이라 중복된 정보를 정의하는 낭비가 아닌가 하는 생각이 든다. 물론 이유가 있다.

Dangeun MDI 프로그램이며 한꺼번에 여러 개의 ApiEdit가 생성될 수 있다. 만약 사용자가 각 컨트롤을 다른 설정상태로 변경했다면 설정 대화상자는 어떤 컨트롤의 설정값을 현재의 설정값으로 인정할 것인가 하는 문제점이 있다. 그래서 ApiEdit컨트롤의 멤버변수값으로 현재의 설정상태를 참조할 수는 없으며 설정상태를 호스트가 따로 가지고 ApiEdit는 호스트의 설정정보의 사본을 받아서 초기화해야 한다. 직관적으로 쉽게 이해가 갈 것이다.

설정상태를 구조체로 만들어야 하는 가장 큰 이유는 바로 취소를 지원하기 위해서이다. 설정상태를 개별적인 전역변수들로 만들어 놓고 설정 대화상자는 이 전역변수의 값을 조작하는 방식일 경우 이런 시나리오를 가정해보자. 사용자가 설정 대화상자를 열어 이 옵션 저 옵션 마구 마음에 드는 대로 고쳤다. 그러면 설정 대화상자는 사용자의 선택 결과를 전역변수에 대입해놓을 것이다. 이렇게 실컷 고쳐 놓고는 . 이거 마음에 안 드는군. 처음부터 다시 하면서 취소 버튼을 클릭하여 버렸다고 하자.

그러면 이제 큰일난 것이다. 사용자가 설정 대화상자에서 콤보박스, 체크박스를 누를 때마다 전역변수를 다 바꿔 버렸기 때문에 원래의 설정상태는 온데 간데 없어져 버렸고 취소 불가능한 상태가 되었다. 이런 상태를 방지하려면 설정 대화상자는 설정 전역변수를 직접 건드리지 말고 그 사본(Copy)을 만든 후 사본만 변경해야 한다. 사용자가 확실히 확인 또는 적용 버튼을 클릭했을 때 변경된 사본값으로부터 진짜 변수를 바꿔야 하는 것이다. 즉 취소를 위해서는 설정상태의 사본이 필요하다.

그런데 전역변수에 설정값들이 흩어져 있으면 사본을 만드는 것이 보통 일이 아니다. 구조체는 그 크기에 상관없이 단 하나의 대입문으로 사본을 만들 수 있다. 왜냐하면 구조체끼리의 대입은 구조체 크기만큼의 메모리 복사이기 때문이다. 설정값이 한 구조체에 모두 포함되어 있으면 CopyOption=OriginalOption 하나로 사본을 만들 수 있을 뿐만 아니라 설정 멤버가 늘어나더라도 사본 생성 코드에는 더 신경쓸 필요가 없어진다.

설정 대화상자에서 변경한 값을 다시 돌려 받을 때도 마찬가지로 사본을 원래 옵션 구조체로 복사하면 된다. 또한 사용자가 변경한 옵션은 다음을 위해 레지스트리에 저장해야 하는데 저장할 대상이 한 구조체에 모여 있으니 저장하기도 쉽고 여러 모로 관리하기가 쉬워진다.