一般来说我们只需要在GraphicalViewer部分显示在Property页即可。
要支持GraphicalViewer和PaletteViewer都能对属性页有支持,就有点复杂。
每个Site只能有一个SelectionProvider,正是这个SelectionProvider里选中的对象才有可能显示在属性页。在GEF中,情况下,GraphicalViewer被设置为这个EditorSite的SelectionProvider。因为只能有一个SelectionProvider,因此我们不能同时设置PaletteViewer为SelectionProvider。
所以,首先我们就需要自定义一个SelectionProvider,它聚合这两部分的Provider,并且在这两者之间切换,如下:
public class CustomSelectionProvider implements ISelectionProvider, FocusListener { private GraphicalViewer graphicalViewer; private PaletteViewer paletteViewer; private Widget focusControl; public CustomSelectionProvider(GraphicalViewer graphicalViewer, PaletteViewer paletteViewer) { super(); this.graphicalViewer = graphicalViewer; this.paletteViewer = paletteViewer; this.graphicalViewer.getControl().addFocusListener(this); this.paletteViewer.getControl().addFocusListener(this); } public void addSelectionChangedListener(ISelectionChangedListener listener) { graphicalViewer.addSelectionChangedListener(listener); paletteViewer.addSelectionChangedListener(listener); } public ISelection getSelection() { if (focusControl == graphicalViewer.getControl()) return graphicalViewer.getSelection(); else if (focusControl == paletteViewer.getControl()) { return paletteViewer.getSelection(); } return null; } public void removeSelectionChangedListener( ISelectionChangedListener listener) { graphicalViewer.removeSelectionChangedListener(listener); paletteViewer.removeSelectionChangedListener(listener); } public void setSelection(ISelection selection) { if (focusControl == graphicalViewer.getControl()) graphicalViewer.setSelection(selection); else if (focusControl == paletteViewer.getControl()) paletteViewer.setSelection(selection); } public void focusGained(FocusEvent e) { focusControl = e.widget; if (paletteViewer.getControl() == focusControl) { ISelection selection = paletteViewer.getSelection(); setSelection(null); setSelection(selection); } else if (graphicalViewer.getControl() == focusControl) { ISelection selection = graphicalViewer.getSelection(); setSelection(null); setSelection(selection); } } public void focusLost(FocusEvent e) { } }
有了这个自定义的SelectionProvider,我们就可以用它来替换Editor中缺省的GraphicalViewer,重写Editor的createPartControl()方法:
@Override public void createPartControl(Composite parent) { super.createPartControl(parent); PaletteViewer paletteViewer = ((CustomPaletteViewerProvider) getPaletteViewerProvider()) .getPaletteViewer(); CustomSelectionProvider selectionProvider = new CustomSelectionProvider( getGraphicalViewer(), paletteViewer); getSite().setSelectionProvider(selectionProvider); }
这里的CustomPaletteViewerProvider也是一个自定义的PaletterViewerProvider,因为缺省的PaletterViewerProvider没有提供方法返回对象的PaletteViewer。自定义一个也很简单,增加一个返回PaletteViewer的方法即可:
public class CustomPaletteViewerProvider extends PaletteViewerProvider { private PaletteViewer paletteViewer; public CustomPaletteViewerProvider(EditDomain graphicalViewerDomain) { super(graphicalViewerDomain); } @Override public PaletteViewer createPaletteViewer(Composite parent) { paletteViewer = super.createPaletteViewer(parent); return paletteViewer; } public PaletteViewer getPaletteViewer() { return paletteViewer; } }
然后用这个PaletteViewerProvider替换缺省的,重写Editor中的方法:
@Override protected PaletteViewerProvider createPaletteViewerProvider() { return new CustomPaletteViewerProvider(getEditDomain()); }
这样,一个可以左右切换的SelectionProvider就准备好了,它可以同时支持Palette和Graphical两个部分。
最后一个步骤就是怎么让Property View感知他们。每次一个Site上的SelectionProvider选项发生变动的时候,Property View都会被通知,去检查当前选中对象是否支持在Property页上显示。一个能够在Property页上显示的对象有两种实现方式:
1.对象实现IPropertySource接口
2.对象实现IAdaptable接口,在方法getAdater(Class)里,实现IPropertySource.class的支持。
如果用第一种方法,那显示我们需要重写所有的Palette部分,那比较复杂。因此我们选择第二种方法。
对于第二种方式,如果在每种节点类型的getAdapter(Class)方法里追加IPropertySource的实现,那最终结果和使用方法一一样,得把整个Palette都重写一次。
对于方法2,我们还有第二个选项,扩展“org.eclipse.core.runtime.adapters”扩展点。
所有的Palette上的对象,也都是一个AbstractEditPart,这个对象的getAdapter(Class)的实现是:如果没有找着匹配的实现,则最后会查找Eclipse里所有已经注册的adapter对。例如:
public Object getAdapter(Class key) { if (AccessibleEditPart.class == key) return getAccessibleEditPart(); return Platform.getAdapterManager().getAdapter(this, key); }
因此我们可以通过扩展“org.eclipse.core.runtime.adapters”扩展点来达到目的,可以如下扩展:
<extension point="org.eclipse.core.runtime.adapters"> <factory adaptableType="org.eclipse.gef.ui.palette.editparts.PaletteEditPart" class="。。。.PaletteAdapterFactory"> <adapter type="org.eclipse.ui.views.properties.IPropertySourceProvider"> </adapter> </factory> </extension>
其中adaptableType表示对哪种类型的结点应用,adapter里的type表示这个adapter应该返回一个什么类型的对象,class就是具体的实现类。例如一个简单的实现如下:
public class PaletteAdapterFactory implements IAdapterFactory { public Object getAdapter(Object adaptableObject, Class adapterType) { return new CustomPropertySourceProvider( (PaletteEditPart) adaptableObject); } public Class<PaletteEditPart>[] getAdapterList() { return new Class[] { PaletteEditPart.class }; } } class CustomPropertySourceProvider implements IPropertySourceProvider { private PaletteEditPart editPart; public CustomPropertySourceProvider(PaletteEditPart editPart) { this.editPart = editPart; } public IPropertySource getPropertySource(Object object) { return new CustomPropertySource(editPart); } } class CustomPropertySource implements IPropertySource { private PaletteEditPart editPart; public CustomPropertySource(PaletteEditPart editPart) { this.editPart = editPart; } public Object getEditableValue() { return null; } public IPropertyDescriptor[] getPropertyDescriptors() { IPropertyDescriptor[] descriptors = new IPropertyDescriptor[1]; descriptors[0] = new TextPropertyDescriptor("TEXT", "ToString"); return descriptors; } public Object getPropertyValue(Object id) { return editPart.toString(); } public boolean isPropertySet(Object id) { return true; } public void resetPropertyValue(Object id) { } public void setPropertyValue(Object id, Object value) { } }
这个实现的效果就是,每次选中Palette中的一个结点,则把它的toString()结果显示在Property上。