JPz'log Coin Coin and Plop da Plop

1Sep/055

Building a GEF-based Eclipse editor – part 3

Continuing on the GEF series, let's tackle the EditParts.

First of all, sorry for the delay since the last GEF-related entry, but I had (and still have ...) a lot of work to do ;-)

EditParts play a very essential role in the GEF architecture. We previously saw how to create the view elements and you are supposed to already have your model. EditParts are simply the controllers that will make the link between the model and the view. More precisely, each view element will be mapped to an EditPart. In turn, each EditPart is related to the model. In simple cases, you can just map one EditPart to one instance of your model. However in most situations, one view element will hold for several classes instances in your model. Indeed, you don't necessarly want to expose each element of your model as a visual component. For instance if you had a Person class which owns a list of children, you may want to expose the list of children inside the Person instance view. This kind of flexibility is essential. EditParts have to be organized as trees. If you don't see how your model maps to a tree, then you will have to think about it ;-) Most of the time you have some kind of top-level class which is the root of your model. This will be mapped to a top-level EditPart whose view is the GEF editor container. The following picture gives an outline:

AbstractGraphicalEditPart should be subclassed to add EditParts that should be displayed in GEF. EditParts are expected to listen to the model changes. Doing so is easy if your model classes have events support via java.beans.PropertyChangeSupport. Connecting / disconnecting to the model is done the following way:

   /*     * (non-Javadoc)     *     * @see org.eclipse.gef.EditPart#activate()     */    public void activate()    {        if (!isActive())        {            super.activate();            getCastedModel().addPropertyChangeListener(this);        }    }    /*     * (non-Javadoc)     *     * @see org.eclipse.gef.EditPart#deactivate()     */    public void deactivate()    {        if (isActive())        {            super.deactivate();            getCastedModel().removePropertyChangeListener(this);        }    }

The getCastedModel() method is a convenient method that I add to convert the result of EditPart.getModel() to the actual class. EditParts should provide a list of children elements in the model if they have some. Proper EditParts are then instanciated. Here is an example:

   /*     * (non-Javadoc)     *     * @see org.eclipse.gef.editparts.AbstractEditPart#getModelChildren()     */    protected List getModelChildren()    {        List list = Collections.list(Collections.enumeration(getCastedModel().getStates()));        return list;    }

An EditPart should also provide a figure, for instance:

   /*     * (non-Javadoc)     *     * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure()     */    protected IFigure createFigure()    {        StateImpl state = getCastedModel();        figure = new StateFigure(state.getName(), state.isInitialState(), state.isFinalState());        return figure;    }

EditParts are instanciated from the model. To do that, we need a factory like this one:

public class BusinessProtocolEditPartFactory implements EditPartFactory {    /*     * (non-Javadoc)     *     * @see org.eclipse.gef.EditPartFactory#createEditPart(org.eclipse.gef.EditPart,     *      java.lang.Object)     */    public EditPart createEditPart(EditPart context, Object model)    {        EditPart part;        if (model instanceof BusinessProtocol)        {            part = new BusinessProtocolEditPart();        }        else if (model instanceof State)        {            part = new StateEditPart();        }        else if (model instanceof Operation)        {            part = new OperationEditPart();        }        else        {            return null;        }        part.setModel(model);        return part;    } }

The editor must know about this factory since when it will be provided with the model instance, it will ask for an EditPart of the top-level instance that will be given:

   /*     * (non-Javadoc)     *     * @see org.eclipse.gef.ui.parts.GraphicalEditor#configureGraphicalViewer()     */    protected void configureGraphicalViewer()    {        super.configureGraphicalViewer();
       GraphicalViewer viewer = getGraphicalViewer();        viewer.setEditPartFactory(new BusinessProtocolEditPartFactory()); (...)

In the editor that I have implemented, I had to display states. They will be nodes in the view, so their EditParts have to implement the NodeEditPart interface and provide visual anchors as well as the linking relations in the model:

   /*     * (non-Javadoc)     *     * @see org.eclipse.gef.NodeEditPart#getSourceConnectionAnchor(org.eclipse.gef.ConnectionEditPart)     */    public ConnectionAnchor getSourceConnectionAnchor(ConnectionEditPart connection)    {        return figure.getAnchor();    }    /*     * (non-Javadoc)     *     * @see org.eclipse.gef.NodeEditPart#getTargetConnectionAnchor(org.eclipse.gef.ConnectionEditPart)     */    public ConnectionAnchor getTargetConnectionAnchor(ConnectionEditPart connection)    {        return figure.getAnchor();    }    /*     * (non-Javadoc)     *     * @see org.eclipse.gef.NodeEditPart#getSourceConnectionAnchor(org.eclipse.gef.Request)     */    public ConnectionAnchor getSourceConnectionAnchor(Request request)    {        return figure.getAnchor();    }    /*     * (non-Javadoc)     *     * @see org.eclipse.gef.NodeEditPart#getTargetConnectionAnchor(org.eclipse.gef.Request)     */    public ConnectionAnchor getTargetConnectionAnchor(Request request)    {        return figure.getAnchor();    }    /*     * (non-Javadoc)     *     * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#getModelSourceConnections()     */    protected List getModelSourceConnections()    {        return getCastedModel().getOutgoingOperations();    }    /*     * (non-Javadoc)     *     * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#getModelTargetConnections()     */    protected List getModelTargetConnections()    {        return getCastedModel().getIncomingOperations();    }

When you display relations between nodes (like here between states), you will need to provide an EditPart for the instances returned in getModelXXXConnections(). These EditParts will have to extend AbstractConnectionEditPart. Typically, their figures will be PolylineConnection subclasses or instances.

If you read carefuly the GEF documentation and this post, you should understand how to manage to get your editor display the figures, including nodes and relations between them. However you cannot move the figures ... this will be for next time ;-)

Be sure to leave comments here, I might have forgotten a few things and there are parts were I can have been too fast on.

Share this post:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • DZone
  • Live
  • Netvibes
  • StumbleUpon
  • Technorati
  • FriendFeed
  • Wikio
  • Twitter
  • Identi.ca
  • Reddit
  • RSS
  • Slashdot

Related posts:

  1. Building a GEF-based Eclipse editor – part 2
  2. Building a GEF-based Eclipse editor – part 1
  3. Building a GEF-based Eclipse editor – part 0
  4. Eclipse 3.1 M5
  5. Diff / Patch and Eclipse

Comments (5) Trackbacks (0)
  1. Please tell me how to save the model composed with GEF? I try to save model using serialization but I get exception:
    java.io.NotSerializableException: org.eclipse.ui.views.properties.TextPropertyDescriptor :(

  2. Hi,

    You should save your model from the doSave / doSaveAs inherited methods of your editor class. The TextPropertyEditor is reponsible for loading/saving from/to the model, so trying to serialize it is not a good idea. You should rather not make your model a property source but use a composition to provide the property source in a decoupled way.

    Cheers

  3. With XMLEncoder i can get some good results? Or i need to save the model to file with my format?

  4. Saving to your own format is always preferable, but maybe you’ve made a mistake somewhere else. Personnaly I save to XML documents using Dom4J. It rocks.

  5. Can you give me any example?


Leave a comment


No trackbacks yet.

JPz'log is Digg proof thanks to caching by WP Super Cache