Eclipse

Graphical Editing Framework으로 Eclipse 기반 애플리케이션 구현하기

_침묵_ 2007. 4. 1. 00:02

출처 :http://www.ibm.com/developerworks/kr/library/os-gef/index.html

 

Graphical Editing Framework으로 Eclipse 기반 애플리케이션 구현하기

GEF 시작하기

사용자 삽입 이미지
사용자 삽입 이미지
사용자 삽입 이미지

난이도 : 초급

Randy Hudson, Software developer, IBM

2003 년 7 월 29 일

Graphical Editing Framework (GEF)을 사용하여 Eclipse 기반 애플리케이션을 구현하는 초기 단계를 설명한다. GEF는 스테이트 다이어그램, 액티비티 다이어그램, 클래스 다이어그램, AWT용 GUI 빌더, Swing과 SWT, 프로세스 플로우 에디터 등 Eclipse용 다양한 애플리케이션을 구현하는데 사용되었다. Eclipse와 GEF 모두 오픈 소스이다. IBM WebSphere Studio Workbench 에도 포함되었다.

이 글은 GEF를 사용 단계에 초점을 맞춰 설명한다. 각 단계를 완전히 끝내기 보다는 애플리케이션 모델의 하위 세트를 사용하여 이들이 어떻게 작동하는지를 먼저 살펴볼 것이다.

GEF 개요

GEF는 그래픽으로 디스플레이하고 편집할 모델이 있는 것으로 간주한다. 따라서 GEF는 Eclipse 워크벤치 어디에서나 사용되는EditPartViewer유형의) 뷰어를 제공한다. Jface 뷰어와 마찬가지로 GEF 뷰어는 SWT Control에 대한 어댑터이다. 비슷한 것은 그것뿐이다. GEF 뷰어는 모델-뷰-컨트롤러(MVC) 기반이다.

컨트롤러(controllers)는 뷰와 모델 사이의 가교 역할을 한다. (그림 1). 각 컨트롤러 (여기에서는EditPart는 모델을 뷰로 매핑하고 모델을 변경하는 역할을 한다. EditPart 역시 이 모델을 관찰하고 모델의 변경 내역을 뷰에 반영한다. EditPart는 사용자가 인터랙팅 할 객체이다. EditPart는 나중에 더 자세히 다루겠다.



그림 1. Model-View-Controller
사용자 삽입 이미지

GEF는 두 가지 뷰어 유형인 그래픽 기반 뷰어와 트리 기반 뷰어(view)를 제공한다. SWTCanvas에 그려진 그림들을 사용하고 Draw2D 플러그인에서 정의된다. TreeViewer는 뷰에 SWT Tree와 TreeItems를 사용한다.


사용자 삽입 이미지


Step 1. 자신의 모델 가져오기

GEF는 모델에 대해 아무것도 모른다. 각 모델 유형은 아래 기술된 속성을 충족시키는 한 작동된다.

모델에 무엇이 있는가?

모든 것이 모델 안에 있다. 이 모델은 지속되고 복구되는 유일한 것이다. 여러분의 애플리케이션은 모든 중요한 데이터를 이 모델에 저장한다. 편집, 실행 취소, 재실행하는 동안 모델이 하는 일은 그저 인내하는 것이다. 그림과 EditPart는 여러 번 가비지 컬렉션에 있다가 다시 만들어진다.

간단히 말해서, 명령어(Command)는 개념상으로는 모델의 일부이다. 명령어는 모델은 아니지만 이것을 통해 모델이 편집된다. 명령어는 사용자가 실행 취소할 수 있는 모든 변경 사항들을 수행하는데 사용된다.

명령어는 모델에 대해서만 알고 있는 것이 이상적이다. EditPart나 그림을 참조하지 않아야 한다. 또한 명령어는 (팝업 다이얼로그 같은) 사용자 인터페이스를 가급적 호출하지 않아야 한다.

두 개의 모델 이야기

단순한 GEF 애플리케이션은 다이어그램을 그리는데 쓰이는 에디터이다. (여기에서 다이어그램은 단순한 그림을 의미한다. 클래스 다이어그램 같은 그런 다이어그램이 아니다.) 다이어그램은 어떤 형상으로 모델링 될 수 있다. 그 형상은 위치, 색상 등에 대한 속성을 갖고 있고 다중 형상의 그룹 구조일수도 있다. (그림 2)



그림 2. 간단한 모델
사용자 삽입 이미지

또 다른 일반적인 GEF 애플리케이션은 클래스 다이어그램 에디터 같은 우물 에디터이다. 이 다이어그램의 한 가지 중요한 정보는 클래스가 나타나는 (x, y) 위치이다. 이 모델은 X와 Y 속성을 갖고 있는 클래스를 의미한다고 생각할 수도 있다. 대부분의 개발자들은 합리적이지 못한 애트리뷰트로 모델을 더럽히고 싶어하지 않는다. 그와 같은 애플리케이션에서 “비즈니스” 모델은 중요한 상세 의미가 저장된 기본 모델을 의미한다. 다이어그램에서 정보는 "뷰 " 모델에 저장되고(비즈니스 모델에서의 "뷰 "를 의미한다. 즉 한 다이어그램에서 여러 번 보이는 객체이다.) 가끔 분할이 이 작업공간에 반영된다. 다른 리소스들이 이 다이어그램과 비즈니스 모델을 개별적으로 보존하는데 사용된다. 같은 비즈니스 모델에 여러 다이어그램이 있을 수 있다. (그림 3)



그림 3. 비즈니스와 뷰 모델로 분할된 모델
사용자 삽입 이미지

모델이 두 개의 부분으로 분할되든, 다중 리소스로 분할되든, GEF에게는 아무런 상관이 없다. 모델이란 용어는 전체 애플리케이션 모델을 지칭할 때 사용된다. 스크린상의 객체는 이 모델의 다중 객체에 상응한다. GEF는 개발자들이 각 매핑을 쉽게 핸들 할 수 있도록 설계되었다.

공지 전략

뷰의 업데이트는 모델로부터 온 공지 결과인 경우가 대부분이다. 모델은 일정한 공지 메커니즘을 갖고 있고 이 메커니즘이 애플리케이션에 적절한 업데이트로 매핑된다. 읽기 전용 모델이거나, 파일 시스템 또는 원격 연결 같은 공지할 수 없는 모델은 예외이다.

공지 전략은 (객체 당) 분산되거나 (도메인 별로) 중앙화된다. 도메인 공지자는 모델의 모든 객체의 변경 내용을 알고 있고 이러한 변경 사항을 도메인 리스너에게 알린다. 애플리케이션이 이 공지 모델을 사용하면 뷰어 마다 도메인 리스너를 추가해야 한다. 리스너가 변경 사항을 받으면 영향을 받은 EditPart(들)을 찾은 다음 변경 사항들을 적절히 처리한다. 애플리케이션이 분산 공지를 사용한다면 각 EditPart는 고유의 리스너를 영향을 받는 모델 객체들 마다 추가한다.


사용자 삽입 이미지


Step 2. 보기(view) 정의하기

다음 단계에서는 Draw2D 플러그인의 그림을 사용하여 모델을 디스플레이 하는 방법을 결정한다. 어떤 그림은 모델 객체들 중 하나를 직접 디스플레이 하는데 쓰일 수 있다. 예를 들어, Label 그림은 이미지와 String을 디스플레이 하는데 사용된다. 가끔, 여러 그림들, 레이아웃 매니저, 보더들을 조합해서 원하는 결과가 만들어 질 수 있다. 마지막으로, 여러분 애플리케이션 고유의 방식으로 그린 그림을 구현한다.

그림과 레이아웃의 구성과 구현 관련 자세한 내용은 GEF SDK에 포함된 Draw2D 개발자 가이드를 참조하기 바란다.

Draw2D와 GEF를 함께 사용하면 프로젝트 관리가 더 쉬워지고 요구 사항 변경도 더 유연해진다. 다음은 가이드라인이다.

  • 바퀴를 다시 만들지 말라.제공된 레이아웃 매니저들을 결합하는 것 만으로도 거의 대부분 렌더링이 가능하다. 툴바 레이아웃(수직 또는 수평 방향)과 보더 레이아웃의 결합으로 여러 그림들을 구성할 수 있다. 레이아웃 매니저를 작성하는 수 밖에 없다. 하지만 GEF에서 제공하는 팔레트를 보자. 이 팔레트는 Draw2D의 많은 그림들과 레이아웃을 사용하여 렌더링 된다.

  • EditPart와 그림을 철저히 구별한다.EditPart가 여러 그림들, 레이아웃, 보더들의 합성 구조를 사용한다면 EditPart에 많은 상세 부분을 숨겨라. (좋은 생각은 아니지만) EditPart가 자신에 대한 모든 것을 구현하게 하는 것도 가능하다. 하지만 이렇게 하면 컨트롤러와 뷰 구분이 모호해진다. EditPart는 그림 구조에 대해 자세히 알고 있기 때문에 이 구조를 비슷한 EditPart와 함께 사용하는 것은 불가능하다. 또한 외형과 구조를 변경하면 예견치 못한 버그가 생긴다.

    대신, 구조에 대한 상세를 숨길 수 있는 그림의 하위 클래스를 작성하라. 그런 다음, EditPart(컨트롤러)가 뷰를 업데이트 할 때 사용하는 하위 클래스에 대한 최소한의 API를 정의한다. 관심의 분산 이라고 일컬어지는 이러한 관행은 재사용 효과도 높이고 버그도 줄어드는데 효과가 있다.

  • 그림에서 모델 또는 EditPart를 참조하지 말라그림은 EditPart나 모델에 접근해서는 안된다. 어떤 경우, EditPart가 리스너로서 자기 자신을 그림에 추가하기도 하지만 이것은 EditPart로서가 아닌 리스너로서 알려졌을 때만 가능하다. 이러한 디커플링(de-coupling) 방식으로 재사용 효과를 누릴 수 있다.

  • 콘텐트 패인(pane)을 사용하라.가끔, 다른 그래픽 엘리먼트를 포함하게 될 컨테이너가 있고, 그 컨테이너 외부를 꾸며야 할 때가 있다. 예를 들어, 우물 클래스는 일반적으로 박스로 보인다. 그 윗 부분은 클래스 이름과 상투어구로 레이블이 달리고 아래 부분은 애트리뷰트와 메소드를 위해 장소이다. 이것은 여러 그림들을 구성하는 것으로도 가능하다. 첫 번째 그림은 이 클래스의 제목 박스이고 또 다른 그림은 컨텐트 패인(contents pane)이 될 것이다. 이 그림에는 결국 애트리뷰트와 메소드에 대한 그림이 포함될 것이다. EditPart 구현을 나중에 작성할 때 컨텐트 패인이 모든 자식 엘리먼트들의 부모로 사용되도록 하는 것은 식은죽 먹기다.

사용자 삽입 이미지


Step 3. EditPart 작성하기

이번에는 모델과 뷰를 컨트롤러 또는 EditPart에 연결할 것이다. 이것은 "프레임웍(framework)" 을 GEF에 배치하는 단계이다. 제공된 클래스들은 추상적이기 때문에 클라이언트들은 실제로 코드를 작성해야 한다. 하위 클래스 작성은 우리에게도 익숙하고, 모델을 뷰에 매핑하는 유연하고 단순한 방법이다.

하위 클래스 작성에는 세 가지 기본 구현이 제공된다. 트리 뷰어에 나타나는 EditPart용AbstractTreeEditPart를 사용한다. 그래픽 뷰어의AbstractGraphicalEditPartAbstractConnectionEditPart를 확장한다. 여기에서는 그래픽 EditPart에 초점을 맞출 것이다. 같은 원리가 트리 뷰어에도 적용된다.

EditPart 사이클

EditPart를 작성하기 전에 이것의 출처가 어디이고, 더 이상 필요하지 않을 때 어떻게 되는지 알아두면 편리하다. 각 뷰어는 EditPart를 구현할 때 팩토리를 사용하여 설정된다. 뷰어의 컨텐트를 설정할 때 그 뷰어에 대한 인풋을 나타내는 모델 객체를 제공하여 이를 수행한다. 일반적으로 가장 높은 모델 객체이다. 다른 모델 객체들은 그 모델 객체로부터 트래버스 된다. 뷰어는 이 팩토리를 사용하여 인풋 객체에 대한contents EditPart를 구현한다. 뷰어에 있는 각 EditPart는 자식(그리고 연결) EditPart를 파뮬레이트 및 관리하면서 새로운 EditPart가 필요할 때 뷰어가 파퓰레이트 될 때까지 그 EditPart 팩토리에 위임한다. 새로운 모델 객체들이 사용자에 의해 추가되면서 객체들을 나타내는 EditPart들은 상응하는 EditPart들을 구현하여 응답한다. 뷰 구현은 EditPart 구현과 병행한다. 따라서 각 EditPart가 구현되고 부모 EditPart에 추가된 후에, 그림이든 트리 아이템이든, 뷰에서도 같은 일이 일어난다.

EditPart는 사용자가 상응하는 모델 객체를 제거하자 마자 버려진다. 사용자가 삭제를 취소하면 복구된 객체를 나타내기 위해 다시 만들어진 것은 다른 EditPart이다. 이는 EditPart가 장기 정보들을 포함할 수 없고 명령어에 의해 참조되어서는 안되기 때문이다.

첫 번째 EditPart: Contents EditPart

여러분이 작성한 첫 번째 EditPart는 다이어그램에 상응하는 EditPart이다. 이 EditPart는 뷰어의 컨텐츠(contents)이다. 이것은 이 모델의 최 상위 엘리먼트에 해당하며 뷰어의 root EditPart (그림 4)가 그 부모가 된다. 루트(root)는 연결 레이어, 핸들 레이어 같은 다양한 그래픽 레이어를 제공하여 컨텐츠에 대한 기초를 마련한다. 루트의 기능은 각 모델 객체에 독립적이며 GEF는 루트를 위한 여러 구현들을 제공한다.



그림 4. 뷰어의 EditPart
사용자 삽입 이미지

이 컨텐츠의 그림은 그렇게 흥미롭지는 않다. 그리고 다이어그램의 자식들을 포함하고 있는 빈 패널일 경우가 종종 있다. 그림은 모호하고 다이어그램의 자식들을 배치할 레이아웃 매니저를 사용하여 초기화 되어야 한다. 하지만 구조는 있어야 한다. 리턴된 자식 모델 객체들의 리스트가 이 다이어그램의 직접적인 자식을 결정한다. Listing 1은 샘플 EditPart로서 XYLayoutdmf 사용하여 자식을 배치시킬 그림을 만든다.



Contents EditPart의 초기 구현
public class DiagramContentsEditPart extends AbstractGraphicalEditPart {    protected IFigure createFigure() {        Figure f = new Figure();        f.setOpaque(true);        f.setLayoutManager(new XYLayout());        return f;    }    protected void createEditPolicies() {        ...    }    protected List getModelChildren() {        return ((MyModelType)getModel()).getDiagramChildren();    }}

이 다이어그램에 대한 아이템을 결정하기 위해 메소드가 구현된다. 이 메소드는 다이어그램의 노드 같은 자식 모델 객체getModelChildren()들의 리스트를 리턴한다. 수퍼클래스는 이러한 모델 객체들의 리스트를 사용하여 상응하는 EditPart를 만든다. 새롭게 만들어진 EditPart는 자식 EditPart의 부분 리스트에 추가된다. 이렇게 되면 각 자식의 그림이 다이어그램의 그림에 추가된다. 기본적으로 아무것도 없는 리스트가 리턴된다. 이것은 자식이 없다는 의미이다.

그래픽 EditPart

(이 다이어그램의 아이템을 나타내는) 나머지 EditPart들은 그래픽으로 디스플레이 될 데이터를 갖게 될 것이다. 또한 연결 또는 자식 같은 고유의 구조도 갖게 된다. 많은 GEF 애플리케이션들이 이들간 연결을 사용하여 레이블이 달린 아이콘을 나타낸다. EditPart가 그림으로서 Label을 사용하고 모델이 이름, 아이콘, 레이블로 오가는 연결을 제공한다고 가정해보자. Listing 2는 이러한 유형의 EditPart를 구현하는 코드이다.



Listing 2. "node" EditPart의 초기 구현
public class MyNodeEditPart extends AbstractGraphicalEditPart {    protected IFigure createFigure() {        return new Label();    }    protected void createEditPolicies() {        ...    }    protected List getModelSourceConnections() {        MyModel node = (MyModel)getModel();        return node.getOutgoingConnections();    }    protected List getModelTargetConnections() {        MyModel node = (MyModel)getModel();        return node.getIncomingConnections();    }    protected void refreshVisuals() {        MyModel node = (MyModel)getModel();        Label label = (Label)getFigure();        label.setText(node.getName());        label.setIcon(node.getIcon());        Rectangle r = new Rectangle(node.x, node.y, -1, -1);        ((GraphicalEditPart) getParent()).setLayoutConstraint(this, label, r);    }}

refreshVisuals()매소드가 오버라이드(overridden) 된다. 이 메소드는 모델에서 데이터를 사용하여 그림을 업데이트 할 때 호출된다. 이 경우, 이 모델의 이름과 아이콘은 레이블에 반영된다. 중요한 것은, 이 레이블이 그 부모에 대한 레이아웃 제한을 통과하여 배치된다는 점이다. EditPart 콘텐츠에서 XY 레이아웃 매니저를 사용했다. 이 레이아웃은 Rectangle 제한을 사용하여 자식 그림을 배치할 장소를 결정한다. "-1"이라는 넓이와 높이는 이 그림의 최적 크기를 나타낸다.

사용자 삽입 이미지
Tip #1

그림은setBounds(...)메소드를 사용해서는 안된다. XYLayout 같은 레이아웃 매니저를 사용하면 스크롤바가 정확히 업데이트 된다. 또한 XYLayout은 상대적 제한을 절대 위치로 변환하는데 사용된다. 그리고 그림을 원하는 크기로 자동으로 조정하는데 사용된다. (다시 말해서, 제한 넓이와 높이는 –1인 경우가 바로 그것이다.)

refreshVisuals()메소드는 EditPart의 초기화 동안 단 한 번 호출되고 다시 호출되지 않는다. 애플리케이션은 모델 공지에 대해 응답할 때refreshVisuals()을 다시 호출한다. 퍼포먼스를 높이기 위해서 각 모델 애트리뷰트용 코드를 고유의 메소드(또는 "switch"를 가진 하나의 메소드)로 만들면 된다. 이러한 방식으로 모델이 공지될 때 최소한의 코드를 실행하여 변경된 것만 리프레시 할 수 있다.

또 다른 차이점은 연결 지원을 위한 코드이다.getModelChildren()처럼,getModelSourceConnections()getModelTargetConnections()는 노드들 간 링크를 나타내는 모델 객체들을 리턴한다. 이 수퍼클래스는 필요할 때 상응하는 EditPart를 만들고, 이들을 소스 리스트와 연결 목표 EditPart에 추가한다. 노드는 연결을 참조하고 EditPart는 단 한번 만들어진다. GEF는 연결이 한번만 만들어졌는지를 확인한다.

연결하기

EditPart 연결을 작성하는 것도 크게 다르지 않다.AbstractConnectionEditPart의 하위 클래스를 만드는 것으로 시작한다.refreshVisuals()는 모델에서 그림으로 애트리뷰트를 매핑하기 위해 구현된다. 전과는 약간 다르지만 연결 역시 제한을 갖는다. 여기서 제한이란 연결을 바꿀 때 연결 라우터에 의해 사용된다. 더욱이 연결 EditPart의 그림은 Draw2DConnection이 되어야 한다. 여기에는 요구 조건이 한 개 더 있다. 바로 연결 앵커이다.

연결은ConnectionAnchor에 의해 양 끝에 고정되어야 한다. 따라서 연결 EditPart 또는 노드 구현에서 어떤 앵커를 사용할 것인지를 지정해야 한다. 기본적으로 GEF가 Node EditPart가NodeEditPart인터페이스를 구현하여 앵커를 지정하는 것으로 되어있다. 이렇게 하는 한 가지 이유는 각 끝에 있는 노드에서 사용되는 그림에 따라 앵커를 선택하기 때문이다. 연결 EditPart는 노드가 사용하고 있는 그림에 대해 알아서는 안된다. 또 다른 이유는 사용자가 연결을 만들 때 연결 EditPart는 존재하지 않기 때문에 그 노드는 스스로 피드백을 보여줄 수 있어야 하기 때문이다. Listing 3에는 필요한 앵커 지원을 추가한다.



Listing 3. "node" EditPart에 앵커 지원 추가하기
public class MyNodeEditPart    extends AbstractGraphicalEditPart    implements NodeEditPart{    ...    public ConnectionAnchor getSourceConnectionAnchor(ConnectionEditPart connection) {        return new ChopboxAnchor(getFigure());    }    public ConnectionAnchor getSourceConnectionAnchor(Request request) {        return new ChopboxAnchor(getFigure());    }    public ConnectionAnchor getTargetConnectionAnchor(ConnectionEditPart connection) {        return new ChopboxAnchor(getFigure());    }    public ConnectionAnchor getTargetConnectionAnchor(Request request) {        return new ChopboxAnchor(getFigure());    }    ...}

사용자 삽입 이미지
Tip #2

NodeEditPart인터페이스를 실제로 구현하는 것을 잊지 말라. 그렇지 않으면 메소드가 절대로 호출되지 않을 것이다.

연결을 취하는 메소드는 기존 연결 EditPart에 앵커를 설정할 때 사용했던 것이다. 또 다른 두 개의 메소드는 Request를 취한다. 이들 메소드는 사용자가 새로운 연결을 만들 때 편집 과정 중에 사용된다. 이 예제에서 chopbox 앵커는 모든 상황에서 리턴된다. chopbox 앵커는 라인이 노드의 그림에서 바운딩 박스를 가로지르는 점만 찾는다.

연결 EditPart를 구현하는 것은 비교적 간단하다.PolylineConnection의 디폴트 생성은 모든 사용 케이스에 맞기 때문에 그림을 반드시 만들 필요는 없다. (그림 4)



Listing 4. Connection EditPart 초기 구현
public class MyConnectionEditPart extends AbstractConnectionEditPart {    protected void createEditPolicies() {        ...    }    protected void refreshVisuals() {        PolylineConnection figure = (PolylineConnection)getFigure();        MyConnection connx = (MyConnection)getModel();        figure.setForegroundColor(MagicHelper.getConnectionColor(connx));        figure.setRoutingConstraint(MagicHelper.getConnectionBendpoints(connx));    }}

사용자 삽입 이미지
Tip #3

가장 중요한 것은 ConnectionEditParts를 언제 사용할지, 그리고 언제 사용하지 않을지를 알아야 한다. 연결 EditPart는 사용자가 선택하고 인터랙팅할 수 있는 무엇인가 있을 때 사용된다. 여기에는 아마도 모델에 있는 객체로의 직접 코릴레이션이 있고 이것에 의해 삭제될 수 있다.

라인을 그릴 필요가 있는 노드 또는 컨테이너가 있다면 그림의 paint 메소드에서 라인을 그리거나 Polyline 그림을 포함하고 있는 그림을 구성한다.

연결은 언제나 소스와 목표를 갖고 있어야 한다. 소스나 목표 없이 존재할 수 있는 연결이 필요하다면AbstractGraphicalEditPart를 확장하여 연결 그림을 사용하는 것이 더 낫다.

모델 리스닝(listening)

생성 후에 EditPart는 모델에서 변경 공지를 리스닝해야 한다. GEF는 모델 중립적이기 때문에 모든 애플리케이션들은 자신의 리스너를 추가하고 결과 공지를 핸들 해야 한다. 공지가 도착하면 핸들러는 제공된 메소드들 중 한 개를 호출하여 리프레시를 실행한다. 예를 들어, 자식이 삭제되면refreshChildren()을 호출하여 상응하는 EditPart와 이것의 그림도 삭제되도록 한다. 간단한 애트리뷰트 변경일 경우refreshVisuals()가 사용될 수 있다. 이전에 언급했듯이 이 메소드는 다중 부분들로 분해되어 디스플레이 된 애트리뷰트가 쓸데없이 업데이트되는 것을 방지한다.

리스너를 추가하고 이들을 제거하는 것을 잊으면 잦은 메모리 유출의 원인이 된다. 이러한 이유로 리스너를 추가하고 제거한 점이 API에 명확히 표시되어야 한다. EditPart는activate()를 확장하여 나중에 제거되어야 하는 리스너를 추가해야 한다.deactivate()를 확장하여 같은 리스너들을 제거한다. Listing 5는 모델 공지를 위한 노드 EditPart 구현에 추가하는 방법이다.



Listing 5. "node" EditPart에서 모델 변경 리스닝하기
public class MyNodeEditPartextends AbstractGraphicalEditPart    implements NodeEditPart, ModelListener{    ...    public void activate() {        super.activate();        ((MyModel)getModel()).addModelListener(this);    }    public void deactivate() {        ((MyModel)getModel()).removeModelListener(this);        super.deactivate();    }    public void modelChanged(ModelEvent event) {        if (event.getChange().equals("outgoingConnections"))            refreshSourceConnections();        else if (event.getChange().equals("incomingConnections"))            refreshTargetConnections();        else if (event.getChange().equals("icon")          || event.getChange().equals("name"))            refreshVisuals();    }    ...}

모델 편집하기

지금까지 EditPart가 어떻게 만들어지고, 이것이 어떻게 보여지는지, 그리고 모델이 변경될 때 어떻게 업데이트하는지를 설명했다. EditPart는 모델 변경의 핵심 플레이어이다. 이것은 명령어에 대한 요청이 EditPart로 보내질 때 발생한다. 요청은 EditPart가 마우스 드래그 같은 피드백을 보여주도록 요청하는데 사용된다. 또한 EditPart는 요청을 지원, 방지, 무시할 수 있다. 지원 또는 거부되는 요청 유형이 EditPart의 작동을 결정한다.

지금까지의 초점은 모델의 구조와 속성들을 뷰에 매핑하는 것에 맞춰졌다. 이것이 바로 EditPart 클래스에서 여러분이 기본적으로 해야 할 모든 것이다. 이것의 작동은EditPolicies라고 하는 플러거블 헬퍼가 결정한다. 이 예제에서createEditPolicies()메소드를 거부했다. 이 메소드를 구현하면 EditPart로 더 잘 수행된다. 물론 애플리케이션의 모델을 변경하는 방법을 알고 있는 편집 정책들을 제공해야 한다.

작동 편집이 플러거블이기 때문에 다양한 EditPart 구현을 개발할 때 모델을 뷰에 매핑하고 모델 업데이트를 핸들하는 태스크를 중심으로 클래스 계층을 만들 수 있다.


사용자 삽입 이미지


Step 4. 마무리

이제는 모델을 그래픽 디스플레이 하는데 필요한 모든 것들을 갖추었다. 마지막으로IEditorPart를 사용하게 될 것이다. 하지만 GEF의 뷰어들은 뷰, 다이얼로그 또는 제어를 배치할 수 있는 어떤 곳에서나 사용될 수 있다. 바로 이 단계에서 UI를 플러그인 해야 한다. 이것이 개방되는 리소스용 에디터와 파일 확장을 정의할 것이다. 여러분의 모델도 같은 플러그인 또는 개별 플러그인에서 정의될 것이다. 또한 사전에 파퓰레이트 된 모델이 필요하다. 아직 편집 기능이 없기 때문이다.

샘플 모델 데이터를 제공하는 여러 방법들이 있다. 이 예제의 목적에 의거하여 에디터가 열리면 코드에서 모델을 만들 것이다. 파일의 실제 내용은 무시한다. 이렇게 하기 위해 테스트 팩토리가 있다고 가정한다. 대안으로 데이터를 가진 리소스의 사전 파퓰레이팅 할 예제 위자드를 만들 수 있다. (일반적인 위자드는 빈 다이어그램만을 만든다.) 마지막으로 텍스트 에디터를 사용하여 문서의 내용을 직접 작성할 수 있다.

이제 샘플 모델이 생겼으니 모델을 디스플레이 할 에디터 부분을 만든다. 빠르게 시작하는 방법은 GEF의GraphicalEditor의 하위 클래스를 만들거나 복사하는 것이다. 이 클래스는ScrollingGraphicalViewer의 인스턴스를 만들고 에디터의 컨트롤로 작동할 캔버스를 만든다. 이것은 GEF로 시작할 때 도움이 되는 편의상의 클래스이다. 회의적인 팀 환경, 삭제 또는 이동되는 리소스 같은 것도 고려해야 한다.

Listing 6은 샘플 에디터 구현을 보여준다. 반드시 구현되어야 하는 추상 메소드들이 여러 개 있다. 이 글에서는 모델 영속성과 마커는 무시하도록 하겠다. 그래픽 뷰어에 나타낼 두 가지만 다루겠다. 우선 자신의 EditPart 팩토리로 뷰어를 설정하여 Step 3에서 EditPart를 구현한다. 그런 다음 다이어그램 모델 객체에서 뷰어로 전달한다.



Listing 6. Editor Part의 구현
public class MyEditor extends GraphicalEditor {    public MyEditor() {        setEditDomain(new DefaultEditDomain(this));    }    protected void configureGraphicalViewer() {        super.configureGraphicalViewer(); //Sets the viewer's background to System "white"        getGraphicalViewer().setEditPartFactory(new MyGraphicalEditpartFactory());    }    protected void initializeGraphicalViewer() {        getGraphicalViewer().setContents(MagicHelper.constructSampleDiagram());    }    public void doSave(IProgressMonitor monitor) {        ...    }    public void doSaveAs() {        ...    }    public void gotoMarker(IMarker marker) {        ...    }    public boolean isDirty() {        ...    }    public boolean isSaveAsAllowed() {        ...    }}


사용자 삽입 이미지


다음 단계

위의 과정을 통해, 우리는 간단한 모델을 만들어 보았으며, 그래픽에디터상에서 해당 모델을 표시하는 기초적인 방법을 설명했다. 이와 함께 모델의 편집 정책에 대해서도 간단히 살펴보았다. 편집정책에 대한 더 많은 정보는 GEF SDK와 함께 제공되는 개발자 문서를 통해 얻을 수 있다. (GEF home page)

GEF는 다이어그램상에 객체들을 생성하기위해 필요한 각종 툴들을 표시하는 팔레트 기능을 제공한다. 사용자는 드래그앤드랍 방식으로 팔레트 상의 툴들을 이용할 수 있으며, 커스터마이징도 가능하다.

여러 JFace 액션들을 GEF에서 사용할 수 있다. 실행취소, 정렬, 삭제 등은 메뉴, 툴바, 컨텍스트 메뉴에서 사용될 수 있다.

마지막으로 애플리케이션은 아웃라인 뷰와 속성 뷰를 지원해야 한다. 아웃라인 뷰는 네비게이션과 제한된 편집 목적에 사용된다. GEF의 TreeViewer와 개요 윈도우가 여기에서 사용된다. 속성 시트에서 사용자는 현재 선택된 것의 상세한 속성들을 보고 편집할 수 있다.

선택 사항을 보여주고 사용자가 변경할 수 있도록 하려면 편집 정책을 EditPart에 추가해야 한다. GEF 홈페이지 예제와 GEF SDK에 포함된 개발자 문서를 참조하라.

GEF와 Eclipse 워크벤치에서 제공되는 추가 기능들 까지는 이 글에서 설명하지는 않겠다.

  • 팔레트(Palette). 툴의 팔레트는 다이어그램에 새로운 객체들을 만드는 수단이다. GEF에는 드래그 앤드 드롭, 멀티 드로어, 레이아웃 설정, 컨텐트 커스터마이징을 지원하는 팔레트가 포함되어 있다
  • 액션 바(Action bars). Editor와 View는 툴바, 메뉴, 컨텍스트 메뉴에 Action을 제공한다. GEF는 여러 가지 재사용 가능한 액션 구현을 제공하지만 이것은 어디까지나 이를 디스플레이 하는 애플리케이션에 달려있다.
  • 속성 시트(Property sheet). 속성 시트는 선택된 아이템들의 상세한 속성들을 디스플레이 하는데 사용된다. GEF에서는 속성 시트 지원을 EditPart 또는 모델에 추가할 수 있다.
  • 아웃라인(Outline). 아웃라인 뷰는 다이어그램의 구조를 보여준다. 대게는 모든 것에 사용될 수 있다. GEF의 TreeViewer가 이 아웃라인 뷰에 종종 사용된다.

사용자 삽입 이미지


참고자료


사용자 삽입 이미지


필자소개

사용자 삽입 이미지

사용자 삽입 이미지

Randy Hudson: 소프트웨어 개발자, IBM.

'Eclipse' 카테고리의 다른 글

[번역완료] Java Application Profiling using TPTP  (0) 2007.04.25
GEF - Editor with Palette  (0) 2007.03.31
GEF - 원그리기  (0) 2007.03.31