Getting your Xtext model to the Properties View
The default eclipse properties view processes structured selections, but in a text editor you usually get a TextSelection. One way of still getting information displayed is to push a customised structured selection to the view. In order to do that, we adapt code from Xtext’s outline view implementation for registering a selection listener that will trigger the update whenever the text selection changes. I am well aware that I am presenting a very crude implementation… it is just a proof of concept.
The first step is extending the XtextEditor, creating and using an additional Listener, …
public class MyXtextEditor extends XtextEditor {
private MySelectionChangeListener mylistener;
@Override
public void createPartControl(Composite parent) {
super.createPartControl(parent);
mylistener = new MySelectionChangeListener(this);
mylistener.install(getSelectionProvider());
}
@Override
public void dispose() {
mylistener.uninstall(getSelectionProvider());
super.dispose();
}
}
…and binding it in the UiModule.
public Class<? extends XtextEditor> bindEditor() {
return MyXtextEditor.class;
}
Next, we make sure that the Listener triggers the information of the properties view.
public class MySelectionChangeListener implements ISelectionChangedListener {
private XtextEditor editor;
public MySelectionChangeListener(XtextEditor editor) {
this.editor=editor;
}
public void selectionChanged(SelectionChangedEvent event) {
try {
ISelection selection = event.getSelection();
if (!selection.isEmpty() && selection instanceof ITextSelection) {
final ITextSelection textSel = (ITextSelection) selection;
//determine the Model element at the offset and invoke the
//invoke the information of the properties view
editor.getDocument().readOnly(new IUnitOfWork.Void<XtextResource>() {
public void process(XtextResource resource) throws Exception {
IParseResult parseResult = resource.getParseResult();
if(parseResult==null)return;
CompositeNode rootNode = parseResult.getRootNode();
int offset = textSel.getOffset();
AbstractNode node =
ParseTreeUtil.getCurrentOrFollowingNodeByOffset(rootNode, offset);
EObject object=NodeUtil.getNearestSemanticObject(node);
PropertyPageInformer.informPropertyView(editor, object);
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
//install and uninstall is basically copied form
//AbstractSelectionChangedListener
public void install(ISelectionProvider selectionProvider) {
if (selectionProvider == null)
return;
if (selectionProvider instanceof IPostSelectionProvider) {
IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider;
provider.addPostSelectionChangedListener(this);
}
else {
selectionProvider.addSelectionChangedListener(this);
}
}
public void uninstall(ISelectionProvider selectionProvider) {
if (selectionProvider == null)
return;
if (selectionProvider instanceof IPostSelectionProvider) {
IPostSelectionProvider provider = (IPostSelectionProvider) selectionProvider;
provider.removePostSelectionChangedListener(this);
}
else {
selectionProvider.removeSelectionChangedListener(this);
}
}
}
The PropertyPageInformer is responsible for sending our customised structured selection to the properties view.
public class PropertyPageInformer {
public static void informPropertyView(XtextEditor editor, EObject object) {
PropertySheet propertyView=null;
//fetch the properties view
try{
propertyView = (PropertySheet)PlatformUI.getWorkbench().
getActiveWorkbenchWindow().getActivePage().
findView(IPageLayout.ID_PROP_SHEET);
} catch (Exception e) {
}
if(propertyView!=null){
//make sure our editor is marked as active part
//otherwise the selectionChanged-call will be ignored
propertyView.partActivated(editor);
//feed the view with our custom selection
propertyView.selectionChanged(editor, constructSelection(object));
}
}
private static ISelection constructSelection(final EObject object) {
return new StructuredSelection(){
public Object[] toArray() {
return new Object[]{new MyPropertyInfo(object)};
}
};
}
}
MyPropertyInfo finally contains the information that can be processed by the view.
class MyPropertyInfo implements IAdaptable, IPropertySource{
List<String> ids=new ArrayList<String>();
Map<String, String> idToLabelMap=new HashMap<String, String>();
Map<String, String> idToCategoryMap=new HashMap<String, String>();
Map<String, String> idToValueMap=new HashMap<String, String>();
public MyPropertyInfo(EObject object) {
String id="type";
ids.add(id);
idToCategoryMap.put(id, "ObjectInfo");
idToLabelMap.put(id, "type of the current object");
idToValueMap.put(id, object.getClass().getSimpleName());
id="someID";
ids.add(id);
idToLabelMap.put(id, "some String");
idToValueMap.put(id, ""+this.hashCode());
}
@SuppressWarnings("rawtypes")
public Object getAdapter(Class adapter) {
if(adapter.equals(IPropertySource.class)){
return this;
}
return null;
}
public boolean isPropertySet(Object id) {
return false;
}
public Object getPropertyValue(Object id) {
return idToValueMap.get(id);
}
public IPropertyDescriptor[] getPropertyDescriptors() {
IPropertyDescriptor[] descs=new IPropertyDescriptor[ids.size()];
for (int i=0;i<ids.size();i++) {
final String id=ids.get(i);
descs[i]=new PropertyDescriptor(id, idToLabelMap.get(id)){
@Override
public String getCategory() {
return idToCategoryMap.get(id);
};
};
}
return descs;
}
public Object getEditableValue() {
return null;
}
public void setPropertyValue(Object id, Object value) {}
public void resetPropertyValue(Object id) {}
}