Reputation: 4185
Assume the following simplified EMF model structure:
Graph
/ \
Node Edge
In my GEF editor, the EditPart
s are organized as follows.
GraphEditPart (Figure = FreeformLayer) excerpt
@Override
protected List<EObject> getModelChildren() {
List<EObject> childrenList = new ArrayList<EObject>();
Graph graph = (Graph) getModel();
childrenList.addAll(graph.getNodes());
return childrenList;
}
NodeEditPart (Figure = extending Figure
) excerpt (also showing how Edges
for which a Node
is source or target are got)
@Override
protected List<Edge> getModelSourceConnections() {
Node node = (Node) getModel();
Graph graph = node.getGraph();
String nodeId = node.getId();
List<Edge> sourceList = new ArrayList<Edge>();
if (graph != null)
sourceList.addAll(graph.getOutEdges(nodeId));
return sourceList;
}
@Override
protected List<Edge> getModelTargetConnections() {
// Same principle as getModelSourceConnections
}
Editor class excerpt (in case it matters)
@Override
protected void initializeGraphicalViewer() {
super.initializeGraphicalViewer();
GraphicalViewer viewer = getGraphicalViewer();
viewer.setContents(graph);
ScalableFreeformRootEditPart root = (ScalableFreeformRootEditPart) viewer.getRootEditPart();
ConnectionLayer connLayer = (ConnectionLayer) root.getLayer(LayerConstants.CONNECTION_LAYER);
GraphicalEditPart contentEditPart = (GraphicalEditPart) root.getContents();
ShortestPathConnectionRouter shortestPathConnectionRouter = new ShortestPathConnectionRouter(contentEditPart.getFigure());
connLayer.setConnectionRouter(shortestPathConnectionRouter);
}
All EditPart
s have their own adapter (extending org.eclipse.emf.ecore.util.EContentAdapter
or implementing org.eclipse.emf.common.notify.Adapter
).
This results in an EditPart
structure, where NodeEditPart
s are children of the GraphEditPart
, and EdgeEditPart
s are orphans, i.e., they have no parent
. Consequentially, I've had difficulties refreshing figures whenever Edge
s got added or deleted.
I've managed to make the update work when I added an Edge
by doing an expensive iteration in the GraphAdapter
(which is notified as a newly created Edge
must be registered on the Graph
(newEdge.setGraph(graph)
):
if (notification.getOldValue() == null && notification.getNewValue() instanceof Edge) {
for (Object ep : getChildren()) {
if (ep instanceof NodeEditPart) { // There are in fact other EditParts as well
Graph graph = (Graph) getModel();
NodeEditPart nep = (NodeEditPart) ep;
Node n = (Node) nep.getModel();
if (graph.getOutEdges(n.getSId()).contains(notification.getNewValue()) || graph.getInEdges(n.getSId()).contains(notification.getNewValue()))
nep.refresh();
}
[Note: If - by the way - you can think of any better way of doing this, feel free to hit me up with your solution!]
I am unable to reliably remove the Edge
figure from the editor when deleting the Edge
model object! Sometimes it works, sometimes it doesn't.
I guess the unreliability might have to do with the fact that (a) my real life model has three layers of abstraction, and (b) that different EMF Adapter
s don't always recognize changes in the same temporal order (?).
execute()
from EdgeDeleteCommand
simply calls edge.setGraph(null)
, which triggers EMF to clean up after itself (i.e., model elements that aren't connected to the graph are removed from the model).
How can I reliably remove Edge
s' figures when deleting the respective model object, when the respective EditPart
is an orphan?
Upvotes: 1
Views: 342
Reputation: 4185
Simply let the source and target model objects know that something about them has changed, and let their Adapter
handle the refresh. Assume the following EdgeDeleteCommand
's execute()
method.
@Override
public void execute() {
graph = edge.getGraph();
source = edge.getSource();
target = edge.getTarget();
edge.setGraph(null);
source.eNotify(new NotificationImpl(Notification.REMOVE, edge, null));
target.eNotify(new NotificationImpl(Notification.REMOVE, edge, null));
}
The last two lines of code notify the source and target model elements that the object edge
has been removed "from them". This is in fact a fake notification message, as edge
was never a child of source
or target
. However, the Adapter
s attached to the the source and target NodeEditPart
will register this, and can react as follows.
@Override
public void notifyChanged(Notification notification) {
switch (notification.getEventType()) {
case Notification.REMOVE:
refresh();
break;
default:
break;
}
}
The NodeEditPart.refresh()
method will call getModelSourceConnections()
and getModelTargetConnections()
"on its way", which will return a list of Edge
s each, with the deleted edge
not in them anymore.
Upvotes: 1