Friday, November 6, 2015

GEF4 Tutorial - Part 4 - Dragging and store to model

In step 4 of this tutorial, the text nodes can be dragged around with the mouse. The new positions are stored into the model object.
The model as whole is restored and persisted at application start and end.

For the source of this tutorial step see github - gef4.mvc.tutorial4.

Note: parts of this tutorial are copied from other examples or from forum postings.

Restoring and persisting the Model

For mapping the model, here JAXB is use.

public class Model {
    LinkedList<TextNode> nodes = new LinkedList<>();

Those annotation tell JAXB how to map content to XML and reverse.
This code loads an XML file and create the model with all child objects.

jaxbContext = JAXBContext.newInstance(Model.class, TextNode.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
model = (Model) jaxbUnmarshaller.unmarshal(new File("model.xml"));

This code persists the model to a XML file:

Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal( model, new File("model.xml"));

Adding this code into the applications start and stop methods, automates the reload and store.

The Adapter pattern used in GEF4

Eclipse support the Adapter pattern, see this article:

In summary, it means, objects in Eclipse context that implement the IAdaptable interface, can give an implementation of a requested interface.

In GEF4, this pattern was enhanced. 
See this article by Alexander Ny├čen:

In addition to the Eclipse adapters, in GEF4, adapters can be configured at runtime, can exists for the same interface type in different roles, can have a reference to the adapted object.

Making the nodes selectable

So in GEF4, the configuration of the Guice module is one of the important control points of a application.

To make the nodes in the tutorial selectable, the following code was taken from the Logo example.

protected void bindAbstractContentPartAdapters( MapBinder<AdapterKey<?>, Object> adapterMapBinder) {
    // register (default) interaction policies (which are based on viewer
    // models and do not depend on transaction policies)

    // geometry provider for selection feedback
            new TypeToken<Provider<IGeometry>>(){},

    // geometry provider for hover feedback
            new TypeToken<Provider<IGeometry>>(){},

Normally shown node:

The mouse hoovering over the node, creates a surrounding box marker.

Clicking makes the box darker, so it is shown as selected.

Making the node dragable

In the Guice module configure:

bindTextNodePartAdapters(AdapterMaps.getAdapterMapBinder(binder(), TextNodePart.class));

The implementation:

protected void bindTextNodePartAdapters( MapBinder<AdapterKey<?>, Object> adapterMapBinder) {
    // register resize/transform policies (writing changes also to model)
    // interaction policies to relocate on drag
        .addBinding( AdapterKey.get(FXClickDragTool.DRAG_TOOL_POLICY_KEY))

This uses the standard components to make items dragable.
It is surprising that this works, as there is yet no linkage to the model. 
Try it out!
It even works if you press the button to update the model (vary the values).
The dragging information is stored in the visuals as a transformation. The model and part can continue to work with the original coordinates.

Updating the model

To give the whole a sense, the position of the TextNode shall be stored to the model. Then it can be persisted and restored.

For this, the ItemTransformPolicy is extended from FXTransformPolicy.

public class ItemTransformPolicy extends FXTransformPolicy {

    public ITransactionalOperation commit() {
        ITransactionalOperation visualOperation = super.commit();
        ITransactionalOperation modelOperation = createUpdateModelOperation();
        ForwardUndoCompositeOperation commit = new ForwardUndoCompositeOperation("Translate()");
        if (visualOperation != null) commit.add(visualOperation);
        if (modelOperation != null) commit.add(modelOperation);
        return commit.unwrap(true);

    private ITransactionalOperation createUpdateModelOperation() {
        return new ChangeTextNodePositionOperation(getHost());

The ItemTransformPolicy combines the original FXTranformPolicy with a new opereration, the ChangeTextNodePositionOperation. The new operation shall remove the tranformation from the visuals, and store the information into the mode.

public class ChangeTextNodePositionOperation extends AbstractOperation implements ITransactionalOperation {

    TextNodePart part;
    public ChangeTextNodePositionOperation(IVisualPart<Node, ? extends Node> part) {
        super( "" );
        Assert.isLegal(part instanceof TextNodePart, "Only TestNodePart supported for ChangeItemPositionOperation");
        this.part = (TextNodePart) part;

    public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
        Affine transform = part.getAdapter(FXTransformPolicy.TRANSFORM_PROVIDER_KEY).get();
        // tell the part, which updates the model, will also trigger a doRefreshVisuals
        part.translate(transform.getTx(), transform.getTy());
        // reset the transformation
        return Status.OK_STATUS;

    public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
        return null;

    public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
        return null;

    public boolean isNoOp() {
        return false;


Last step is to configure the ItemTranformPolicy to be used as implementation for FXTransformPolicy.


No comments:

Post a Comment