. 스타일 정보

분석기의 스타일별 색상은 생성자에서 초기화하는데 이 색상은 변경 가능한 설정 대상이다. 사람마다 좋아하는 색상이 다르므로 당연히 변경할 수 있도록 해야 한다. 또한 분석기와 확장자의 연결 상태는 SOption::Init에서 초기화하는데 이 상태도 변경할 수 있다. 어떤 파일을 어떤 분석기와 연결할 것인가도 사용자의 취향을 반영해야 한다.

하지만 구문 분석 방식은 하드 코딩되어 있기 때문에 사용자가 변경할 수 없다. 예를 들어 C++ 키워드 목록을 편집한다거나 스타일을 하나 더 추가하는 것은 불가능하다. 이런 설정까지 가능하게 만들려면 IsKeyword 등의 함수에 지역적으로 선언되어 있는 키워드 목록을 외부 파일에 두고 실행중에 읽어와야 하는데 현재 구조는 그런 방식을 지원하기 어렵다. 컨텍스트간의 변환 흐름이나 구분자 목록 등도 사용자가 변경할 수 없으며 오로지 색상과 확장자만 변경할 수 있도록 할 것이다.

사용자가 변경할 수 있는 옵션들 중 가장 실용적인 것은 역시 스타일의 색상이다. 이 값을 사용자가 선택하도록 하려면 분석기의 생성자에서 초기화한 색상 대신 SOption에 기억된 색상값을 사용하도록 해야 한다. SOption에 스타일 색상 기억을 위한 멤버를 추가하도록 하자.

 

struct SOption

{

    void GetStyleColor();

    void SetStyleColor(CParse *Parser);

     ....

     TCHAR arExt[4][250];

    SParseStyle arStyle[4][32];

};

 

arStyle은 각 분석기의 스타일을 지정하는 2차원 구조체 배열인데 첫 번째 첨자는 분석기의 ID이며 두 번째 첨자는 스타일 번호이다. 예를 들어 arStyle[1][2] 1번 분석기의 2번 스타일에 대한 이름과 색상값 정보를 가진다. 기본 분석기의 스타일은 Init에서 따로 초기화한다.

 

void SOption::Init()

{

     ....

     arStyle[0][0].fore=-1;

}

 

SParseStyle구조체의 생성자는 모든 전경색을 끝 표시인 -2로 초기화하는데 기본 분석기는 스타일이 하나밖에 없으므로 0번 스타일을 기본색으로만 바꾸면 된다. 배경색은 이미 생성자에서 -1로 초기화되어 있다. 기본 분석기를 제외한 나머지 분석기의 스타일 색상은 GetStyleColor 함수가 초기화한다.

 

void SOption::GetStyleColor()

{

     CParse *Parser;

     int id,i;

 

     for (id=1;id<=3;id++) {

          switch(id) {

          case 1:

              Parser=new CParseCpp;

              break;

          case 2:

              Parser=new CParseHtml;

              break;

          case 3:

              Parser=new CParseSql;

              break;

          }

          for (i=0;i<32;i++) {

              Parser->GetStyleName(i,arStyle[id][i].name);

              Parser->GetStyleColor(i,arStyle[id][i].fore,arStyle[id][i].back);

              if (arStyle[id][i].fore==-2)

                   break;

          }

 

          delete Parser;

     }

}

 

1번부터 3번까지 현재 ApiEdit가 지원하는 분석기를 순서대로 생성하여 분석기의 생성자가 초기화한 스타일 색상과 이름을 읽어 arStyle 배열에 채운다. 이 함수에 의해 arStyle은 분석기의 디폴트 스타일 색상을 가지게 될 것이다. 0번 기본 분석기는 스타일 색상을 가지지 않으므로 이 함수에서 조사할 필요가 없으며 Init에서 상수 초기화하였다. 다음 함수는 스타일의 색상을 변경한다.

 

 

void SOption::SetStyleColor(CParse *Parser)

{

     int i;

     int id;

 

     id=int(Parser->GetInfo(0));

 

     for (i=0;i<32;i++) {

          Parser->SetStyleColor(i,arStyle[id][i].fore,arStyle[id][i].back);

          if (arStyle[id][i].fore==-2)

              break;

     }

}

 

Parser 분석기 객체의 포인터를 인수로 전달하면 arStyle에 저장된 스타일 색상 전체를 분석기에게 대입한다. CParser는 호스트로부터 스타일 색상을 대입받기 위해 SetStyleColor 멤버함수를 가지고 있다. 문법의 색상을 변경하고 싶다면 arStyle의 색상표를 편집한 후 이 함수를 호출하면 된다. GetStyleColor 함수는 호스트의 OnCreate에서 최초 실행시에 딱 한 번만 호출된다.

 

int OnCreate(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

     ....

     Option.StartAction=SHRegReadInt(SHCU,KEY"Setting","StartAction",1000);

     if (Option.StartAction == 1000) {

        Option.GetStyleColor();

     } else {

     ....

 

StartAction 레지스트리 키가 없을 때, 즉 아직 레지스트리에 아무 정보도 작성되어 있지 않을 때만 이 함수가 호출되어 ApiEdit의 디폴트 스타일 색상을 조사한다. 두 번째 실행될 때부터는 이 함수가 호출되지 않는다. 권장옵션으로 돌아갈 때도 처음 실행될 때와 마찬가지 조건이므로 이 함수를 호출한다.

 

BOOL CALLBACK GeneralDlgProc(HWND hDlg,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

     ....

          case IDC_BTNDEFAULT:

              if (MessageBox(hDlg,"모든 설정 상태를 처음 설치 상태로 초기화하시겠습니까?",

                   "알림",MB_YESNO)==IDYES) {

                   NewOption.Init();

               NewOption.GetStyleColor();

                   PropSheet_Changed(GetParent(hDlg),hDlg);

                   goto label_reload;

              }

              return TRUE;

 

SOption구조체의 멤버임에도 생성자나 Init에서 arStyle을 초기화하지 않는 이유는 사본을 만들 때 GetStyleColor 함수가 호출되지 않도록 하기 위해서이다. GetStyleColor 함수는 분석기 객체를 동적으로 전부 생성해보고 일일이 색상을 물어 보기 때문에 무척이나 느리다. 지금은 분석기가 4개밖에 없으므로 괜찮지만 만약 20개 정도로 늘어나면 문제가 심각해질 것이다.

설정 대화상자를 띄울 때 NewOption이라는 이름으로 사본을 만드는데 그때마다 모든 분석기가 생성되었다가 파괴된다면 어떻게 되겠는가? NewOption Option을 복사받으므로 색상 조사를 위해 분석기를 굳이 만들어 볼 필요도 없다. 또한 새 윈도우가 만들어질 때마다 SetSetting 함수에서 원래 옵션을 조사하기 위해 SOption 구조체를 하나 만드는데 이때도 마찬가지다. GetStyleColor 함수 호출을 최소한으로 자재하면 대화상자가 뜨는 속도, 새 윈도우를 만드는 속도가 빨라진다. 즉 프로그램의 반응성이 높아진다.

GetStyleColor 함수에 의해 조사된 스타일 색상은 SOption::Save 함수에 의해 레지스트리에 저장된다. 확장자 연결 상태도 같이 저장하도록 하였다.

 

void SOption::Save(TCHAR *Key)

{

     TCHAR szSub[MAX_PATH];

     int i,j;

 

     ....

     lstrcpy(szKey,Key);

     lstrcat(szKey,"Style");

     for (i=1;i<=3;i++) {

          wsprintf(szSub,"%d-Ext",i);

          SHRegWriteString(SHCU,szKey,szSub,arExt[i]);

          for (j=0;j<32;j++) {

              if (arStyle[i][j].fore == -2) {

                   break;

              }

              wsprintf(szSub,"%d-%d-name",i,j);

              SHRegWriteString(SHCU,szKey,szSub,arStyle[i][j].name);

              wsprintf(szSub,"%d-%d-fore",i,j);

              SHRegWriteInt(SHCU,szKey,szSub,arStyle[i][j].fore);

              wsprintf(szSub,"%d-%d-back",i,j);

              SHRegWriteInt(SHCU,szKey,szSub,arStyle[i][j].back);

          }

     }

}

 

1번부터 3번 분석기까지 루프를 돌면서 0번 스타일에서 31번 스타일까지 색상과 이름을 조사하여 레지스트리에 기록하였다. 분석기의 첨자가 1부터 시작함을 유의하자. 기본 분석기의 스타일 색상은 항상 고정되어 있기 때문에 사용자가 변경할 수 있는 옵션이 아니며 따라서 저장 대상도 아니다. Init에서 전경색만 -1로 바꾸면 기본 분석기에 대한 처리는 더 이상 할 필요가 없다. 이렇게 저장된 스타일 색상은 SOption::Load에서 다시 읽어들인다.

 

 

 

void SOption::Load(TCHAR *Key)

{

     TCHAR szSub[MAX_PATH];

     int i,j;

 

     ....

     lstrcpy(szKey,Key);

     lstrcat(szKey,"Style");

     for (i=1;i<=3;i++) {

          wsprintf(szSub,"%d-Ext",i);

          SHRegReadString(SHCU,szKey,szSub,"",arExt[i],256);

          for (j=0;j<32;j++) {

              wsprintf(szSub,"%d-%d-fore",i,j);

              arStyle[i][j].fore=SHRegReadInt(SHCU,szKey,szSub,-2);

              if (arStyle[i][j].fore == -2) {

                   break;

              }

              wsprintf(szSub,"%d-%d-name",i,j);

              SHRegReadString(SHCU,szKey,szSub,"",arStyle[i][j].name,32);

              wsprintf(szSub,"%d-%d-back",i,j);

              arStyle[i][j].back=SHRegReadInt(SHCU,szKey,szSub,0);

          }

     }

}

 

이제 SOption::arStyle 배열은 실제로 화면출력에 사용될 스타일 색상을 영구히 가지게 될 것이다. 이 정보는 분석기가 선택될 때마다 SetStyleColor 함수에 의해 분석기로 다시 전달된다. OnCommand에서 분석기를 변경할 때마다 이 함수를 호출하도록 하자.

 

void OnCommand(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

     ....

     case IDM_SYN_NONE:

          pSi->Ae.SetParser(0);

          break;

     case IDM_SYN_CPP:

          pSi->Ae.SetParser(1);

        Option.SetStyleColor(pSi->Ae.GetParser());

          break;

     case IDM_SYN_HTML:

          pSi->Ae.SetParser(2);

        Option.SetStyleColor(pSi->Ae.GetParser());

          break;

     case IDM_SYN_SQL:

          pSi->Ae.SetParser(3);

        Option.SetStyleColor(pSi->Ae.GetParser());

          break;

 

단 기본 분석기는 별도로 스타일 색상을 줄 필요가 없다. 진짜 분석기가 아니라 자리를 채우기 위한 가짜 분석기이므로 여러 모로 차별대우를 좀 받고 있다. SelectParser에서 확장자에 따른 분석기를 자동 선택할 때도 이 함수를 호출한다.

 

void SelectParser(CApiEdit &Ae,TCHAR *path)

{

     ....

     Ae.SetParser(ID);

    Option.SetStyleColor(Ae.GetParser());

}

 

여기까지 작성하면 당근은 이제 분석기의 생성자가 초기화하는 색상보다 레지스트리에 저장된 색상대로 문서를 출력하게 될 것이다. 레지스트리의 색상을 초기화하려면 레지스트리 편집기를 열어서 당근의 루트키를 완전히 삭제한 후 테스트해야 한다. 또는 도구/설치 정보 삭제 명령으로도 초기화할 수 있다. OnCreate에서 StartAction키가 없어야만 GetStyleColor 함수를 호출하기 때문에 레지스트리를 한 번 비워야 한다. 아직 색상을 변경할 수는 없지만 변경할 준비는 완료되었으며 이전과 똑같이 동작할 것이다.