In this topic we will see a method to build a cutsom figure with specific edge anchor zones.
We will build a diagram editor allowing to draw operators such as plus and minus. We'll connect them to input entries and output results.
An operator has two inputs and one output. The input may be connected to an entry (that the user may modify), or a result (which is read only). The output can only be connected to a result.
Our aim is to be able to anchor the diagram edges on the graphical representations of ou operator inputs and output. To do that, we will use specific model objects (OperatorInput
and OperatorOutput
) that will be contained in our operators (containment association). Their graphical representations will be “manually” positionned in a container rectangle (this rectangle will also receive the other graphical items that helps to represent the operator).
Full code is availabale through :
Full code is also available for several intermediate steps of this tutorial through :
This tutorial has been built with :
Known bugs and fixes :
We will build our model with the annotated java interfaces method.
Math
jfb.examples.gmf.math
package jfb.examples.gmf.math; /** * @model */ public interface OperatorInput { /** * @model */ public Operator getOperator(); /** * @model */ public Number getNumber(); }
package jfb.examples.gmf.math; /** * @model */ public interface OperatorOutput { /** * @model */ public Operator getOperator(); /** * @model */ public Result getResult(); }
package jfb.examples.gmf.math; import java.util.List; /** * @model abstract="true" */ public interface Operator { /** * @model containment="true" */ public List<OperatorInput> getInputs(); /** * @model containment="true" */ public OperatorOutput getOutput(); }
package jfb.examples.gmf.math; /** * @model */ public interface MinusOperator extends Operator { }
package jfb.examples.gmf.math; /** * @model */ public interface PlusOperator extends Operator { }
package jfb.examples.gmf.math; import java.util.List; /** * @model abstract="true" */ public interface Number { /** * @model */ public List<OperatorInput> getOperatorInputs(); }
package jfb.examples.gmf.math; /** * @model */ public interface Entry extends Number { }
package jfb.examples.gmf.math; /** * @model */ public interface Result extends Number { /** * @model */ public OperatorOutput getOperatorOutput(); }
package jfb.examples.gmf.math; import java.util.List; /** * @model */ public interface MathDiagram { /** * @model containment="true" */ public List<Operator> getOperators(); /** * @model containment="true" */ public List<Entry> getEntries(); /** * @model containment="true" */ public List<Result> getResults(); }
EMF Generator model
named math.genmodel
in the model
folder (see tutorial #1 for an example).Annotated Java
as Model Importer
and jfb.example.gmf.math
in the packages list.math.ecore
fileoperatorInputs : OperatorInput
(child node of Number
) and set the EOpposite
property to number : Number
(wich belongs to the OperatorInput
element).number : Number
(child node of OperatorInput
), the EOpposite
property should be set to operatorInputs : OperatorInput
.result : Result
child node of OperatorOutput
and the operatorOutput : OperatorOutput
child node of Result
.inputs : OperatorInput
child node of Operator
and the operator : Operator
child node of OperatorInput
.inputs : OperatorOutput
child node of Operator
and the operator : Operator
child node of OperatorOutput
.Lower Bound
and Upper Bound
of the inputs : OperatorInput
node in the property view to 2 (child node of the Operator
element).GMF dashboard view
(see tutorial #1 for more details on these steps)math.genmodel
fileGenerate Model Code
Generate Edit Code
Generate Editor Code
We need 6 creation tools :
Procedure :
Derive
label which is on the left of the Tooling Def Model
rectanglemath.gmftool
in the folder modelMathDiagram
and click Next
Nodes
column, select (and select only) : Entry
, Minusoperator
, PlusOperator
, Result
Connections
column, select (and select only) : Entry.operatorInputs
, OperatorOutput.result
Finish
Procedure :
Derive
label which is on the left of the Graphical Def Model
rectangle.math.gmfgraph
in the model
folderMathDiagram
and click Next
Nodes
column, select (and select only) : Entry
, Minusoperator
, OperatorInput
, OperatorOutput
, PlusOperator
, Result
Connections
column, select (and select only) : Entry.operatorInputs
, OperatorOutput.result
, Result.operatorInputs
Label
column, select : Entry.value
, Result.value
Finish
math.gmfgraph
fileRectangle MinusOperatorFigure
node (which is under the Figure Descriptor MinusOperatorFigure
) :Boder Layout
Rectangle
named MinusOperatorCompartmentFigure
Rectangle MinusOperatorCompartmentFigure
), add a Border Layout Data
(with Alignment
= CENTER
)Figure Descriptor MinusOperatorFigure
node, add a Child access
and select Rectangle MinusOperatorCompartmentFigure
for its figure (in the properties view)Plus operator
:Canvas math
node, add two Compartment
nodesMinusOperatorFigureCompartment
Figure Descriptor MinusOperatorFigure
for its figure and Child Access getFigureMinusOperatorCompartmentFigure
for its accessorPlusOperatorFigureCompartment
Figure Descriptor PlusOperatorFigure
for its figure and Child Access getFigurePlusOperatorCompartmentFigure
for its accessorFigure Descriptor ResultFigure
, change the Rectangle ResultFigure
into an Ellispe ResultFigure
(it is possible to open the math.gmfmap
with a text editor and replace the string Rectangle
by Ellipse
in the node <actualFigure xsi:type=“gmfgraph:Rectangle” name=“ResultFigure”>
Combine
labelmath.gmfmap
filename in the model
folderMathDiagram
and click Next
Next
on the Select Diagram Palette
pageNext
on the Select Diagram Canvas
pageOperatorOutput (<OperatorOutputResult: output)
and OperatorInput (<EntryOperatorInputs: inputs)
form the links zone to the nodes zone (right to left) on the Mapping
page like this :Finish
math.gmfmap
fileNode Mapping <Result/Result>
add a Feature Label Mapping
and set its properties :Feature to display
=Number.value:EDouble
Diagram Label
=Diagram Label ResultValue
Read Only
=true
Node Mapping <Entry/Entry>
add a Feature Label Mapping
and set its properties :Feature to display
=Number.value:EDouble
Diagram Label
=Diagram Label EntryValue
Read Only
=false
Node Mapping <MinusOperator/MinusOperator>
node (which is under Top Node Reference <operators:MinusOperator/MinusOperator>
) : Compartment Mapping
and select Compartment MinusOperatorFigureCompartment (MinusoperatorFigure)
in the properties view Child Reference
and set its properties like this : Compartment
=Compartment Mapping <MinusOperatorFigureCompartment>
Containment feature
=Operator.inputs:OperatorInput
Reference Child
= (empty)Node Mapping <OperatorInput/OperatorInput>
under this Child Reference
nodeTop Node Reference <inputs:OperatorInput/OperatorInput>
under which was the node that has just been movedChild Reference
and set its properties like this : Compartment
=Compartment Mapping <MinusOperatorFigureCompartment>
Containment feature
=Operator.outut:OperatorOutput
Reference Child
= (empty)Node Mapping <OperatorOutput/OperatorOutput>
under this Child Reference
nodeTop Node Reference <output:OperatorOutput/OperatorOutput>
under which was the node that has just been movedNode Mapping <PlusOperator/PlusOperator>
node (which is under Top Node Reference <operators:PlusOperator/PlusOperator>
) : Compartment Mapping
and select Compartment PlusOperatorFigureCompartment (PlusoperatorFigure)
in the properties view Child Reference
and set its properties like this : Compartment
=Compartment Mapping <PlusOperatorFigureCompartment>
Containment feature
=Operator.inputs:OperatorInput
Reference Child
= Node mapping <OperatorInput/>
Child Reference
and set its properties like this : Compartment
=Compartment Mapping <PlusOperatorFigureCompartment>
Containment feature
=Operator.outut:OperatorOutput
Reference Child
= Node mapping <OperatorOutput/>
Node Mapping <Result/Result>
: Diagram Node
=Node Result (ResultFigure)
Tool
=Creation Tool Result
Node Mapping <Entry/Entry>
: Diagram Node
=Node Entry (EntryFigure)
Tool
=Creation Tool Entry
Node Mapping <MinusOperator/MinusOperator>
: Diagram Node
=Node MinusOperator (MinusOperatorFigure)
Tool
=Creation Tool MinusOperator
Node Mapping <PlusOperator/PlusOperator>
: Diagram Node
=Node PlusOperator (PlusOperatorFigure)
Tool
=Creation Tool PlusOperator
Node Mapping <OperatorOutput/>
: Diagram Node
=Node OperatorOutput (OperatorOutputFigure)
Tool
= (empty !)Node Mapping <OperatorInput/>
: Diagram Node
=Node OperatorInput (OperatorInputFigure)
Tool
= (empty !)Link Mapping <{Number.operatorInputs:OperatorInput}/EntryOperatorInput>
: Target Feature
=Number.operatorInputs:OperatorInput
Diagram Link
=Connection EntryOperatorInput
Tool
=Creation Tool Entry/Result to operator input
Link Mapping <{Result.operatorOutput:OperatorOutput}/OperatorOutputResult>
:Target Feature
=OperatorOutput.result:Result
Diagram Link
=Connection OperatorOutputResult
Tool
=Creation Tool Operator output to result
Transform
label of the dashboardmath.gmfgen
should be created automaticallyList layout
property is set to true in the properties view for the following compartment nodes :Gen Compartment MinuOperatorMinusOperatorFigureCompartmentEditPart
Gen Compartment PluOperatorPlusOperatorFigureCompartmentEditPart
Generate diagram editor
label of the dashboard/Math.diagram/src/jfb/examples/gmf/math/diagram/edit/commands/MinusOperatorCreateCommand.java
file, edit the doExecuteWithResult(IProgressMonitor, IAdaptable)
method and modify it like this (do not forget to add the 'NOT' mention after @generated
) :/** * @generated NOT */ protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { MinusOperator newElement = MathFactory.eINSTANCE.createMinusOperator(); // Adds both operator inputs newElement.getInputs().add(MathFactory.eINSTANCE.createOperatorInput()); newElement.getInputs().add(MathFactory.eINSTANCE.createOperatorInput()); // Adds the operator output newElement.setOutput(MathFactory.eINSTANCE.createOperatorOutput()); MathDiagram owner = (MathDiagram) getElementToEdit(); owner.getOperators().add(newElement); doConfigure(newElement, monitor, info); ((CreateElementRequest) getRequest()).setNewElement(newElement); return CommandResult.newOKCommandResult(newElement); }
/Math.diagram/src/jfb/examples/gmf/math/diagram/edit/commands/PlusOperatorCreateCommand.java
file./Math.diagram/src/jfb/examples/gmf/math/diagram/preferences/DiagramConnectionsPreferencePage.java
and add this to set the default edge routing mode to rectilinear
:public static void initDefaults(IPreferenceStore preferenceStore) { preferenceStore.setDefault(IPreferenceConstants.PREF_LINE_STYLE, Routing.RECTILINEAR); }
A this point, it is not possible to distinguish plus operators from minus operators, and their graphical representations are not very pleasant ! We will customize our graphical representations in the next steps.
In this part, we will customize the graphical representation of our operators to get something like this :
jfb.examples.gmf.math.diagram.edit.parts.custom
PlusRoundedRectangle
which will extend org.eclipse.draw2d.RoundedRectangle
paintFigure
method in order to represent the plus sign : package jfb.examples.gmf.math.diagram.edit.parts.custom; import org.eclipse.draw2d.ColorConstants; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.RoundedRectangle; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; public class PlusRoundedRectangle extends RoundedRectangle { public PlusRoundedRectangle() { super(); setLineWidth(2); } public void paintFigure(Graphics graphics) { super.paintFigure(graphics); graphics.setForegroundColor(ColorConstants.black); graphics.setForegroundColor(ColorConstants.black); graphics.setLineStyle(Graphics.LINE_SOLID); graphics.setLineWidth(3); Rectangle r = getBounds(); // vertical line graphics.drawLine( new Point(r.x + r.width / 2, r.y + r.height * 0.2), new Point(r.x + r.width / 2, r.y + r.height * 0.8)); // horizontal line graphics.drawLine( new Point(r.x + r.width * 0.2, r.y + r.height / 2), new Point(r.x + r.width * 0.8, r.y + r.height / 2)); }; }
MinusRoundedRectangle
the same way :package jfb.examples.gmf.math.diagram.edit.parts.custom; import org.eclipse.draw2d.ColorConstants; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.RoundedRectangle; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; public class MinusRoundedRectangle extends RoundedRectangle { public MinusRoundedRectangle() { super(); setLineWidth(2); } public void paintFigure(Graphics graphics) { super.paintFigure(graphics); graphics.setForegroundColor(ColorConstants.black); graphics.setForegroundColor(ColorConstants.black); graphics.setLineStyle(Graphics.LINE_SOLID); graphics.setLineWidth(3); Rectangle r = getBounds(); // horizontal line graphics.drawLine( new Point(r.x + r.width * 0.2, r.y + r.height / 2), new Point(r.x + r.width * 0.8, r.y + r.height / 2)); }; }
* Now we add our rounded rectangles to the graphical representations of our operators. To do that, edit the /Math.diagram/src/jfb/examples/gmf/math/diagram/edit/parts/MinusOperatorMinusOperatorFigureCompartmentEditPart.java
file and modify the createFigure
method :
/** * @generated NOT */ public IFigure createFigure() { ResizableCompartmentFigure result = (ResizableCompartmentFigure) super .createFigure(); result.setTitleVisibility(false); // Setup for a XYLayout IFigure contentPane = result.getContentPane(); contentPane.setLayoutManager(new XYLayout()); // Delete content pane insets Insets is = contentPane.getInsets(); is.top = 0; is.bottom = 0; is.left = 0; is.right = 0; // Setup graphical elements MinusRoundedRectangle roundedRectangle = new MinusRoundedRectangle(); contentPane.add(roundedRectangle); // Add the resize events listener result.addFigureListener(new OperatorCompartmentFigureListener(this, roundedRectangle)); return result; }
OperatorCompartmentFigureListener
. So, you may get some compilation problems ; we will create the missing class just a few steps later.
PlusOperatorPlusOperatorFigureCompartmentEditPart.java
:/** * @generated NOT */ public IFigure createFigure() { ResizableCompartmentFigure result = (ResizableCompartmentFigure) super .createFigure(); result.setTitleVisibility(false); // Setup for a XYLayout IFigure contentPane = result.getContentPane(); contentPane.setLayoutManager(new XYLayout()); // Delete content pane insets Insets is = contentPane.getInsets(); is.top = 0; is.bottom = 0; is.left = 0; is.right = 0; // Setup graphical elements PlusRoundedRectangle roundedRectangle = new PlusRoundedRectangle(); contentPane.add(roundedRectangle); // Add the resize events listener result.addFigureListener(new OperatorCompartmentFigureListener(this, roundedRectangle)); return result; }
OperatorCompartmentFigureListener
. This component will resize the rounded rectangle and the operator inputs and output :package jfb.examples.gmf.math.diagram.edit.parts.custom; import java.util.List; import jfb.examples.gmf.math.diagram.edit.parts.OperatorInputEditPart; import jfb.examples.gmf.math.diagram.edit.parts.OperatorOutputEditPart; import org.eclipse.draw2d.FigureListener; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.RoundedRectangle; import org.eclipse.draw2d.geometry.Insets; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.gef.editparts.AbstractEditPart; import org.eclipse.gef.editparts.AbstractGraphicalEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.ListCompartmentEditPart; import org.eclipse.gmf.runtime.diagram.ui.figures.ResizableCompartmentFigure; public class OperatorCompartmentFigureListener implements FigureListener { private ListCompartmentEditPart compartmentEditPart = null; private RoundedRectangle roundedRectangle = null; public static final double MARGIN = 20; // The margin to apply before drawing our operator public static final double R = 50; // The base length public static final double REF_W = 2 * MARGIN + R * 6; // Reference width public static final double REF_H = 2 * MARGIN + R * 5; // Reference height public OperatorCompartmentFigureListener(ListCompartmentEditPart compartmentEditPart, RoundedRectangle roundedRectangle) { this.compartmentEditPart = compartmentEditPart; this.roundedRectangle = roundedRectangle; } @Override public void figureMoved(IFigure f) { ResizableCompartmentFigure figure = (ResizableCompartmentFigure) f; if (figure.getSize().width != 0) { IFigure contentPane = figure.getContentPane(); Insets is = figure.getInsets(); // Determine the scale to apply double xScale = ((double) figure.getSize().width - is.left - is.right) / REF_W; double yScale = ((double) figure.getSize().height - is.top - is.bottom) / REF_H; // Set the constraints (bounds) for the rounded rectangle Rectangle constraint = new Rectangle( (int) ((MARGIN + R) * xScale), (int) ((MARGIN) * yScale), (int) (R * 4 * xScale), (int) (R * 5 * yScale)); contentPane.setConstraint(roundedRectangle, constraint); // Set the constraints for the input and output nodes List<AbstractEditPart> childs = compartmentEditPart.getChildren(); boolean firstInputProcessed = false; for (AbstractEditPart child : childs) { if (child instanceof AbstractGraphicalEditPart) { AbstractGraphicalEditPart gEditPart = (AbstractGraphicalEditPart) child; // Operator output ? if (gEditPart instanceof OperatorOutputEditPart) { constraint = new Rectangle( (int) ((REF_W - MARGIN - R) * xScale), (int) ((REF_H - R) / 2 * yScale), (int) (R * xScale), (int) (R * yScale)); contentPane.setConstraint(gEditPart.getFigure(), constraint); } // Operator input ? else if (gEditPart instanceof OperatorInputEditPart) { constraint = new Rectangle( (int) (MARGIN * xScale), !firstInputProcessed ? (int) ((MARGIN + R) * yScale) : (int) ((MARGIN + R * 3) * yScale), (int) (R * xScale), (int) (R * yScale)); contentPane.setConstraint(gEditPart.getFigure(), constraint); firstInputProcessed = true; // This boolean heps to know if we process the first or the seconde operator input } } } } } }
MinusOperatorMinusOperatorFigureCompartmentEditPart
and PlusOperatorPlusOperatorFigureCompartmentEditPart
)Before we add a feature that computes automatically the operations, we have to deal with a potential problem : at this point, it is possible to create cycles in our diagram :
If we let it uncorrected, it will imply infinite loops when trying to set the operator's result value.
To deal with it, we will add a contraint to our diagram that will detect the cycles.
These are the steps to follow :
math.gmfmap
Mapping
node, add an Audit Container
node, and set its Name
and Id
to MathAuditContainer
in the properties view.Audit Rule
node and set its properties :Id
= CycleDetectorRule
Message
= A cycle has been detected
Name
= CycleDetectorRule
Severity
= ERROR
Use In Live Mode
= false
Diagram Element Target
node and set its Element
property to Link Mapping <{OperatorOutput.result:Result}>
(we want the error icon to appear on the links between the operators and their results)Constraint
node and set its properties to :Body
= CycleDetectorConstraint
Language
= java
Transform
label of the dashboardmath.gmfgen
Gen Diagram MathDiagrameditPart
node and modify its properties in order to activate the validation decorators in the diagram : Validation decorators
= true
Validation Enabled
= true
Generate diagram editor
label of the dashboardAt this point a java skeleton has been generated that must be customized with the constraint specific java code. Before we implement the constraint, we will create a utility class that will contain the cycle detection code.
Math.diagram
project, create a new package named jfb.examples.gmf.math.diagram.util
.CycleDetectionHelper
and copy/paste this code :package jfb.examples.gmf.math.diagram.util; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import jfb.examples.gmf.math.OperatorInput; import jfb.examples.gmf.math.Result; import org.eclipse.emf.common.util.EList; public class CycleDetectionHelper { /** * Navigate through the model from one result to detect if there is a cycle. * @param fromResult the result from which the navigation starts. * @return a boolean indicating if a cycle has been detected. */ public static boolean cycleDetected(Result fromResult) { List<Result> processedResults = new ArrayList<Result>(); processedResults.add(fromResult); return cycleDetected(processedResults, fromResult); } /** * Navigate through the model from one result to detect if there is a cycle. * @param processedResults result already processed during this navigation. * @param result the result to process. * @return a boolean indicating if a cycle has been detected. */ private static boolean cycleDetected(List<Result> processedResults, Result result) { System.out.println("cycleDetected(" + processedResults + ", " + result + ")"); EList<OperatorInput> inputs = result.getOperatorInputs(); boolean cycleDetected = false; for (Iterator<OperatorInput> it = inputs.iterator(); it.hasNext() && !cycleDetected;) { OperatorInput operatorInput = it.next(); cycleDetected = cycleDetected(processedResults, operatorInput); } return cycleDetected; } /** * Navigate through the model from one result to detect if there is a cycle. * @param processedResults result already processed during this navigation. * @param input the input to examine. * @return a boolean indicating if a cycle has been detected. */ private static boolean cycleDetected(List<Result> processedResults, OperatorInput input) { System.out.println("cycleDetected(" + processedResults + ", " + input + ")"); boolean cycleDetected = false; if (input != null) { Result nextResult = input.getOperator().getOutput().getResult(); if (nextResult != null) { // A cycle is detected if we meet once again the first result in the stack cycleDetected = processedResults.get(0) == nextResult; // If no cycle is detected at this point, we proceed with the next result if (!cycleDetected) { // but only if we haven't already met this result (which would mean that // our diagram contains a cycle but for a different result from the // one on which we are working for this time, ie. processedResults.get(0)). if (!processedResults.contains(nextResult)) { processedResults.add(nextResult); cycleDetected = cycleDetected(processedResults, nextResult); } } } } return cycleDetected; } }
Now we can implement the constraint :
/Math.diagram/src/jfb/examples/gmf/math/diagram/providers/MathValidationProvider.java
validate
method of the Adapter1
inner class and modify it like this (do not forget to add NOT
after @generated
) :/** * @generated NOT */ public IStatus validate(IValidationContext ctx) { Edge edge = (Edge) ctx.getTarget(); Result result = (Result) edge.getTarget().getElement(); boolean cycleDetected = CycleDetectionHelper.cycleDetected(result); return cycleDetected ? ctx.createFailureStatus(edge) : ctx.createSuccessStatus(); }
As we want validation to be performed whenever our diagram is saved, we must add the following line in the doSaveDocument(IProgressMonitor monitor, Object element, IDocument document, boolean overwrite)
method of our MathDocumentProvider
class (located in the jfb.examples.gmf.math.diagram.part
package) :
ValidateAction.runValidation((View) document.getContent());
If you start eclipse, modify you diagram and save it, you should get this result (an error icon appears on all the operator output to result links of the cycle) :
Now that we are able to detect cycles, we can add a feature that will compute automatically the operations.
To do that, we will listen to the model change notifications, and more precisely :
With GMF, model changes notification are easy to handle. It is possible to simply override the EditParts handleNotificationEvent(Notification notification)
methods.
Before we add these handles, we first add a utility class that will help use to compute our operations :
jfb.examples.gmf.math.diagram.edit.parts.custom
package, add a class named AutomaticComputationHelper
and paste this code :package jfb.examples.gmf.math.diagram.edit.parts.custom; import jfb.examples.gmf.math.Number; import jfb.examples.gmf.math.Operator; import jfb.examples.gmf.math.OperatorInput; import jfb.examples.gmf.math.PlusOperator; import jfb.examples.gmf.math.Result; import jfb.examples.gmf.math.diagram.util.CycleDetectionHelper; import org.eclipse.emf.common.util.EList; public class AutomaticComputationHelper { public static void numberValueChanged(Number number) { EList<OperatorInput> inputs = number.getOperatorInputs(); for (OperatorInput operatorInput : inputs) { Operator op = operatorInput.getOperator(); updateOperatorResult(op); } } public static void operatorOutputToResultConnectionChanged(Result result) { if (result.getOperatorOutput() == null) { result.setValue(0); } else { updateOperatorResult(result.getOperatorOutput().getOperator()); } } public static void updateOperatorResult(Operator operator) { Result result = operator.getOutput().getResult(); if (result!=null) { // If there is a cycle... if (CycleDetectionHelper.cycleDetected(result)) { result.setValue(0); } else { Number in1 = operator.getInputs().get(0).getNumber(); Number in2 = operator.getInputs().get(1).getNumber(); double _in1 = in1 != null ? in1.getValue() : 0; double _in2 = in2 != null ? in2.getValue() : 0; result.setValue(operator instanceof PlusOperator ? _in1 + _in2 : _in1 - _in2); } } } }
Now we will add code to handle the model changes.
“Entry value changes” will be handled in EntryEditPart
:
/Math.diagram/src/jfb/examples/gmf/math/diagram/edit/parts/EntryEditPart.java
protected void handleNotificationEvent(Notification notification) { super.handleNotificationEvent(notification); System.out.println("entry.handleNotificationEvent(" + notification + ")"); if (notification.getNotifier() instanceof Entry) { if (notification.getFeature() instanceof EAttribute) { String attrName = ((EAttribute) notification.getFeature()).getName(); if ("value".equals(attrName)) { AutomaticComputationHelper.numberValueChanged((Number) notification.getNotifier()); } } } }
“Number to OperatorInput connections” will be handled in OperatorInputEditPart
:
/Math.diagram/src/jfb/examples/gmf/math/diagram/edit/parts/OperatorInputEditPart.java
protected void handleNotificationEvent(Notification notification) { super.handleNotificationEvent(notification); if (notification.getNotifier() instanceof OperatorInput) { if (notification.getFeature() instanceof EReference) { String refName = ((EReference) notification.getFeature()).getName(); if ("number".equals(refName)) { AutomaticComputationHelper.updateOperatorResult(((OperatorInput) notification.getNotifier()).getOperator()); } } } }
“Result value changes” and “OperatorOutput to Result connections” will be handled in ResultEditPart
:
/Math.diagram/src/jfb/examples/gmf/math/diagram/edit/parts/ResultEditPart.java
protected void handleNotificationEvent(Notification notification) { super.handleNotificationEvent(notification); if (notification.getNotifier() instanceof Result) { if (notification.getFeature() instanceof EAttribute) { String attrName = ((EAttribute) notification.getFeature()).getName(); if ("value".equals(attrName)) { AutomaticComputationHelper.numberValueChanged((Number) notification.getNotifier()); } } else if (notification.getFeature() instanceof EReference) { String refName = ((EReference) notification.getFeature()).getName(); if ("operatorOutput".equals(refName)) { AutomaticComputationHelper.operatorOutputToResultConnectionChanged((Result) notification.getNotifier()); } } } }
If you start eclipse, you should now get a “full featured” operator diagram editor !
I hope that this material will be helpful for you. If you want to support it, your help is welcome :
Discussion
Thanks for the great tutorial! It helped me a lot!
Regards, Dinko
If you want to implement ports/pins or any kind of node attached to the side of another node, you might want to try the Affixed Parent Side property. It's much easier with no coding. In this example the Operator input/output nodes should have this property.
Regards, Dinko
Yes you are right ! Thank you for the tip !
Regards,
JFB
Hello,
At first I wanted to congratulate you on your various tutoriels on GMF. They allowed me to take in in hand this tool and to arrive at a minimum result…
I adapted your explanations to the fact that I wish to realize, but in the stage of modification of createFigure, I don't find the class equivalent to OperatorCompartmentFigureListener in my project. Could you direct me on packages it sensible to contain it.
Otherwise in the creation of the class PlusRoundedRectangle (also in MinusRoundedRectangle), you have the operation graphics.setForegroundColor( ColorConstants.black) repeated two times in succession, is it an copy-paste error or is there a particular reason in this redundancy?
I thank you for your attention and I ask you to excuse me for my bad English.
Albert, French developer…
Saddened for my first question, she shows typically that it is always necessary to read everything before putting quetions. I thus found my answer farther in the tutoriel. It is nevertheless written well. I return, I just jump through the window…
Kind Regards, Albert
Puisque tu es Français, une fois n'est pas coutume, parlons Français !
Merci pour les encouragements. Et tu as 1000 fois raisons, la double ligne positionnant la couleur du fond est une erreur de copier/coller ! Il faudra que je corrige à l'occasion !
JFB
I am having 2 silly problems first I couldn't specify the labels in “Graphical Definition Model” step but I have passed it, I don't know if this will generate a problem later? Second I cannot specify the features math.gmfmap file, “features to display” cannot be edited (I have pressed on the browse button then “Add” but nothing happens) and “Diagram label” drop down list doesn't contain anything. So I am stuck and cannot go any further in your generous tutorial. I believe that I have missed something, but I have started from the beginning and couldn't find the problem.
One more thing, I am very interested in this tool in my work to make a modeling tool, I want to draw a special figures like yours but cannot find the way to follow to do my own notations, I mean like clear steps to change my nodes and links. I would be grateful if you can tell me how to do it as steps or guidance. As hint for my problem, I want to draw circles inside circles and rectangles that contain other shapes :( can you help me.
One last question, do you have any material to enable generation of XML file from the model we draw, i.e. the circuits you have done in this tutorial could be generated to XML data file, right?
Thank you very very much.
Hello Iman,
First, if you meet some problems when you follow this tutorial, I think that it may be because :
If there is a mistake in this tutorial I would appreciate if you could tell me what… So I think it would be a good thing for you to follow the tutorial patiently from the start : this may help you not to miss any step and to detect potentiel mistakes in the tutorial.
To answer the last question : no I have no material to generate XML from the model, because there is no need of such a material as GMF / EMF natively persist the data into XML ! Just open the diagram or model file with a text editor and you will understand what I mean !
Regards,
JFB
Hello Jean,
Thank you very much for response, but I have recently discovered the problem, the classes are missing the method “Double getValue();” in the interface “Number” or just mention that you have added it at the Ecore model itself. That's why I didn't find anything related to value later. Also thank you about XML, I have tried it and it works :)
Thanks a lot for your help, but I am still trying the changing figures thing, I may need help there :) Kind regards, Iman
Wow! These are great tutorials to help people in dealing with GMF. Thank you very much for that and congratulations for all your work!!!
Indeed, I am developing an editor with GMF and your material is very useful.
In regard to this example, I was wondering if you have some information on how to, for each “Entry” or “Result”, have the label aligned in the middle and centre of the figure. Moreover, since in my case I will have a text field instead of a number, how I can have a multi-line field instead of a single line.
Thank you in advance for any suggestions you may have. Ciao
Hi,
Try to replace the FlowLayout in the gmfgraph file by a BorderLayout, add a BoderLayoutData to the label and set its
Alignment
property toCENTER
. Does it help ?JFB
Hi,
After a day of research, I finally found a way to do that! If you want to get your label in the middle of a shape, add a children 'Grid Layout' to your shape, and a children 'grid layout data' to you label, with both 'grab excess place' turn on true, and both alignment on 'center'.
Jean
Hi,
I want to ask and I really hope to answer me soon. I am confused about link mappings in gmfmap!! I am having 4 link notations that are used to connect 10 node types, i.e. there are repeating usage of these notations.
The problem is the following: I want to make sure that they are correct, but I cannot find anywhere what is the correct meanings of Diagram link, source feature, target feature, element, containment feature? What are mandatory and what are optional and how to use them?!!.
The problem was discovered when I tried to change notations of links in gmfgraph and nothing was changed, and I think the problem is in mapping. Hope to tell me a solution to my problem cause I need to deliver that asap :((
Thanks in advance.
Hello Jean-François,
Very nice tutorial! Thanks very much!
I wonder if it's possible to do something similar without validation. I think of adding an icon at the middle of the connection according properties values of ends…
Thanks in advance,
Thierry
Hmmmm… It is probably possible to do it. But maybe not easily. If you find an easy way to it, don't hesitate to share your tips by sending it to me. A new tutorial should be added. JF
Hi Jean-François,
Congratulations for those tutorials, great work! I wonder if it is possible to dispaly some swt elements,custom figures like checkboxes instead of rectangles and others deafaults figure Thanks Mahmoud
Hello Mahmoud,
In fact I don't think that it is possible. If you want to have checkboxes, you may need to create graphical components that simulates a normal checkbox behaviour. I don't think that such a feature is so simple to build…. But if you manage to do that, let me know, we should add a tutorial entry, this may interest some other people.
Regards,
JFB
Many thanks for tutorials! They are great and very useful! Though your plus sign looks like a cross:)
Regards
Hello,
First of all, thank you for all the tutorials, they were very informative. I am new to Eclipse EMF and GMF and have been assigned a project related to editor development using GMF. The meta-model is based on a language that represents long running transactions , much like PI-Calculus. I managed to create the editor, based on syntax, however I am not being able to figure out what and how to write the code for incorporating the semantics of the language.
For example, if I have 2 nodes and a link between them that indicates that it is a sequence i.e Node 2 follows Node 1, how do I show it ? (code it) As in, in the worst case I could name that link 'Tom' instead of 'Sequence',it would still connect those 2 nodes without any knowledge of what it's supposed to do.Like in this example, we made the editor aware that '+' operator would ADD and '-' would subtract two numbers. How do I do this to show sequence and parallel flow of processes ?
Also, when I create a diagram in my basic editor (no semantics), I also get an option to generate a domain model, which inherently I suppose is the instance of my meta-model, however, this generated model is FLAT, i.e. it does not have a tree structure as it should have (as opposed to the time when I create a model from the meta-model after generating the EMF genmodel). Does this problem exist because my editor is not semantically sound ?
I am sorry if I sound so random or uninformed about the topic, but I really do not know how I should proceed. I have seen tutorials that aid in editor generation but none related to semantics and Modifying the generated code. Could anyone please suggest how can I solve this ? Any help will be appreciated, suggestions, code snippets, …I need to submit this on 12/09/2011 (Monday morning).. Please..
Hello,
Gloups… I think I am too late… Two days to solve GMF problems may be…. quite few… The result you're expecting is not really clear. Do you want something to be evaluated or computed automatically as you add nodes and edges to your diagram ? Or do you want your model to be interpreted outside of your editor ?… The generated model that you get is probably flat because you have no containment references.
Regards,
JFB
Hi all,
I am requesting for some help. I need to know a simple procedure on how create an hexagon shape in .gmfgraph file. Thanks a lot in advance.
Derrick
The fourth tutorial shows how to create a custom figure through the gmfgraph model.
This is really a good tutorial. but if u make a video tutorial for Computation automatisation, it will help us, the beginners… Thank you.
I have tried to copy all the steps of creating a cycle detector, but it does not work: the code in CycleDetectionHelper is never called. Could you please navigate me what have I done wrong? On saving the diagram something does happen, but the process seem too complicated to trace (some BatchValidator is called etc.) and it does not call my code.