Creating a GEF Editor – Part 10: Deleting Connections and Fixing of Thing Delete Command

Previous Tutorial: Creating a GEF Editor – Part 9: Connections

This tutorial continues the development of our OPM editor, adding a rarely useful functionality: deleting links (only used by dumb people like me who never get things right the first time :-)). Furthermore, we must fix the Command used to delete things, since it does not deal with links at all (try deleting a thing that has a connecting link). Let’s get to work.

  1. The code to delete an OPMLink is fairly similar to the code used to delete an OPMThing only that a ConnectionEditPart uses a EditPolicy.CONNECTION_ROLE and a ConnectionEditPolicy to handle delete requests, instead of the EditPolicy.COMPONENT_ROLE and ComponentEditPolicy used by the OPMThingEditPart (Reading the code again just now, the javadoc of COMPONENT_ROLE states that this role can be used for any component of the graph, so maybe this would also work for a link. Added a TODO mark in the code to check this later). As usual, we’ll first code the Command, then the EditPolicy and finally change the EditPart so that the code always compiles cleanly
  2. The command to delete a link is pretty simple, just store all the information needed to undo the Command and detach the link from source, target and owner OPD:
    package com.vainolo.phd.opm.gef.editor.command;
    
    import org.eclipse.gef.commands.Command;
    
    import com.vainolo.phd.opm.model.OPMObjectProcessDiagram;
    import com.vainolo.phd.opm.model.OPMLink;
    import com.vainolo.phd.opm.model.OPMThing;
    
    /**
     * Command used to delete a link.
     * @author vainolo
     */
    public class OPMLinkDeleteCommand extends Command {
        /** Link to be deleted. */
        private OPMLink link;
        /** OPD that owns the link. */
        private OPMObjectProcessDiagram opd;
        /** Source of the link. */
        private OPMThing source;
        /** Target of the link. */
        private OPMThing target;
    
        /**
         * {@inheritDoc}
         */
        @Override public boolean canExecute() {
            return link != null;
        }
    
        /**
         * Disconnect link from source and target things and remove
         * from owner OPD.
         */
        @Override public void execute() {
            opd = link.getOpd();
            source = link.getSource();
            target = link.getTarget();
    
            link.setSource(null);
            link.setTarget(null);
            link.setOpd(null);
        }
    
        /**
         * Reconnect the link to the source and target and add
         * it to the owner OPD.
         */
        @Override public void undo() {
            link.setSource(source);
            link.setTarget(target);
            link.setOpd(opd);
        }
    
        /**
         * Set the link that will be delete from the diagram.
         * @param linkParam the link to delete from the diagram.
         */
        public void setLink(final OPMLink linkParam) {
            link = linkParam;
        }
    }
    
  3. The ConnectionEditPolicy is also very straightforward, creating and initializing the Command:
    package com.vainolo.phd.opm.gef.editor.policy;
    
    import org.eclipse.gef.editpolicies.ConnectionEditPolicy;
    import org.eclipse.gef.requests.GroupRequest;
    
    import com.vainolo.phd.opm.gef.editor.command.OPMLinkDeleteCommand;
    import com.vainolo.phd.opm.model.OPMLink;
    
    /**
     * Edit policy used by the OPMLink class to server delete requests.
     * @author vainolo
     *
     */
    public class OPMLinkConnectionEditPolicy extends ConnectionEditPolicy {
    
    	/**
    	 * Create a {@link OPMLinkDeleteCommand} and fill its details.
    	 * @param request the request that requires treatment.
    	 * @return a {@link OPMLinkDeleteCommand} that deletes a link from the model.
    	 */
    	@Override protected OPMLinkDeleteCommand getDeleteCommand(GroupRequest request) {
    		OPMLinkDeleteCommand command = new OPMLinkDeleteCommand();
    		command.setLink((OPMLink) getHost().getModel());
    		return command;
    	}
    }
    
  4. And to close things up, we install the new EditPolicy in the OPMLinkEditPart:
    package com.vainolo.phd.opm.gef.editor.part;
    
    import org.eclipse.draw2d.IFigure;
    import org.eclipse.draw2d.PolylineConnection;
    import org.eclipse.gef.EditPolicy;
    import org.eclipse.gef.editparts.AbstractConnectionEditPart;
    import org.eclipse.gef.editpolicies.ConnectionEndpointEditPolicy;
    
    import com.vainolo.phd.opm.gef.editor.policy.OPMLinkConnectionEditPolicy;
    
    /**
     * {@link EditPart} for the {@link OPMLink} model element.
     * @author vainolo
     */
    public class OPMLinkEditPart extends AbstractConnectionEditPart {
    
    	/**
    	 * Create and initialize a new {@link OPMLinkEditPart}.
    	 */
    	public OPMLinkEditPart() {
    		super();
    	}
    	
    	/**
    	 * Installs two edit policies:
    	 * <ol>
    	 *   <li>For the {@link EditPolicy#CONNECTION_ENDPOINTS_ROLE} a {@link ConnectionEndpoinEditPolicy}.</li>
    	 *   <li>For the {@link EditPolicy#CONNECTION_ROLE} a {@link OPMLinkConnectionEditPolicy}</li>
    	 */
    	@Override protected void createEditPolicies() {
    		installEditPolicy(EditPolicy.CONNECTION_ENDPOINTS_ROLE, new ConnectionEndpointEditPolicy());
    		installEditPolicy(EditPolicy.CONNECTION_ROLE, new OPMLinkConnectionEditPolicy());		
    	}
    
    	@Override protected IFigure createFigure() {
    		PolylineConnection conn = new PolylineConnection();
    		return conn; 
    	}
    }
    
  5. That was all that we needed to delete the link from the diagram. Go ahead and check your results, there is nothing more pleasurable than this.
  6. Now we have to fix the Command that we used to delete OPMThing instances, so that it also removes the incoming and outgoing links from the graph. This would be very very simple if we were not interested in an undoable command, but we are interested, therefore it gets a bit more complicated. What we have to do is store all of the links that we deleted in the Command, also storing their source and target OPMThing. The undo method of the Command restores these connections to their original state (Note also that since the OPMLink is contained in the OPMObjectProcessDiagram we must also detach it on execute and reattach it on undo).
    package com.vainolo.phd.opm.gef.editor.command;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import org.eclipse.gef.commands.Command;
    
    import com.vainolo.phd.opm.model.OPMLink;
    import com.vainolo.phd.opm.model.OPMObjectProcessDiagram;
    import com.vainolo.phd.opm.model.OPMThing;
    
    /**
     * Command used to delete a thing.
     * The functionality of this class fairly closed so it is declared final. 
     * @author vainolo
     *
     */
    public final class OPMThingDeleteCommand extends Command {
    	
        /** Thing to be deleted. */
    	private OPMThing thing;
    	/** OPD that owns the thing. */
    	private OPMObjectProcessDiagram opd;
    	/** Incoming and outgoing links. */
    	private List<OPMLink> links;
    	/** Sources for the links that start or end at this thing. */
    	private Map<OPMLink, OPMThing> linkSources;
    	/** Targets for the links that start or end at this thing. */ 
    	private Map<OPMLink, OPMThing> linkTargets;
    
    	@Override
    	public void execute() {
    		detachLinks();
    		thing.setOpd(null);
    	}
    
    	@Override
    	public void undo() {
    		reattachLinks();
    		thing.setOpd(opd);
    	}
    
    	/**
    	 * Detach all links from the thing and from the other
    	 * connecting thing, storing the connection information in local
    	 * data structures.
    	 */
    	private void detachLinks() {
    		links = new ArrayList<OPMLink>();
    		linkSources = new HashMap<OPMLink, OPMThing>();
    		linkTargets = new HashMap<OPMLink, OPMThing>();
    		links.addAll(thing.getIncomingLinks());
    		links.addAll(thing.getOutgoingLinks());
    		for (OPMLink link : links) {
    			linkSources.put(link, link.getSource());
    			linkTargets.put(link, link.getTarget());
    			link.setSource(null);
    			link.setTarget(null);
    			link.setOpd(null);
    		}
    	}
    
    	/**
    	 * Reattach all links to their source and target things.
    	 */
    	private void reattachLinks() {
    		for (OPMLink link : links) {
    			link.setSource(linkSources.get(link));
    			link.setTarget(linkTargets.get(link));
    			link.setOpd(opd);
    		}
    	}
    	
    	/**
    	 * Set the thing to delete from the diagram.
    	 * @param thing the Thing to delete from the diagram.
    	 */
    	public void setThing(OPMThing thing) {
    		this.thing = thing;
    		this.opd = thing.getOpd();
    	}
    }
    
  7. That should close all things. You now have a fully operational GEF editor!

You can find the final project files here.

GEF rocks!!!

Next Tutorial: Creating a GEF Editor – Part 11: Creating Link Bendpoints

4 thoughts on “Creating a GEF Editor – Part 10: Deleting Connections and Fixing of Thing Delete Command

  1. First thanks for this really helpfull tutorial!!! it’s helping me really a lot!

    I’m not an expert of GEF, i’ve worked more with GMF but if you appreciate it I’ve got a suggestion for you. This is only a personal opinion to solve the problem. Maybe you already now it, but i prefer it becouse it leave DeleteThingCommands indipendent, without involving it in the link deletion (That is done by another command).

    The idea is to modifyed the “OPMThingComponentEditPolicy.createDeleteCommand()” instead of the “DeleteThingCommands” so it returns a “CompoundCommand” containing a list of Commands to first delete all the links involved and after the node.
    This is my code. Hope it’s helpfully :-)


    package com.vainolo.phd.opm.gef.editor.policy;

    import java.util.HashSet;
    import java.util.Map;
    import java.util.Set;
    import org.eclipse.gef.commands.Command;
    import org.eclipse.gef.commands.CompoundCommand;
    import org.eclipse.gef.editpolicies.ComponentEditPolicy;
    import org.eclipse.gef.requests.GroupRequest;
    import com.vainolo.phd.opm.gef.editor.command.OPMThingDeleteCommand;
    import com.vainolo.phd.opm.gef.editor.part.OPMLinkEditPart;
    import com.vainolo.phd.opm.model.OPMLink;
    import com.vainolo.phd.opm.model.OPMThing;

    public class OPMThingComponentEditPolicy extends ComponentEditPolicy {

    @Override protected Command createDeleteCommand(GroupRequest deleteRequest) {

    //Create a empty CompuondCommand where we are going to put the commands to delete incoming/outgoing link and the elements itself;
    CompoundCommand resultC=new CompoundCommand();

    //Create a Set of all link that need to be deleted
    Set s=new HashSet(((OPMThing) getHost().getModel()).getIncomingLinks());
    s.addAll(((OPMThing) getHost().getModel()).getOutgoingLinks());

    // the "getEditPartRegistry()" is a map that save the EditPartReference to the Model Elements
    Map editPartMap=getHost().getViewer().getEditPartRegistry();
    for (OPMLink l:s){
    //Retrive each link editPart and ask him for the "DeleteCommand"
    Command c=((OPMLinkEditPart)editPartMap.get(l)).getCommand(deleteRequest);

    //Add the DeleteLinkComamnd to the CompuoundCommand
    resultC.add(c);
    }

    //Create the OPMThingDeleteCommand to delete the element;
    OPMThingDeleteCommand thingDeleteCommand = new OPMThingDeleteCommand();
    thingDeleteCommand.setThing((OPMThing) getHost().getModel());

    //Finally add the Command to delete the element
    resultC.add(thingDeleteCommand);

    //Notice that the commands will be executed from the first that we addded to the last.
    //When undoing the command this will be undo form last to first.
    //Consistency is always maintained.

    return resultC;
    }
    }

    • Nice idea! never thought of doing it that way. Although I think that in the code you should create a new request and not pass the same request, or am I wrong?
      I have tried learning GMF a number of times but have failed miserably. I cannot understand how the giant framework works and this kills my enthusiasm. Do you know a good source where I can learn how GMF works? All I have found are tutorials that show how to do things but don’t explain why.
      Thanks!

      • I’ve learned gmf only becouse my actual works require it. I’m learning it, trying to resolve or implements my team request.
        I’m trying to convince them that for our project gef will be more stable and flexible. I think GMF is good only if your editor had to standard thing otherwise, cause the missing of a real documentation, you spend more time searching or understending how you can do someting instead writing a new editor with gef. And yes, also my enthusiasm have been killed by GMF.

        Returning to our gef editor…to be polite, yes you sould create a new DeleteRequest with the correct parameter.
        My solution still work only becouse the method OPMLinkConnectionEditPolicy.getDeleteCommand() don’t analyze the request, and always return a OPMLinkDeleteCommand

        • “Happy” to know that I am not the only one who can’t cope with GMF. And again, thanks for the tip.

Leave a Reply