eclipse

eclipse atf selection event handling

gonGon 2010. 8. 27. 19:30
eclipse atf는 중심이 되는 MozBrowserEditor와 대여섯개의 view, 그리고 debug perspective로 구성된다.

editor와 view들은 각각 selection event를 전파(provide)하거나 구독(listen)한다.
editor와 특정 view들은 provider이면서 listener이기도 하다.

provider가 되기 위해서는 ISelectionProvider를, listenr가 되기 위해서는 ISelectionListener를 구현한다.
(eclipse의 selection event mechanism은 여기를 참조)


ISelectionListener vs. ISelectionChangedListener

일반적으로 view나 editor가 event를 청취하려면 ISelectionListener를 구현하고, event provider에게 등록한다.
구현할 method는 public void selectionChanged(IWorkbenchPart part, ISelection selection) 이다.

그럼 ISelectionChangedListener는 뭘까?
view를 만들 때 PageBookView를 상속하면, active되는 part가 변경될 때, view 안에 page만 갈아끼워서 자원의 낭비를 줄이는 이점이 있다.  이는 part가 activated될 때, view에서 상응하는 page를 보여줄 것인지를 물어보는 식으로 작동한다.

ex)
protected boolean isImportant(IWorkbenchPart part) {
return part instanceof IEditorPart;
}

실제 보여질 UI(SWT control)와 내용은 page가 만들게 된다.
page는 Page를 상속받고 createControl(Composite parent) 안에서 UI와 내용을 엮게 된다. 
(ContentProvider와 LabelProvider를 알 필요가 있다.)
사실, eclipse에서 사용하는 UI의 패턴이 몇 개 되지 않고(
TreeViewer, TableViewer, TableTreeViewer 등
), 대부분 jface에서 제공하는 것을 사용하게 된다.

이 Viewer들은 ISelectionProvider를 구현하고 있어, view에서 발생하는 selection event에 대한 provider역할도 해 준다.

page에서는 Viewer.addSelectionChangedListener(this)를 호출하여 
page를 event의 청취자로 등록할 수 있다. 
다만, 이 때 page는 ISelectionChangedListener을 구현한다.

ex)
public void selectionChanged(SelectionChangedEvent e) {
...
}

ISelectionChangedListener는 page내의 widget에서 발생한 event를 page가 받아서 handling하기 위해 쓰인다. 인자가 event 하나 밖에 없으므로 widget이 여러 개 있을 때는 event.getSource()를 통해 event를 발생시킨 widget이 어디인지 확인한다.


cf) Viewer들은 ISelectionProvider를 구현하고 있다. 그러므로,
public void createControl(Composite parent) {
   ...
   getSite().setSelectionProvider(treeViewer);
   ...
 }
와 같이 eclipse Selection mechanism에 provider로 등록할 수 있다.
이 때는  
     같은 Site를 공유하면서 
ISelectionListener를 구현하는 
모든 view, editor, page등에게 
event가 전파된다.

ISelectionListener를 구현한 청취자의 경우를 보자.
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
     ...
}
와 같이 event handler를 구현할 때,
인자로 part를 받으므로 어떤 Part(viewpart or editorpart)에서 온 것인지 확인할 수 있고,
selection을 통해, 어떤 widget이 일으킨 event인지 알 수도 있다.



DOMInspectorView를 예로 들어, 
atf에서 처리한 기법을 보자.
DOMInspectorView는 BrowserBoundView를 상속받는다. BrowserBoundView의 doCreatePage(part)를 보자.
protected PageRec doCreatePage(IWorkbenchPart part) {
...
return new PageRec(part, page);
}
doCreatePage는 PageBookView에서 정의하였고, 
doCreatePage를 통해 PageRec 객체를 얻는다.

PageBookView는 pageRec객체에서 page를 얻고, page에서 site를 얻어내고, site로부터 다시 selectionProvider를 얻는다.

slectionProvider.addSelectionChangedListener( PageBookView_Inner_ISelectionChanedLister )

selectionProvider로 page객체가 등록되었으면 (또는 page 내의 widget VIewer들)
page객체의 addSelectionChangedListener가 호출되고, PageBookView를 상속한 View part가 청취자로 등록되는 것이다. 
PageBookView 안에는 ISelectionChangedLister를 구현하는 anonymous class가 존재한다.


DOMInspectorPage에서는 selectionChanged 때마다 청취자 list를 돌면서 아래와 같이 selectionChanged(event)를 호출해 준다.

protected void fireSelectionChanged(final SelectionChangedEvent event) {
Object[] listeners = selectionListeners.getListeners();
for (int i = 0; i < listeners.length; ++i) {
final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i];
SafeRunnable.run(new SafeRunnable() {
public void run() {
l.selectionChanged(event);
}
});
}
}

위와 같이 호출할 때, eclipse Selection mechanism에 의해 SelectionChangedEvent(part, selection) event를 일으켜 준다. (AbstractTreeViewer참조)

결국, ISelectListener를 구현한 part( Editor, View 등)에서도 selectionChanged(part, selection) 함수가 호출된다. part를 출력해 보면 page(DOMInspectorPage)가 들어있는 ViewPart가 나온다.

event handler를 상속받아 구현하지 않고 직접 호출할 때에는 event 객체를 직접 만들어야 하는데, 이 때는 event를 제어할 수 있는 이점이 있다.

protected IDOMNodeSelection selection = new IDOMNodeSelection() {
...
}
fireSelectionChanged(new SelectionChangedEvent(this, this.selection));
와 같이 selection event를 직접 만들고, 

Class MyEditor implements ISelectionListener{

  public void selectionChanged(IWorkbenchPart part, ISelection selection) {
     ...
     if (selection instanceof IDOMNodeSelection) {
     ...
     }
  }

}

event handler에서 event type에 따라 작동을 제어할 수 있다.