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.
@XmlRootElement
public
class
Model {
@XmlElement
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.
@Override
protected
void
bindAbstractContentPartAdapters( MapBinder<AdapterKey<?>,
Object> adapterMapBinder)
{
super.bindAbstractContentPartAdapters(adapterMapBinder);
//
register (default) interaction policies (which are based on viewer
//
models and do not depend on transaction policies)
adapterMapBinder
.addBinding(AdapterKey.get(FXClickDragTool.CLICK_TOOL_POLICY_KEY))
.to(FXFocusAndSelectOnClickPolicy.class);
adapterMapBinder
.addBinding(AdapterKey.get(FXHoverTool.TOOL_POLICY_KEY))
.to(FXHoverOnHoverPolicy.class);
//
geometry provider for selection feedback
adapterMapBinder
.addBinding(AdapterKey.get(
new
TypeToken<Provider<IGeometry>>(){},
FXDefaultFeedbackPartFactory.SELECTION_FEEDBACK_GEOMETRY_PROVIDER))
.to(VisualBoundsGeometryProvider.class);
//
geometry provider for hover feedback
adapterMapBinder
.addBinding(AdapterKey.get(
new
TypeToken<Provider<IGeometry>>(){},
FXDefaultFeedbackPartFactory.HOVER_FEEDBACK_GEOMETRY_PROVIDER))
.to(VisualBoundsGeometryProvider.class);
}
Normally shown node:
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)
adapterMapBinder
.addBinding(AdapterKey.get(FXTransformPolicy.class))
.to(FxTransformPolicy.class);
//
interaction policies to relocate on drag
adapterMapBinder
.addBinding(
AdapterKey.get(FXClickDragTool.DRAG_TOOL_POLICY_KEY))
.to(FXTranslateSelectedOnDragPolicy.class);
}
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 {
@Override
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());
}
}
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;
}
@Override
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
transform.setTx(0);
transform.setTy(0);
return
Status.OK_STATUS;
}
@Override
public
IStatus redo(IProgressMonitor monitor,
IAdaptable info)
throws
ExecutionException {
return
null;
}
@Override
public
IStatus undo(IProgressMonitor monitor,
IAdaptable info)
throws
ExecutionException {
return
null;
}
@Override
public
boolean
isNoOp() {
return
false;
}
}
Last step is to configure the ItemTranformPolicy to be used as implementation for FXTransformPolicy.
adapterMapBinder
.addBinding(AdapterKey.get(FXTransformPolicy.class))
.to(ItemTransformPolicy.class);
No comments:
Post a Comment