Published by Arto Jarvinen on 06 Jun 2013
Understanding the outline view
I’ve been away from this project for a while travelling to customers in Asia (that’s where many customers are these days). This means that it will take me many hours to try to remember what I did the last time. Posts like this one helps me remember where I left the project the last time.
This post describes my first cut on understanding the outline view of a generated (slightly modified) GMF editor. I try to describe what happens when an item is selected in the outline view. The story is not complete as of now as there is a lot going on under the hood when one clicks on an item. I will probably come back to this post and amend it later.
The outline view typically shows to the left of the diagram editors in the Eclipse instance. If not, it can be displayed using the Window -> Show View menu command. It shows the contents of the active diagram in two alternative formats: as an outline (tree) format and as a minimized overview image of the current diagram.
The outline page (DiagramOutlinePage) that contains both the (tree) outline and the (diagram) overview extends ContentOutlinePage. This class contains the edit part viewer of the type TreeViewer. The TreeViewer in turn refers to the GUI tree widget of the type org.eclipse.swt.widgets.Tree which contains TreeItems. The TreeItems are subclasses of Widgets. Widgets can refer to arbitrary application objects. In our case each TreeItem refers to a TreeEditPart.
The TreeViewer edit part viewer listens to selections in the Tree. (The diagram overview does not have any associated edit part viewer and is thus just a “dumb” view.)
The outline view with its TreeViewer (edit part viewer) is activated when a diagram is activated. At that time my ProcessmodelDiagramEditor is adapted with (my version of) a DiagramOutlinePage.
The following code within ProcessmodelDiagramEditor.getAdapter() creates the outline page with an included tree viewer:
else if (type == IContentOutlinePage.class) {
TreeViewer viewer = new TreeViewer(); // Also creates a default root edit part
viewer.setRootEditPart(new DiagramRootTreeEditPart()); // Replace default root edit part
return new DiagramOutlinePage(viewer);
}
DiagramOutlinePage is in my modified code defined in my ProcessmodelDiagramEditor and thus overrides the default implementation in DiagramEditor.
The DiagramRootTreeEditPart modifies the default root edit part by also showing a widget representing the diagram which in turn represents a domain element in the model (in my case a Process).
The TreeViewer is populated by edit parts created by my own version of an EditPartFactory. Note that these edit parts are particular to the TreeViewer. The diagram editors contain other types of edit parts.
When a tree item is selected in the outline viewer, the widgetSelected() method of a SelectionListener is called (the SelectionListener is created and its widgetSelected method is defined in the TreeViewer.hookControl() method). The widgetSelected looks like follows:
public void widgetSelected(SelectionEvent e) {
TreeItem[] ties = tree.getSelection();
Object newSelection[] = new Object[ties.length];
for (int i = 0; i < ties.length; i++)
newSelection[i] = ties[i].getData();
ignore = true;
setSelection(new StructuredSelection(newSelection));
ignore = false;
}
The call to setSelection() calls another setSelection(), that of the tree viewer's SelectionManager. The SelectionManager holds a list of selected edit parts and this list is set by the setSelection() method. The SelectionManager.setSelection() adds the selected edit parts to its selection attribute and also marks the edit parts itself as selected.
After that it (indirectly) calls the AbstractEditPartViewer.fireSelectionChanged() method which tells any class which cares to listen that the selection has changed.
The two classes that listen are a SelectionSynchronizer and the OutlineView which contains the tree viewer (the OutlineView is an Eclipse view just like the PropertySheet).
The SelectionSynchronizer.syncSelection() iterates over all edit part viewers, in my case the TreeViewer and a DiagramGraphicalViewer, and sets the selection of each to the actual selection calling each viewer's setViewerSelection() like this:
private void setViewerSelection(EditPartViewer viewer, ISelection selection) {
ArrayList result = new ArrayList();
Iterator iter = ((IStructuredSelection) selection).iterator();
while (iter.hasNext()) {
EditPart part = convert(viewer, (EditPart) iter.next());
if (part != null)
result.add(part);
}
viewer.setSelection(new StructuredSelection(result));
if (result.size() > 0)
viewer.reveal((EditPart) result.get(result.size() - 1));
}
A key method here is convert() that finds corresponding edit parts in different viewers:
protected EditPart convert(EditPartViewer viewer, EditPart part) {
Object temp = viewer.getEditPartRegistry().get(part.getModel());
EditPart newPart = null;
if (temp != null) {
newPart = (EditPart) temp;
}
return newPart;
}
Having found the corresponding edit part in the other (to be synchronized) edit part viewer the setSelection() of the viewer is called (see above). It calls SelectionManager.setSelection() which first iterates over all edit parts in the current selection and removes any that are not in the new selection. It then adds any newly selected edit parts to the current selection. Each edit part is also marked as selected or not by calling its setSelected() with the proper argument (selection status).
The setSelected() of the edit part sets the value of the selected attribute and then calls its fireSelectionChanged(). fireSelectionChanged() calls the selectedStateChanged() on the set of listeners which are all edit policies. (This only happens for edit parts in the DiagramGraphicalViewer.) The SelectionEditPolicy adds "selection handles" to the edit part as part of high-lighting the selected edit part(s). I here conveniently ignore how the other edit policies do or don't contribute to the selection process.
When the selection manager has iterated over all selected edit parts it fires its fireSelectionChanged() in a never-ending chain reaction. This call eventually ends upp in DiagramGraphicalViewer.fireSelectionChanged() which calls (asynchronously) flushSelectionEvents (when single-stepping, this method never gets called). flushSelectionEvents tells each of a number of listeners that the selection has changed. Among these listeners are yet another SelectionSynchronizer but also the DeleteElementActions that are prebuilt (the tree viewers context menu delete command is built when the context menu is shown). The SelectionSynchronizer synchronizes (back) the tree viewer with the diagram edit part viewer. This seems a bit superfluous since the diagram edit part viewer was just a bit earlier synchronized with the tree edit part viewer. No harm done I guess as long as the avalanche of fireSelectionChanged() doesn't end up in a loop. The DeleteElementActions are rebuilt to target the newly selected edit parts and the corresponding commands are enabled.
The thread that redraws the editor pane doesn't seem to get reached at all when debugging the above sequence of events. It's only when one hits F8 (Resume) when the editor pane is refreshed and one can see the actual changes. This confused me for a while as I thought I saw where the selection was changed but nothing happened.


