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.
Mathjfb.examples.gmf.mathpackage 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 CodeGenerate Edit CodeGenerate Editor CodeWe 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 NextNodes column, select (and select only) : Entry, Minusoperator, PlusOperator, ResultConnections column, select (and select only) : Entry.operatorInputs, OperatorOutput.resultFinishProcedure :
Derive label which is on the left of the Graphical Def Model rectangle.math.gmfgraph in the model folderMathDiagram and click NextNodes column, select (and select only) : Entry, Minusoperator, OperatorInput, OperatorOutput, PlusOperator, ResultConnections column, select (and select only) : Entry.operatorInputs, OperatorOutput.result, Result.operatorInputsLabel column, select : Entry.value, Result.valueFinishmath.gmfgraph fileRectangle MinusOperatorFigure node (which is under the Figure Descriptor MinusOperatorFigure) :Boder LayoutRectangle named MinusOperatorCompartmentFigureRectangle 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 nodesMinusOperatorFigureCompartmentFigure Descriptor MinusOperatorFigure for its figure and Child Access getFigureMinusOperatorCompartmentFigure for its accessorPlusOperatorFigureCompartmentFigure 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 NextNext 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 :Finishmath.gmfmap fileNode Mapping <Result/Result> add a Feature Label Mapping and set its properties :Feature to display=Number.value:EDoubleDiagram Label=Diagram Label ResultValueRead Only=trueNode Mapping <Entry/Entry> add a Feature Label Mapping and set its properties :Feature to display=Number.value:EDoubleDiagram Label=Diagram Label EntryValueRead Only=falseNode 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:OperatorInputReference 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:OperatorOutputReference 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:OperatorInputReference Child = Node mapping <OperatorInput/>Child Reference and set its properties like this : Compartment=Compartment Mapping <PlusOperatorFigureCompartment>Containment feature=Operator.outut:OperatorOutputReference Child = Node mapping <OperatorOutput/>Node Mapping <Result/Result> : Diagram Node=Node Result (ResultFigure)Tool=Creation Tool ResultNode Mapping <Entry/Entry> : Diagram Node=Node Entry (EntryFigure)Tool=Creation Tool EntryNode Mapping <MinusOperator/MinusOperator> : Diagram Node=Node MinusOperator (MinusOperatorFigure)Tool=Creation Tool MinusOperatorNode Mapping <PlusOperator/PlusOperator> : Diagram Node=Node PlusOperator (PlusOperatorFigure)Tool=Creation Tool PlusOperatorNode 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:OperatorInputDiagram Link=Connection EntryOperatorInputTool=Creation Tool Entry/Result to operator inputLink Mapping <{Result.operatorOutput:OperatorOutput}/OperatorOutputResult> :Target Feature=OperatorOutput.result:ResultDiagram Link=Connection OperatorOutputResultTool=Creation Tool Operator output to resultTransform 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 MinuOperatorMinusOperatorFigureCompartmentEditPartGen Compartment PluOperatorPlusOperatorFigureCompartmentEditPartGenerate 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.customPlusRoundedRectangle which will extend org.eclipse.draw2d.RoundedRectanglepaintFigure 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.gmfmapMapping 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 = CycleDetectorRuleMessage = A cycle has been detectedName = CycleDetectorRuleSeverity = ERRORUse In Live Mode = falseDiagram 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 = CycleDetectorConstraintLanguage = javaTransform label of the dashboardmath.gmfgenGen Diagram MathDiagrameditPart node and modify its properties in order to activate the validation decorators in the diagram : Validation decorators = trueValidation Enabled = trueGenerate 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.javavalidate 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.javaprotected 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.javaprotected 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.javaprotected 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
Alignmentproperty 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.