Creating a GEF Editor – Part 4: Showing the Model on the Editor

Previous Tutorial: Creating a GEF Editor – Part 3: Basic GEF Editor

The last episode of “Creating a GEF Editor” ended with the creation of a magnificent GEF-based editor that did… well.. nothing. But it worked!. In this tutorial we will add some more spice to the brew and will teach you how to display the model entities in the graph. There will be code, there will be modeling, and it will be FUN (I hope). So lets get started.

  1. Before we start, we have to fix one small configuration problem I found in the genmodel file that we created on the previous tutorials (not a serious problem but it does look bad). Thing is, the code generated by the genmodel came out in package opm, which for obvious reasons is not nice.

    So please open your genmodel file, and navigate to the package entry (the one with the purple icon). On the property sheet, we have to redefine the “Base Package” property, and while we are doing this, we’ll also change the names of the other generated packages which are all defined under the “Packages Suffixes” section. So the properties that will be changed:

    • Base Package: com.vainolo.phd
    • Implementation: model.impl
    • Interface: model
    • Metadata: model
    • Presentation: model.presentation
    • Provider: model.provider
    • Tests: model.test
    • Utility: model.util


    That should do the trick. Not we get to the ugly part of these changes. Not ALL changes done to the genmodel file can be applied to the generated code without causing some problems in the existing code, specially when classes must be deleted or moved (as in our case), therefore before re-generating the code, we’ll delete all of the generated plug-in projects, and the generated source files and plug-in files in the model project. Do this CAREFULLY. If you followed my instructions exactly up to here, you should be deleting the com.vainolo.phd.opm.model.edit, com.vainolo.phd.opm.model.editor, and com.vainolo.phd.opm.model.tests projects (if asked, also delete them from the disk). In the com.vainolo.phd.opm.model project, you should delete the META-INF directory, the build.properties, plugin.properties, and plugin.xml files and all of the files under the src directory. Your workspace should look like this after deleting everything (take a look before you delete):

    Good, now regenerate the code by right-clicking on the genmodel root and selecting “Generate All”. The GEF plug-in project should also compile, and it you try it should also execute correctly.

  2. Now for what I want to show you in this tutorial. First thing we are going to do is create a “dummy” model so that we can start playing without having to create a real mode, initialize it, load it, etc. This model is created by a utility class I called OPMModelUtils, with the following code:
    package com.vainolo.phd.opm.gef.utils;
    
    import com.vainolo.phd.opm.model.OPMFactory;
    import com.vainolo.phd.opm.model.OPMLink;
    import com.vainolo.phd.opm.model.OPMObject;
    import com.vainolo.phd.opm.model.OPMProcess;
    import com.vainolo.phd.opm.model.ObjectProcessDiagram;
    
    public enum OPMModelUtils {
    	INSTANCE;
    	
    	private OPMFactory factory = OPMFactory.eINSTANCE;
    	
    	public ObjectProcessDiagram createModel() {
    		ObjectProcessDiagram opd = factory.createObjectProcessDiagram();
    		OPMObject object1 = factory.createOPMObject();
    		object1.setName("O1");
    		opd.getObjects().add(object1);
    		OPMObject object2 = factory.createOPMObject();
    		object2.setName("O2");
    		opd.getObjects().add(object2);
    		OPMProcess process = factory.createOPMProcess();
    		process.setName("P1");
    		opd.getProcesses().add(process);
    		OPMLink link = factory.createOPMLink();
    		link.setSource(object1);
    		link.setTarget(process);
    		opd.getLinks().add(link);
    		return opd;
    	}
    }
    

    I didn’t say this, but from this point all classes will be created in the com.vainolo.phd.opm.gef project unless otherwise stated. The new class is a Singleton (as Joshua Bloch says it should be done) that provides a simple function to create a dummy model that consists of two OPMObjects, one OPMProcess and one OPMLink.

  3. The basic GEF editor architecture is composed of three elements, since it is an MVC framework:
    1. Model: the source of the information that will be displayed and changed using the editor. This can be whatever you want, in our case it is a model created in EMF.
    2. EditorParts: the controllers of the GEF framework. The EditParts glue between the model and the view, creating the view based on the model and updating the model based on request that are done in the view (and updating the view again… seems strange but when the view is being update the infinitesimal updates are done twice: once when you change the view to update the model and once when the view is updated because of the model change… but I’m going a bit too fast here). the EditParts are also GEF’s model of the editor – an EditPart is responsible for providing its model children, which will create EditParts that will be drawn inside the figure that is provided by this EditPart (we’ll see that later).
    3. View: GEF views are basically org.eclipse.draw2d figures drawn on the editor. These figures are provided by the EditPart when requested by the GEF framework.
  4. We already have a model, so we’ll start by creating a view for an OPMObject. An OPMObject is represented by a rectangle with the name of the OPMObject in the middle of the rectangle. Creating a figure that displays this is easy, so go ahead and in the GEF project create a new class called com.vainolo.phd.opm.gef.editor.figure.OPMObjectFigure (having packages to catalog your classes is always nice, so they will be created by the functionality that the classes provide). This is the code for the class:
    package com.vainolo.phd.opm.gef.editor.figure;
    
    import org.eclipse.draw2d.Figure;
    import org.eclipse.draw2d.Graphics;
    import org.eclipse.draw2d.Label;
    import org.eclipse.draw2d.RectangleFigure;
    import org.eclipse.draw2d.XYLayout;
    import org.eclipse.draw2d.geometry.Rectangle;
    
    public class OPMObjectFigure extends Figure {
    	private Label label;
    	private RectangleFigure rectangle;
    	
    	public OPMObjectFigure() {
    		setLayoutManager(new XYLayout());
    		rectangle = new RectangleFigure();
    		add(rectangle);
    		label = new Label();
    		add(label);
    	}
    
    	@Override protected void paintFigure(Graphics graphics) {
    		Rectangle r = getBounds().getCopy();
    		setConstraint(rectangle, new Rectangle(0, 0, r.width, r.height));
    		setConstraint(label, new Rectangle(0, 0, r.width, r.height));
    	}
    	
    	public Label getLabel() {
    		return label;
    	}
    }
    
  5. Next, we’ll add our EditParts. Although we are creating an editor to show the OPMObject class, we have to create two EditParts, one for the OPMObject and another one for the ObjectProcessDiagram which will become the container of all of the other EditParts in the graph. From what I’ve seen in most tutorials and explanations on the web, this is how things should work: there is always a “root” EditPart that serves as the “canvas” on which all of the diagram/graph is drawn/edited. In our case, this is the ObjectProcessDiagram. Therefore we’ll be creating two classes: an OPMObjectEditPart and an OPMObjectProcessDiagramEditPart, both descendants of AbstractGraphicalEditPart which already provides us with a lot of functionality. In this tutorial we’ll set the location of the object figure randomly in the diagram:
    • OPMObjectEditPart: This class is used to manage the OPMObject that appear in the model. It creates a OPMObjectFigure class as it’s figure and manages the drawing properties of the figure when requested using the refreshVisuals method. Later on we’ll see how the model instance is set in the class, for now just trust me :-).

      package com.vainolo.phd.opm.gef.editor.part;
      
      import java.util.Random;
      import org.eclipse.draw2d.IFigure;
      import org.eclipse.draw2d.geometry.Rectangle;
      import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
      import com.vainolo.phd.opm.gef.editor.figure.OPMObjectFigure;
      import com.vainolo.phd.opm.model.OPMObject;
      
      public class OPMObjectEditPart extends AbstractGraphicalEditPart {
      	Random rand = new Random();
      
      	@Override 
      	protected IFigure createFigure() {
      		return new OPMObjectFigure();
      	}
      
      	@Override
      	protected void createEditPolicies() {
      		// TODO Auto-generated method stub
      		
      	}
      	
      	@Override protected void refreshVisuals() {
      		OPMObjectFigure figure = (OPMObjectFigure)getFigure();
      		OPMObject model = (OPMObject)getModel();
      		ObjectProcessDiagramEditPart parent = (ObjectProcessDiagramEditPart) getParent();
      		
      		figure.getLabel().setText(model.getName());
      		Rectangle layout = new Rectangle(rand.nextInt(300), rand.nextInt(300), 50, 50);
      		parent.setLayoutConstraint(this, figure, layout);
      	}
      }
      
    • ObjectProcessDiagramEditPart: This class manages the ObjectProcessDiagram model instances (currently only one). Two things I’d like to say about this class. First, the Figure that this class creates works as the canvas of the diagram, is fairly simple and doesn’t change, therefore there is no need to create a special class for it. Second, notice that the class overrides the getModelChildren method, which is used by the GEF framework. The framework’s hierarchy doesn’t have to be defined directly by the model so it can be manipulated here (for example if you want to skip one hierarchy step in your model, or if you have two levels of model hierarchy that have only one visual representation). Simply put, GEF starts creating the diagram by requesting the EditPart of the base model class (later we’ll see where this is set), and from it its Figure. It then goes on the retrieve the modelChildren of this EditPart, creating their Figures and adding them as children to the parent’s figure, and so on until the EditPart does not return any children.
      package com.vainolo.phd.opm.gef.editor.part;
      
      import java.util.ArrayList;
      import java.util.List;
      import org.eclipse.draw2d.FreeformLayer;
      import org.eclipse.draw2d.FreeformLayout;
      import org.eclipse.draw2d.IFigure;
      import org.eclipse.draw2d.LineBorder;
      import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
      
      import com.vainolo.phd.opm.model.OPMObject;
      import com.vainolo.phd.opm.model.ObjectProcessDiagram;
      
      public class ObjectProcessDiagramEditPart extends AbstractGraphicalEditPart {
      	@Override 
      	protected IFigure createFigure() {
      		FreeformLayer layer = new FreeformLayer();
      		layer.setLayoutManager(new FreeformLayout());
      		layer.setBorder(new LineBorder(1));
      		return layer;
      	}
      
      	@Override 
      	protected void createEditPolicies() {
      		// TODO Auto-generated method stub
      	}
      	
      	@Override protected List<OPMObject> getModelChildren() {
      		List<OPMObject> retVal = new ArrayList<OPMObject>();
      		ObjectProcessDiagram opd = (ObjectProcessDiagram) getModel();
      		retVal.addAll(opd.getObjects());
      		return retVal;
      	}	
      }
      
      
  6. Now that we have the edit parts, we need to provide the GEF framework with a Factory Class that know how to create these EditParts. The factory receives as a parameter the model instance’s class and returns an EditPart instance that should be used to manage this class. This is the point where the model is connected to the EditPart:
    package com.vainolo.phd.opm.gef.editor.part;
    
    import com.vainolo.phd.opm.model.OPMObject;
    import com.vainolo.phd.opm.model.ObjectProcessDiagram;
    import org.eclipse.gef.EditPart;
    import org.eclipse.gef.EditPartFactory;
    
    public class OPMEditPartFactory implements EditPartFactory {
    
    	@Override public EditPart createEditPart(EditPart context, Object model) {
    		EditPart part = null;
    		
    		if(model instanceof ObjectProcessDiagram) {
    			part = new ObjectProcessDiagramEditPart();
    		} else if(model instanceof OPMObject) {
    			part = new OPMObjectEditPart();
    		}
    		
    		if(part != null) {
    			part.setModel(model);
    		}
    		
    		return part;
    	}
    }
    
  7. Good. Now, to finish the job, we have to load the model in the editor and also define the EditPart factory. This is all done in the OPMGraphicalEditor class:
    package com.vainolo.phd.opm.gef.editor;
    
    import org.eclipse.core.runtime.IProgressMonitor;
    import org.eclipse.gef.DefaultEditDomain;
    import org.eclipse.gef.palette.PaletteRoot;
    import org.eclipse.gef.ui.parts.GraphicalEditorWithFlyoutPalette;
    import com.vainolo.phd.opm.gef.editor.part.OPMEditPartFactory;
    import com.vainolo.phd.opm.gef.utils.OPMModelUtils;
    
    public class OPMGraphicalEditor extends GraphicalEditorWithFlyoutPalette {
    
    	public OPMGraphicalEditor() {
    		setEditDomain(new DefaultEditDomain(this));
    	}
    	
    	@Override protected void initializeGraphicalViewer() {
    		super.initializeGraphicalViewer();
    		getGraphicalViewer().setContents(OPMModelUtils.INSTANCE.createModel());
    	}	
    	
    	@Override protected void configureGraphicalViewer() {
    		super.configureGraphicalViewer();
    		getGraphicalViewer().setEditPartFactory(new OPMEditPartFactory());
    	}	
    	
    	@Override
    	protected PaletteRoot getPaletteRoot() {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public void doSave(IProgressMonitor monitor) {
    		// TODO Auto-generated method stub
    	}
    }
    

    You are probably asking yourselves why we need to use two functions… and I also asked this myself, but the tutorials I read did it this way an it work. I’ll check this a bit more and if there are more conclusions I’ll point them out.

  8. That’s it! You can now execute your project and open an “.opm” file (if its not open yet). My screen looks like this:

The final eclipse project files can be downloaded here.

One reader found that the generated EMF editor does not correctly. Please look at the comments if this happens to you.

I hope you have enjoyed this tutorial as much as I enjoyed learning and writing it. In the next tutorial we’ll read the model from a real emf file, and if that is not enough, I’ll throw in some editing stuff. So stay tuned.

Next Tutorial: Creating a GEF Editor – Part 5: Loading the Model from an EMF File

11 thoughts on “Creating a GEF Editor – Part 4: Showing the Model on the Editor

  1. I cannot create any children in the model-editor of ObjectProcessDiagram. Is there a necessary property to get the “create child”-submenu in the plugin?

  2. Hello Arieh,

    Thanks a lot for the tutorial, it helped me to kick start my development. It is by far the most comprehensive tutorial online.

    I have one question. Do you have any suggestion how to layout elements in the following manner:
    5 columns. 2nd and 4th should contain rectangle elements in, what is closest to Toolbar layout. The other columns are not relevant (in terms of layout) since there will be only connections.
    These elements could be connected in every way. 2nd with another element from 2nd. 2nd with element from 4th. 4th with 4th.

    I already googled one solution, but I thought to see for alternatives.
    http://www.eclipse.org/forums/index.php/t/44869/

    • I Bojan. Thanks for the compliments, glad to be of help.
      I have not really investigated the layouts in GEF, but it is in my to-do list. But if you find a good solution please keep me posted.

  3. I have a problem with Step 2. There is the method “.add”. It does not work in my code. I found the reason: In my code the methods “getProcesses” etc are NO ELists. In your downloaded Project they are all Elists. Can you explain when that happened? I can´t find a difference in the ecore model and the reason why I have no ELists.

    • In the opm.genmodel file you can set whether the generated classes use EMF of standard collections. In the first node of the genmodel file, the property “Suppress EMF types” should be set to false (the property is under the “Mode Feature Defaults” group. After changing this, regenerate your model.
      This is the only reason that pops to my mind. Hope it does the trick.

  4. Hi Admin,

    I have a problem on OPMObjectEditPart.java
    it says model.getName() where modle is OPMObject object. That class does not have getName() method instead it have getEString().

    Do you know what is that?

    • Sorry my mistake. On my opm.ecore file we created on tutorial 1, instead of putting “name” to Name field under first attribute on OPMObject I put “EString”.

      I rectified it.

Leave a Reply