[C++Builder] IDE 확장 - 나만의 Component Editor 를 추가해보자

볼랜드포럼에 올렸던 글을 블로그에 재게시 합니다.
http://cbuilder.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_tutorial&no=129


지금까지의 내용을 정리해 봅시다.

처음에는 멀티라인 입력이 가능하도록 TLabel 을 상속받아서 Lines 속성을 추가하여 TMyLabel 을 만들었습니다.

그 다음에는 Font , Color 의 변경이 쉽도록 Component Editor 를 추가하였습니다.

그런데 더 욕심이 생깁니다. 이런 것조차도 귀찮아집니다. 기존의 TLabel을 그대로 쓰고 다만 Component Editor만
마음에 드는 놈이 있었으면 좋겠습니다. TLabel 을 쓰던 프로젝트를 그대로 쓸수 있고 여러가지 속성을 변경시킬 수
있는 편리한 전용 에디터가 있다면 너무 편리할 것 같습니다.

그래서 Caption , Font , Color 속성을 편집할 수 있는 Component Editor 를 만들어보도록 하겠습니다.

컴포넌트 에디터를 만드는 방법은 일반 Application 만드는 방법과 다른점은 없습니다.
다만 별도의 class 를 추가하고 동작을 정의하는 멤버 함수를 추가해 주어야 합니다.

그리고 멤버함수에서 폼을 동적생성하고 ShowModal 해서 화면에 표시하는 것입니다.
화면에 나타난 폼을 이용해서 필요한 작업을 하고 OK 버튼을 눌렀을 때 변화된 내용을 선택된 컴포넌트에 적용하는
것입니다.

순서는 다음과 같습니다.

(1) File -> New -> Application 해서 폼을 화면에 나타내고 필요한 컴포넌트를 drop 합니다.
(2) 폼의 이벤트 핸들러를 작성합니다.
(3) TComponentEditor 를 상속받은 Component Editor class 를 헤더 파일에 작성합니다.
(4) cpp 파일에 멤버함수를 구현합니다.
(5) 멤버함수에서 (1)에서 만든 폼을 new 연산자를 이용하여 동적 생성합니다.
(6) 폼이 화면에 나타나기 전에 설정해 주어야 하는 것들을 먼저 작성합니다.
(7) ShowModal 해서 폼을 화면에 나타냅니다.
(8) OK 버튼을 누르면 선택된 컴포넌트에 적용해야 하는 것들을 작성합니다.
(9) 폼을 delete 로 삭제합니다.



작성해야 하는 파일은 모두 7개입니다.

MyCompoEditor.cpp           //Component Editor 가 있는 메인 소스
MyCompoEditor.h              //Component Editor 의 class 정의
MyCompoEditor.dfm          //Form 내용 저장
MyCompoEditor_bcb6.cpp  //패키지의 엔트리 소스
MyCompoEditor_bcb6.res   //리소스 파일
MyCompoEditor_bcb6.bpk  //패키지 설정 파일
MyCompoEditor_reg.cpp    //패키지를 등록하는 Register 함수가 있는 파일


새로운 Application 을 시작해서 파일명을 MyCompoEditor.cpp 로 저장한후 적당한 폼을 구성해서
이벤트 핸들러를 작성하고 TComponentEditor 를 상속받은 TMyCompoEditor 를 만들어서 멤버 함수들을
작성해 줍니다. 컴포넌트를 더블 클릭하면 실행되는 것이므로 Edit 를 함수를 써도 되지만 그냥 연습해 보는
차원에서 GetVerb, GetVerbCount, ExecuteVerb 함수를 작성하는 것으로 합니다.

그뒤에 Register() 함수를 저장할 MyCompoEditor_reg.cpp 를 작성하고, 패키지 파일을 만들어서
MyCompoEditor.cpp , MyCompoEditor_reg.cpp 를 Add 합니다.

아래와 같이 말입니다.




MyCompoEditor.h 에 들어갈 class 정의는 다음과 같습니다.
class PACKAGE TMyCompoEditor : public TComponentEditor
 {
   public:
     int __fastcall GetVerbCount(void);
     AnsiString __fastcall GetVerb(int Index);
     void __fastcall ExecuteVerb(int Index);
 };

GetVerbCount, GetVerb 함수는 전과 거의 비슷합니다. 문제는 ExecuteVerb 함수입니다.
시작할 때 폼을 new 로 생성합니다.
TMyCompoEditorForm* MyCompoEditor = new TMyCompoEditorForm(NULL);


그리고, 이제부터 폼을 화면에 띄우기 전에 해야할 일들을 채워나갑니다.

그 전에 폼이 어떻게 생겼는지 구경해 봅시다.



  폼을 크게 3개로 나눕니다. 왼쪽의 Memo 는 Caption 에 들어갈 내용을 적는 것입니다. 오른쪽은 PageControl 로
Font, Color 를 변경합니다. Font TabSheet  에는 Font 의 Name , Size , Style 을 변경할 수 있습니다. 변경하면
왼쪽의 Memo 에 변경내용이 바로 적용됩니다. 단, Size 는 Memo 에는 적용하지 않고 OK 버튼을 누르면 컴포넌트에
적용됩니다. Color TabSheet 는 이전에 델마당에서 조무영님께서 공개해 주셨던 소스가 있습니다. 이걸 장성호님께서
C++ Builder 용으로 옮기셨습니다. 소스를 몇가지 변경하여 적용하였습니다. 장성호님 말씀대로 C++Builder 에서 속도가 느립니다. 나름대로 최적화 해보려고 했지만 델파이만큼은 빨라지지 않습니다. 처음에는 for 문을 바꾸어서 값을 비교하지 않도록 해봤지만 거의 달라지지 않습니다. 이런 주제는 계속 연구해 봐야 할 것 같습니다. 델파이 소스를 C++ 로 옮겼을때 최적화 방법 같은 것 말입니다.

소스가 길기 때문에 간단하게만 소개합니다.

선택된 컴포넌트를 TLabel* 로 타입캐스팅해서 멤버에 접근할 수 있도록 합니다.
TLabel* MyCompo = (TLabel*)Component;

선택된 컴포넌트는 MyCompo , 컴포넌트 에디터는 MyCompoEditor 로 지정해서 이 둘간의 정보를 교환합니다.

예를 들어 선택된 컴포넌트의 Font Style 이 Italic 이면 Component Editor 를 호출했을 때 Italic CheckBox 가 Checked 로 표시되는 것입니다. 편집과정에서 CheckBox 를 해제하고 OK 버튼을 누르면 해제된 Style이 컴포넌트에 적용됩니다. 실제로 동작시켜보면 원리를 쉽게 알 수 있습니다.

특이한 것은 Contains 함수입니다. 이것은 델파이에서 in 이라는 키워드와 같습니다. 델파이에서 in 은 집합의 원소가 있는 지를 알아보는 키워드입니다. 원소가 있으면 true 없으면 false 입니다. C++ 은 컴파일러 차원에서 집합을 지원하지 않습니다. 그래서 C++Builder 제작자들이 Contains 라는 함수를 사용하여 델파이와 비슷하게 이용할 수 있도록 하였습니다.
if(MyCompo->Font->Style.Contains(fsBold)) //선택된 컴포넌트의 폰트가 Bold 이면
   MyCompoEditor->BoldChkBx->Checked = true; //CheckBox 를  checked 로 한다.

위의 if 문에서 선택된 컴포넌트의 Font Style 에 fsBold 가 들어있으면 true 를 반환합니다.

집합 이야기가 나온김에 하나더 알아봅시다. 집합에 원소를 추가하고 삭제하는 것은 어떻게 할까요?

<< 는 집합에 원소를 추가하고
>> 는 집합에서 원소를 뺍니다.
Memo->Font->Style = Memo->Font->Style << fsBold; //Memo 의 Font Style 에 Bold 추가

위와 같이 << 연산자를 이용하여 Font Style 에 fsBold 원소를 추가하는 것입니다.


이제 폼을 화면에 보여줍니다.
MyCompoEditor->ShowModal(); //EditorForm 을 화면에 보여준다.

그 뒤 OK 버튼을 누르면 컴포넌트에 적용시켜야 할 속성들을 변경하고 Designer->Modified(); 한후 delete 로 폼을
파괴합니다.


이벤트 핸들러 중에서 특이한 것은 Font Size 를 변경하는 부분입니다. 최대값을 99 , 최소값을 8로 지정하여 이 값이 넘어가지 않도록 하고 Edit 창에서 직접 수동으로 입력할 수 있도록 합니다. 그런데 그냥 AnsiString 으로 하면 내용을 모두 지울때 에러 창을 내보내기 때문에 C 의 문자열 방식으로 변경해야 합니다.
int size = atoi(Fontsize->Text.c_str()); //직접 값을 입력할 수 있도록 문자열을 C 형식으로 바꾸고 정수변환


이제 소스를 컴파일하고 설치해 봅니다.


Install 이 성공하면 다음과 같은 창이 뜹니다.




컴포넌트 팔레트에 등록되지 않으므로 registered 메시지는 뜨지 않습니다. 여기서 만든것은 Component Editor 이지
Component 가 아니므로 컴포넌트 팔레트에서는 찾을 수 없습니다.

Register 함수에서 TMyCompoEditor 를 TLabel 의 Component Editor 로 지정했습니다.
File -> New -> Applicaiton 해서 Label 을 폼에 drop 하고 더블 클릭하면 드디어 원하는 것이 나타납니다.

다음과 같이...




이번 Component Editor 제작에 참고했던 소스는 Imp 님의 ImpStringGridEditor.pas 와 Graphics32 의 GR32_Dsgn_Bitmap.pas 였습니다. 적당히 복잡하면서 이런 저런 구현이 있는 소스가 분석하기에는 좋더군요. 구성을 어떻게 해야 하는지 배울 수 있었습니다. 직접 만들어서 문제점을 해결해 나가다 보니까 소스의 구조가 눈에 확 들어오더군요. 소스를 공개해준 컴포넌트 제작자 분들과 컬러 선택 다이얼로그 소스를 공개하신 조무영, 장성호 님에게도 감사를 드립니다.