. 옵션의 적용

설정 대화상자를 통해 사용자는 NewOption의 값을 원하는 대로 변경하게 된다. 이렇게 변경된 옵션은 사용자가 확인 버튼을 클릭하거나 적용 버튼을 클릭하여 명시적으로 적용할 것을 명령해야만 실제로 적용된다. 아무리 옵션을 복잡하게 바꾸었더라도 변경되는 대상은 NewOption뿐이며 취소 버튼을 클릭하면 모든 설정상태는 원래의 값을 유지하게 될 것이다.

사용자가 확인 또는 적용 버튼을 클릭할 때는 각 설정 대화상자로 PSN_APPLY 통지 메시지가 전달되며 이 메시지를 받았을 때가 변경된 옵션을 실제로 적용할 때이다. ApiEdit Dangeun의 모양이나 동작방식을 변경할 시점인 것이다. 각각의 설정 페이지들은 PSN_APPLY를 받았을 때 자신이 가진 옵션 중에 변경된 것이 있으면 적용해야 하나 개별 페이지들이 이 작업을 따로따로 하자면 코드가 중복되므로 모든 적용 코드를 ApplyNow라는 함수에 작성하기로 한다. 페이지들은 PSN_APPLY 메시지를 받았을 때 ApplyNow 함수만 호출하면 된다.

ApplyNow의 코드는 다음과 같다. 지금까지 만들어져 있는 모든 옵션 상태가 이 함수에서 적용되는데 코드의 길이는 좀 길지만 비슷한 코드들의 반복일 뿐이다.

 

void ApplyNow()

{

     HWND hChild;

     SInfo *pSi;

     HDC hdc;

     LOGFONT tFont;

 

     hChild=GetWindow(g_hMDIClient,GW_CHILD);

     while (hChild) {

          pSi=(SInfo *)GetWindowLong(hChild,0);

 

          if (Option.bNoFirstSpace != NewOption.bNoFirstSpace) {

               pSi->Ae.SetNoFirstSpace(NewOption.bNoFirstSpace);

          }

 

          if (Option.RightWrap != NewOption.RightWrap) {

               pSi->Ae.SetRightWrap(NewOption.RightWrap);

          }

 

          if (Option.ColMark != NewOption.ColMark) {

               pSi->Ae.SetColMark(NewOption.ColMark);

          }

 

          if (Option.bShowLineNum != NewOption.bShowLineNum) {

               pSi->Ae.SetShowState(SHOWLINENUM,NewOption.bShowLineNum);

          }

 

          if (Option.bShowMargin != NewOption.bShowMargin) {

               pSi->Ae.SetShowState(SHOWMARGIN,NewOption.bShowMargin);

          }

 

          if (Option.bShowEnter != NewOption.bShowEnter) {

               pSi->Ae.SetShowState(SHOWENTER,NewOption.bShowEnter);

          }

 

          if (Option.bShowTab != NewOption.bShowTab) {

               pSi->Ae.SetShowState(SHOWTAB,NewOption.bShowTab);

          }

 

          if (Option.bShowSpace != NewOption.bShowSpace) {

               pSi->Ae.SetShowState(SHOWSPACE,NewOption.bShowSpace);

          }

 

          if (Option.LineRatio != NewOption.LineRatio) {

               pSi->Ae.SetLineRatio(NewOption.LineRatio);

          }

 

          if (Option.nShowCurLine != NewOption.nShowCurLine) {

               pSi->Ae.SetShowCurLine(NewOption.nShowCurLine);

          }

 

          if (Option.HideSelType != NewOption.HideSelType) {

               pSi->Ae.SetHideSelType(NewOption.HideSelType);

          }

 

          if (lstrcmp(Option.logfont.lfFaceName,NewOption.logfont.lfFaceName) != 0 ||

               Option.logfont.lfHeight != NewOption.logfont.lfHeight) {

               tFont=NewOption.logfont;

               hdc=GetDC(NULL);

               tFont.lfHeight=NewOption.logfont.lfHeight*GetDeviceCaps(hdc,LOGPIXELSY)/72;

               tFont.lfWidth=0;

               ReleaseDC(NULL,hdc);

               pSi->Ae.SetFont(&tFont);

          }

 

          if (Option.cFore != NewOption.cFore) {

               pSi->Ae.SetForeColor(NewOption.cFore & 0xff000000 ?

                   NewOption.cFore & 0xffffff:arSysColor[0]);

          }

 

          if (Option.cBack != NewOption.cBack) {

               pSi->Ae.SetBackColor(NewOption.cBack & 0xff000000 ?

                   NewOption.cBack & 0xffffff:arSysColor[1]);

          }

 

          if (Option.cSelFore != NewOption.cSelFore) {

               pSi->Ae.SetSelForeColor(NewOption.cSelFore & 0xff000000 ?

                   NewOption.cSelFore & 0xffffff:arSysColor[2]);

          }

 

          if (Option.cSelBack != NewOption.cSelBack) {

               pSi->Ae.SetSelBackColor(NewOption.cSelBack & 0xff000000 ?

                   NewOption.cSelBack & 0xffffff:arSysColor[3]);

          }

 

          if (Option.MarColor1 != NewOption.MarColor1) {

               pSi->Ae.SetMarColor1(NewOption.MarColor1 & 0xff000000 ?

                   NewOption.MarColor1 & 0xffffff:arSysColor[4]);

          }

 

          if (Option.MarColor2 != NewOption.MarColor2) {

               pSi->Ae.SetMarColor2(NewOption.MarColor2 & 0xff000000 ?

                   NewOption.MarColor2 & 0xffffff:arSysColor[5]);

          }

 

          if (Option.MarkColor != NewOption.MarkColor) {

               pSi->Ae.SetMarkColor(NewOption.MarkColor & 0xff000000 ?

                   NewOption.MarkColor & 0xffffff:arSysColor[6]);

          }

 

          if (Option.CodeColor != NewOption.CodeColor) {

               pSi->Ae.SetCodeColor(NewOption.CodeColor & 0xff000000 ?

                   NewOption.CodeColor & 0xffffff:arSysColor[7]);

          }

 

          if (Option.CurColor != NewOption.CurColor) {

               pSi->Ae.SetCurColor(NewOption.CurColor & 0xff000000 ?

                   NewOption.CurColor & 0xffffff:arSysColor[8]);

          }

 

          if (Option.NumColor != NewOption.NumColor) {

               pSi->Ae.SetNumColor(NewOption.NumColor & 0xff000000 ?

                   NewOption.NumColor & 0xffffff:arSysColor[9]);

          }

 

          if (Option.bHideCurLine != NewOption.bHideCurLine) {

               pSi->Ae.SetHideCurLine(NewOption.bHideCurLine);

          }

 

          if (Option.bCalcTabWithAvg != NewOption.bCalcTabWithAvg) {

               pSi->Ae.SetCalcTabWithAvg(NewOption.bCalcTabWithAvg);

          }

 

          if (Option.CaretWidth != NewOption.CaretWidth) {

               pSi->Ae.SetCaretWidth(NewOption.CaretWidth);

          }

 

          if (Option.TabWidth != NewOption.TabWidth) {

               pSi->Ae.SetTabWidth(NewOption.TabWidth);

          }

 

          if (Option.bAllowDrag != NewOption.bAllowDrag) {

               pSi->Ae.SetAllowDrag(NewOption.bAllowDrag);

          }

 

          if (Option.bUseLineEnd != NewOption.bUseLineEnd) {

               pSi->Ae.SetUseLineEnd(NewOption.bUseLineEnd);

          }

 

          if (Option.nWrap != NewOption.nWrap) {

               if (pSi->Ae.GetWrap() != 0) {

                   pSi->Ae.SetWrap(NewOption.nWrap);

               }

          }

          hChild=GetWindow(hChild,GW_HWNDNEXT);

     }

 

     Option=NewOption;

}

 

첫 차일드의 핸들을 hChild에 구하고 while 루프를 돌며 열린 모든 차일드에 대해 옵션을 적용한다. 즉 생성되어 있는 모든 ApiEdit 컨트롤이 변경된 옵션의 영향을 받는 것이다.

옵션을 변경하는 코드는 아주 간단하다. NewOption Option의 대응되는 변수값을 비교하여 변경되었으면 ApiEdit의 관련 함수를 호출하여 내부 설정 변수값을 바꾸고 필요한 조치를 하도록 하는 것이다. NewOption 구조체는 설정 대화상자가 열리기 직전에 Option 구조체를 복사했으며 이 함수가 호출되었을 때는 사용자가 변경한 옵션 상태를 가지고 있을 것이다. 그래서 NewOption Option을 비교해보면 사용자가 어떤 옵션을 변경했는지를 알 수 있다. 예를 들어 탭 크기를 적용하는 다음 코드를 보자.

 

if (Option.TabWidth != NewOption.TabWidth) {

     pSi->Ae.SetTabWidth(NewOption.TabWidth);

}

 

Option TabWidth(원래 탭 폭) NewOption TabWidth(변경된 탭 폭)와 다르다는 조건은 사용자가 탭 크기 옵션을 조정했다는 뜻이다. 이 경우 Ae SetTabWidth 멤버함수를 호출하여 새로 변경된 탭 폭을 전달한다. ApiEdit는 이 함수가 호출되었을 때 내부적으로 유지하고 있는 TabWidth를 변경할 것이고 이 값으로부터 실제의 탭 크기인 TabSize를 재계산하며 정렬 및 스크롤 정보 갱신을 할 것이다.

사용자가 탭 크기 옵션을 조정하지 않으면 아무 일도 일어나지 않는다. 그래서 NewOption Option의 대응되는 멤버를 비교하는 것이 중요하다. 만약 사용자의 옵션 조정 여부를 확인하지 않고 무조건 옵션을 적용하면 ApiEdit의 모든 Set 함수가 호출될 것이고 불필요한 시간을 낭비하게 된다.

색상을 적용하는 옵션은 조금 특이하다. 색상값을 기억하는 변수의 상위 바이트는 색상표의 색상 번호를 가지는데 만약 이 값이 0이면 기본색이므로 arSysColor 배열에서 읽은 기본 색상을 적용해야 한다. 글꼴의 이름이 변경되었거나 크기가 변경되었을 때 SetFont 함수를 호출하는데 logfont 구조체의 lfHeight는 포인트 단위의 값을 가지므로 이 값을 픽셀로 바꾼 후 적용해야 한다. nWrap 옵션도 적용하는 방식이 조금 다르다. Option구조체의 nWrap은 개행을 할 경우 어떤 개행 방식을 적용할 것인가를 지정하는 것이므로 ApiEdit가 자동개행 상태일 때만 적용된다.

모든 차일드에 대해 변경된 옵션을 적용한 후 마지막으로 Option=NewOption 대입문으로 변경된 옵션을 현재 옵션으로 만든다. 이 대입문으로 옵션 상태를 기억하는 Option 구조체는 사용자의 모든 설정 지시를 기억하게 되며 NewOption은 그 소임을 다하고 다음 설정 대화상자가 호출될 때까지 쉬게 된다.

ApiEdit에 직접 적용되지 않는 값은 Option에 대입해놓기만 한다. StartAction 같은 옵션은 바꾼다고 해서 당장 어떤 효과를 발휘해야 하는 것이 아니므로 ApplyNow 함수는 이런 옵션들에 대해서는 아무런 조치를 취하지 않아도 된다.