Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Graphical Modeling Framework/Tips"

m
(21 intermediate revisions by 6 users not shown)
Line 1: Line 1:
A collection of snippets and tips...
+
A collection of snippets and tips...  
  
= Creating New Elements And Corresponding Views =
+
= Creating New Elements And Corresponding Views =
 
+
<pre>DeviceEditPart selectedElement = ...;
<pre>
+
DeviceEditPart selectedElement = ...;
+
  
 
String compartemntsSemanticHint = NetworkVisualIDRegistry.getType(tests.mindmap.network.diagram.edit.parts.Device_ModulesCompartmentEditPart.VISUAL_ID);
 
String compartemntsSemanticHint = NetworkVisualIDRegistry.getType(tests.mindmap.network.diagram.edit.parts.Device_ModulesCompartmentEditPart.VISUAL_ID);
Line 19: Line 17:
 
CreateViewAndElementRequest req = new CreateViewAndElementRequest(viewDescriptor);
 
CreateViewAndElementRequest req = new CreateViewAndElementRequest(viewDescriptor);
 
CompoundCommand cmd = new CompoundCommand("Create 10 modules");
 
CompoundCommand cmd = new CompoundCommand("Create 10 modules");
for (int i=0; i<10; i++) {
+
for (int i=0; i&lt;10; i++) {
 
cmd.add(modulesCompartment.getCommand(req));
 
cmd.add(modulesCompartment.getCommand(req));
 
}
 
}
 
selectedElement.getDiagramEditDomain().getDiagramCommandStack().execute(cmd);
 
selectedElement.getDiagramEditDomain().getDiagramCommandStack().execute(cmd);
 
</pre>
 
</pre>
 
+
= Change Names Of Newly Created Elements =
= Change Names Of Newly Created Elements =
+
<pre>Collection results = DiagramCommandStack.getReturnValues(cmd);
 
+
<pre>
+
Collection results = DiagramCommandStack.getReturnValues(cmd);
+
 
for (Object res: results) {
 
for (Object res: results) {
 
if (res instanceof IAdaptable) {
 
if (res instanceof IAdaptable) {
 
IAdaptable adapter = (IAdaptable) res;
 
IAdaptable adapter = (IAdaptable) res;
 
View view = (View) adapter.getAdapter(View.class);
 
View view = (View) adapter.getAdapter(View.class);
if (view != null) {
+
if (view&nbsp;!= null) {
 
Module newMod = (Module)view.getElement();
 
Module newMod = (Module)view.getElement();
 
SetRequest reqSet = new SetRequest(selectedElement.getEditingDomain(),
 
SetRequest reqSet = new SetRequest(selectedElement.getEditingDomain(),
Line 45: Line 40:
 
}
 
}
 
</pre>
 
</pre>
 
+
= Create New Elements Using RecordingCommand and CanonicalEditPolicies =
= Create New Elements Using RecordingCommand and CanonicalEditPolicies =
+
 
+
 
<pre>
 
<pre>
 
 
final Device dev = (Device)((View)selectedElement.getModel()).getElement();
 
final Device dev = (Device)((View)selectedElement.getModel()).getElement();
 
TransactionalEditingDomain editingDomain = selectedElement.getEditingDomain();
 
TransactionalEditingDomain editingDomain = selectedElement.getEditingDomain();
Line 56: Line 48:
 
protected void doExecute() {
 
protected void doExecute() {
 
dev.setName("Morda13");
 
dev.setName("Morda13");
for (int i = 0; i < 5; i++) {
+
for (int i = 0; i &lt; 5; i++) {
 
Module newMod = NetworkFactory.eINSTANCE.createModule();
 
Module newMod = NetworkFactory.eINSTANCE.createModule();
 
newMod.setName("X26 - " + i);
 
newMod.setName("X26 - " + i);
Line 65: Line 57:
  
 
</pre>
 
</pre>
 +
= Remove Property Sheet altogether  =
  
= [[Image:New-small.gif]] Remove Property Sheet altogether =
+
Add next method to the generated diagram editor class (usually &lt;ModelName&gt;DiagramEditor)  
 
+
<pre> public Object getAdapter(Class type) {
Add next method to the generated diagram editor class (usually &lt;ModelName&gt;DiagramEditor)
+
 
+
<pre>
+
public Object getAdapter(Class type) {
+
 
if (type == IPropertySheetPage.class) {
 
if (type == IPropertySheetPage.class) {
 
return null;
 
return null;
Line 78: Line 67:
 
}
 
}
 
</pre>
 
</pre>
 +
= Sharing single EditingDomain instance across several diagrams  =
  
 +
'''NOTE:''' This problem (and a solution) is also discussed here: http://code.google.com/p/gmftools/wiki/SharedEditingDomain.
  
= [[Image:New-small.gif]] Sharing single EditingDomain instance across several diagrams =
+
Generated code always creates new (own) instance of EditingDomain for each opened diagram editor. Since EditingDomain owns ResourceSet, different instances of domain model elements will be loaded for each opened diagram editor. Sometimes it is important to share the same domain model instances across several diagram editors (for example to get rid of synchronization problems). This leads us to the question of sharing same instance of EditingDomain across several generated diagram editors. Below you can find step by step description of necessary modifications you have to apply to the generated code to share the same EditingDomain instance between two diagrams of different types.
  
Generated code always creates new (own) instance of EditingDomain for each opened diagram editor. Since EditingDomain owns ResourceSet, different instances of domain model elements will be loaded for each opened diagram editor. Sometimes it is important to share the same domain model instances across several diagram editors (for example to get rid of synchronization problems). This leads us to the question of sharing same instance of EditingDomain across several generated diagram editors. Below you can find step by step description of necessary modifications you have to apply to the generated code to share the same EditingDomain instance between two diagrams of different types.
+
1. Make sure you have org.eclipse.gmf.bridge.trace plugin installed as a part of GMF SDK. (It is provided with the GMF Experimental SDK.)
  
1. Make sure you have org.eclipse.gmf.bridge.trace plugin installed as a part of GMF SDK. (It is provided with the GMF Experimental SDK.)
+
2. Create diagram1.gmfmap, diagram2.gmfmap files and generate diagram1.gmfgen, diagram2.gmfgen. Ensure diagram1.trace and diagram2.trace files created.  
  
2. Create diagram1.gmfmap, diagram2.gmfmap files and generate diagram1.gmfgen, diagram2.gmfgen. Ensure diagram1.trace and diagram2.trace files created.
+
3. Modify generated diagram1.gmfgen and diagram2.gmfgen files to use different Model IDs, Plugin IDs, Editor IDs. Optionally two different diagrams could has different diagram file extensions as well:
  
3. Modify generated diagram1.gmfgen and diagram2.gmfgen files to use different Model IDs, Plugin IDs, Editor IDs. Optionally two different diagrams could has different diagram file extensions as well:
+
[[Image:Model ID.PNG]] [[Image:Plugin ID.PNG]] [[Image:Editor ID.PNG]]
  
[[Image:Model_ID.PNG]] [[Image:Plugin_ID.PNG]] [[Image:Editor_ID.PNG]]
+
<br>4. Modify diagram2.trace file to use different visualID values then specified in diagram1.trace file - the easiest way is to replace “"200”-&gt; “"210”, .., “"800” -&gt; “"810” – and regenerate diagram2.gmfgen from diagram2.gmfmap
  
 +
5. Open diagram2.gmfgen and modify visualID property for GenDiagram to make it different then visualID of GenDiagram in diagram1.gmfgen:
  
4. Modify diagram2.trace file to use different visualID values then specified in diagram1.trace file - the easiest way is to replace “"200”-> “"210”, .., “"800” -> “"810” – and regenerate diagram2.gmfgen from diagram2.gmfmap
+
[[Image:Diagram2Visual ID.PNG]]
  
5. Open diagram2.gmfgen and modify visualID property for GenDiagram to make it different then visualID of GenDiagram in diagram1.gmfgen:
+
<br>6. Ensure both diagram1.gmfgen and diagram2.gmfgen are using the same EditingDomain ID:  
  
[[Image:Diagram2Visual_ID.PNG]]
+
[[Image:EditingDomain ID.PNG]]  
  
 +
<br>7. Generate code for both diagram plugins.
  
6. Ensure both diagram1.gmfgen and diagram2.gmfgen are using the same EditingDomain ID:
+
8. In both generated&nbsp;???DocumentProvider classes substitute EditingDomain creation code:  
 
+
[[Image:EditingDomain_ID.PNG]]
+
 
+
 
+
7. Generate code for both diagram plugins.
+
 
+
8. In both generated ???DocumentProvider classes substitute EditingDomain creation code:
+
  
 
  /**
 
  /**
Line 117: Line 103:
 
  …
 
  …
  
with the following call to the central EditingDomain registry:
+
with the following call to the central EditingDomain registry:  
  
 
  /**
 
  /**
Line 133: Line 119:
 
  …
 
  …
  
NOTE-1: If you are using GMF older then 2.0 M5 you have to modify generated ???DiagramEditor.createEditingDomain() instead to simply call the superclass implementation of following method:
+
NOTE-1: If you are using GMF older then 2.0 M5 you have to modify generated&nbsp;???DiagramEditor.createEditingDomain() instead to simply call the superclass implementation of following method:  
  
 
  /**
 
  /**
Line 142: Line 128:
 
  }
 
  }
  
NOTE-2: The idea of this step is to let two connected instances of GMF editors using the same instance of EditingDomain. You can easily create your own EditingDomain registry/accessor and call it from the corresponding methods instead of calling TransactionalEditingDomain.Registry.
+
NOTE-2: The idea of this step is to let two connected instances of GMF editors using the same instance of EditingDomain. You can easily create your own EditingDomain registry/accessor and call it from the corresponding methods instead of calling TransactionalEditingDomain.Registry.  
  
You can see an example of alternative ???DocumentProvider modification in the following article: http://www.eclipse.org/articles/Article-Integrating-EMF-GMF-Editors/index.html#extending_the_editor_input.
+
You can see an example of alternative&nbsp;???DocumentProvider modification in the following article: http://www.eclipse.org/articles/Article-Integrating-EMF-GMF-Editors/index.html#extending_the_editor_input.  
  
 +
<br>9. Declare EditingDomain with the shared EditingDomain ID (see step 5.) inside plugin.xml for generated for diagram1:
  
9. Declare EditingDomain with the shared EditingDomain ID (see step 5.) inside plugin.xml for generated for diagram1:
+
  &lt;!-- gmf generator persistent region begin --&gt;
 
+
     &lt;extension
  &lt;!-- gmf generator persistent region begin -->
+
           point="org.eclipse.emf.transaction.editingDomains"&gt;
     <extension
+
       &lt;editingDomain
           point="org.eclipse.emf.transaction.editingDomains">
+
       <editingDomain
+
 
             factory="org.eclipse.emf.workspace.WorkspaceEditingDomainFactory"
 
             factory="org.eclipse.emf.workspace.WorkspaceEditingDomainFactory"
             id="SharedEditingDomain"/>
+
             id="SharedEditingDomain"/&gt;
     </extension>
+
     &lt;/extension&gt;
  &lt;!-- gmf generator persistent region end -->
+
  &lt;!-- gmf generator persistent region end --&gt;
  
NOTE: This step is necessary to initialize TransactionalEditingDomain.Registry used above with the proper EditingDomain id. If you are not using TransactionalEditingDomain.Registry to access/create EditingDomain instance (e.g. following the article: http://www.eclipse.org/articles/Article-Integrating-EMF-GMF-Editors/index.html#extending_the_editor_input) then you should skip this step.
+
NOTE: This step is necessary to initialize TransactionalEditingDomain.Registry used above with the proper EditingDomain id. If you are not using TransactionalEditingDomain.Registry to access/create EditingDomain instance (e.g. following the article: http://www.eclipse.org/articles/Article-Integrating-EMF-GMF-Editors/index.html#extending_the_editor_input) then you should skip this step.  
  
10. Only one metamodelType could be registered for each EClass in scope of one EditingDomain. During this step you have to replace by specializationType declaration all the metamodelType declarations inside plugin.xml for diagram2 duplicating metamodelType declarations inside plugin.xml for diagram1 (having the same eclass attribute). Following part of plugin.xml:
+
10. Only one metamodelType could be registered for each EClass in scope of one EditingDomain. During this step you have to replace by specializationType declaration all the metamodelType declarations inside plugin.xml for diagram2 duplicating metamodelType declarations inside plugin.xml for diagram1 (having the same eclass attribute). Following part of plugin.xml:  
  
  <'''metamodelType'''
+
  &lt;'''metamodelType'''
 
       id="shape.diagram2.Diagram_1001"
 
       id="shape.diagram2.Diagram_1001"
 
       name="Undefined"
 
       name="Undefined"
 
       kind="org.eclipse.gmf.runtime.emf.type.core.IHintedType"
 
       kind="org.eclipse.gmf.runtime.emf.type.core.IHintedType"
 
       '''eclass'''="Diagram"
 
       '''eclass'''="Diagram"
       '''edithelper'''="'''shape.diagram.edit.helpers.DiagramEditHelper'''">
+
       '''edithelper'''="'''shape.diagram.edit.helpers.DiagramEditHelper'''"&gt;
     <param name="semanticHint" value="1001"/>
+
     &lt;param name="semanticHint" value="1001"/&gt;
  </'''metamodelType'''>
+
  &lt;/'''metamodelType'''&gt;
  
Should be replaced with:
+
Should be replaced with:  
  
  <'''specializationType'''
+
  &lt;'''specializationType'''
 
       id="shape.diagram2.Diagram_1001"
 
       id="shape.diagram2.Diagram_1001"
 
       name="Undefined"
 
       name="Undefined"
 
       kind="org.eclipse.gmf.runtime.emf.type.core.IHintedType"
 
       kind="org.eclipse.gmf.runtime.emf.type.core.IHintedType"
       '''edithelperadvice'''="'''org.eclipse.gmf.runtime.emf.type.core.edithelper.AbstractEditHelperAdvice'''">
+
       '''edithelperadvice'''="'''org.eclipse.gmf.runtime.emf.type.core.edithelper.AbstractEditHelperAdvice'''"&gt;
     <'''specializes''' id="shape.diagram1.Diagram_1000"/>
+
     &lt;'''specializes''' id="shape.diagram1.Diagram_1000"/&gt;
     <param name="semanticHint" value="1001"/>
+
     &lt;param name="semanticHint" value="1001"/&gt;
  </'''specializationType'''>
+
  &lt;/'''specializationType'''&gt;
  
''<specializes>'' attribute for newly creates specializationTypes should point to the corresponding metamodeType id from plugin.xml generated for diagram1.
+
''&lt;specializes&gt;'' attribute for newly creates specializationTypes should point to the corresponding metamodeType id from plugin.xml generated for diagram1.  
  
11. Patch plugin.xml generated for diagram2 and specify the same clientContext for elementTypeBindings extension point as it is specified for diagram1. Instead of:
+
11. Patch plugin.xml generated for diagram2 and specify the same clientContext for elementTypeBindings extension point as it is specified for diagram1. Instead of:  
  
  <extension point="org.eclipse.gmf.runtime.emf.type.core.elementTypeBindings">
+
  &lt;extension point="org.eclipse.gmf.runtime.emf.type.core.elementTypeBindings"&gt;
     <clientContext id="'''Diagram2_IDClientContext'''">
+
     &lt;clientContext id="'''Diagram2_IDClientContext'''"&gt;
 
       …
 
       …
     </clientContext>
+
     &lt;/clientContext&gt;
     <binding context="'''Diagram2_IDClientContext'''">
+
     &lt;binding context="'''Diagram2_IDClientContext'''"&gt;
 
       …
 
       …
     </binding>
+
     &lt;/binding&gt;
  </extension>
+
  &lt;/extension&gt;
  
type:
+
type:  
  
  <extension point="org.eclipse.gmf.runtime.emf.type.core.elementTypeBindings">
+
  &lt;extension point="org.eclipse.gmf.runtime.emf.type.core.elementTypeBindings"&gt;
     <clientContext id="'''Diagram1_IDClientContext'''">
+
     &lt;clientContext id="'''Diagram1_IDClientContext'''"&gt;
 
       …
 
       …
     </clientContext>
+
     &lt;/clientContext&gt;
     <binding context="'''Diagram1_IDClientContext'''">
+
     &lt;binding context="'''Diagram1_IDClientContext'''"&gt;
 
       …
 
       …
     </binding>
+
     &lt;/binding&gt;
  </extension>
+
  &lt;/extension&gt;
  
12. To correctly unload (model) resources associated with diagram you can add method to generated ???DocumentProvider:
+
12. To correctly unload (model) resources associated with diagram you can add method to generated&nbsp;???DocumentProvider:  
  
 
  protected void disposeElementInfo(Object element, ElementInfo info) {
 
  protected void disposeElementInfo(Object element, ElementInfo info) {
Line 213: Line 198:
 
  }
 
  }
  
= Making figures sensitive to attributes of semantic elements =
+
= Making figures sensitive to attributes of semantic elements =
  
This is about how to change diagram editor generated by GMF to make figures sensitive to semantic element's attributes' values. Changes to attributes' values should be automatically detected and reflected in figure's look. All you have to do is to make a little change in ''*EditPart'' class. I'm going to change edit part for semantic model's Port element, so I will edit ''MY.PACKAGE.diagram.edit.parts.PortEditPart'' class.
+
'''TASK:'''  
  
We will add to the figure a new method which changes figure's parameters according to attributes of semantic element. Add following method to ''PortFigure'' class which is ''PortEditPart'' 's inner class:
+
Change a diagram editor generated by GMF to make its figures sensitive to changes of semantic elements’ attributes’ values. Changes of attributes’ values should be automatically detected and reflected in figures’ look.
<pre>
+
 
public void updateFace() {
+
'''SOLUTION:'''
 +
 
 +
You have to make a little change in ''*EditPart'' class. I’m going to change edit part for the semantic model element named '''Port''', so I have to edit ''my.package''.diagram.edit.parts.'''''PortEditPart''''' class.
 +
 
 +
'''1.''' Add a new method to the '''''PortFigure''''' class, which is a ''PortEditPart''’s inner class:  
 +
<pre> public void updateFace() {
 
Port port = (Port) ((Node) PortEditPart.this.getModel()).getElement();
 
Port port = (Port) ((Node) PortEditPart.this.getModel()).getElement();
  
 
// set line width according to number of some children
 
// set line width according to number of some children
 
int lineWidth = 1;
 
int lineWidth = 1;
if (port.getLogicalUnits().size() > 1) {
+
if (port.getLogicalUnits().size() &gt; 1) {
 
lineWidth = 2;
 
lineWidth = 2;
 
}
 
}
Line 231: Line 221:
 
// update tooltip
 
// update tooltip
 
String tooltipText;
 
String tooltipText;
if (port.getDescription() != null
+
if (port.getDescription()&nbsp;!= null
&& port.getDescription().length() > 0) {
+
&amp;&amp; port.getDescription().length() &gt; 0) {
 
tooltipText = port.getDescription();
 
tooltipText = port.getDescription();
 
} else {
 
} else {
Line 244: Line 234:
 
}
 
}
 
</pre>
 
</pre>
 +
The ''updateFace'' method makes actual changes to the figure’s look. This example implementation sets the line’s width according to the number of Port’s children (''port.getLogicalUnits().size()'') and sets the tool-tip text to the value of element’s attribute named ''description''.
  
Change ''PortFigure'' 's constructor to make it use new method.
+
Then you have to hook up this method in two places: ''PortFigure''’s constructor to update the look when editor starts and the ''handleNotificationEvent'' method to react to live changes.  
  
<pre>
+
'''2.''' Change ''PortFigure''’s constructor to make it use new method.
/**
+
<pre> /**
 
* @generated NOT
 
* @generated NOT
 
*/
 
*/
Line 264: Line 255:
 
}
 
}
 
</pre>
 
</pre>
 
+
'''3.''' Override handleNotificationEvent(Notification notification) method in PortEditPart.  
The last thing is to hook new method to changes in semantic element. To do that you have to override in PortEditPart ''handleNotificationEvent(Notification notification)'' method. Add to the class something like this:
+
<pre> @Override
<pre>
+
@Override
+
 
protected void handleNotificationEvent(Notification notification) {
 
protected void handleNotificationEvent(Notification notification) {
 
if (notification.getNotifier() instanceof Port) {
 
if (notification.getNotifier() instanceof Port) {
Line 275: Line 264:
 
}
 
}
 
</pre>
 
</pre>
 +
Without this method you would need to restart editor to reflect element's changes in figure parameters.
  
Without this method you would need to restart editor to reflect element's changes in figure parameters.
+
Tested on GMF version 2.0. → [http://serdom.eu/ser/2009/01/21/eclipse-gmf-making-figures-sensitive-to-attributes-of-semantic-elements Original post]
  
&rarr; Original version of this section, with colorized source code, can be found [http://serdom.szn.pl/serwiki/Eclipse/GMF/Making_figures_sensitive_to_attributes_of_semantic_elements here]
+
= Printing multiple diagrams  =
  
= Printing multiple diagrams =
+
See this [http://cyberpen.wordpress.com/2008/07/27/print-all-gmf-diagrams-with-a-single-click/ blog posting] by Anoop Menon.  
See this [http://cyberpen.wordpress.com/2008/07/27/print-all-gmf-diagrams-with-a-single-click/ blog posting] by Anoop Menon.
+
  
= Rotating a border item =
+
= Rotating a border item =
From a newsgroup posting by Jaap Reitsma on 01/31/08:
+
  
I have fond a solution that rotates my bordered triangle (always pointing inwards in a container) as well as being initialized properly when opening the diagram. In my first attempt I used MyTriangleEditPart.refreshBounds() to manipulate the orientation. Although it works when dragging the triangle around the container, it does not get initialized properly as the refreshBounds of the bordered item is called when the layout of the parent has not been done yet.
+
From a newsgroup posting by Jaap Reitsma on 01/31/08:
  
The seemingly elegant solution is to hook into the layout of the border item container, i.e. the parent of my bordered triangle figure. The code I have used follows below:
+
I have fond a solution that rotates my bordered triangle (always pointing inwards in a container) as well as being initialized properly when opening the diagram. In my first attempt I used MyTriangleEditPart.refreshBounds() to manipulate the orientation. Although it works when dragging the triangle around the container, it does not get initialized properly as the refreshBounds of the bordered item is called when the layout of the parent has not been done yet.
<pre>
+
 
/* (non-Javadoc)
+
The seemingly elegant solution is to hook into the layout of the border item container, i.e. the parent of my bordered triangle figure. The code I have used follows below:  
 +
<pre>/* (non-Javadoc)
 
* @see  
 
* @see  
 
org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart#activate()
 
org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart#activate()
Line 334: Line 323:
 
}
 
}
 
</pre>
 
</pre>
The setRotationInDegrees is a method in my custom RotableScalablePolygon which is actually a copy of the implementation of the inner figure generated by GMF when using a scalable polygon as figure.
+
The setRotationInDegrees is a method in my custom RotableScalablePolygon which is actually a copy of the implementation of the inner figure generated by GMF when using a scalable polygon as figure.  
 +
 
 +
= [[Image:New-small.gif]] Different Figures for one Ecore class  =
 +
 
 +
'''TASK:'''
 +
 
 +
You want to have different looking diagram figures but all backed by the same Ecore class.
 +
 
 +
'''SOLUTION:'''
 +
 
 +
  Class: CommonClass
 +
  Attribute: CommonClass.type:CommonClassType
 +
 
 +
  Enumeration: CommonClassType
 +
  Values: TypeA = 0, TypeB = 1
 +
 
 +
For CommonClass of CommonClassType.TypeA display Figure 'A' if CommonClassType.TypeB disaply Figure B.
 +
 
 +
  My.gmfmap
 +
  Mapping
 +
    Top Node Reference              '''Diagram Node''' ''Node TypeAFigure''
 +
      Node Mapping
 +
        Constraint                  '''Body''' ''self.type = CommonClassType::TypeA'', '''Language''' ''ocl''
 +
        Feature Seq Initalizer      '''Element Class''' ''CommonClass''
 +
          Feature Value Spec        '''Feature''' ''CommonClass.type:CommonClassType''
 +
            Value Expression        '''Body''' ''CommonClass.type::TypeA'', '''Language''' ''ocl''
 +
 
 +
    Top Node Reference              '''Diagram Node''' ''Node TypeBFigure''
 +
      Node Mapping
 +
        Constraint                  '''Body''' ''self.type = CommonClassType::TypeB'', '''Language''' ''ocl''
 +
        Feature Seq Initalizer      '''Element Class''' ''CommonClass''
 +
          Feature Value Spec        '''Feature''' ''CommonClass.type:CommonClassType''
 +
            Value Expression        '''Body''' ''CommonClass.type::TypeB'', '''Language''' ''ocl''
 +
 +
 
 +
You will of course have to define two figures, two creation tool items, etc..
 +
 
 +
= Setting Initial Position of a Node =
 +
 
 +
'''TASK:''' You want to set the initial position of a node, but the user should still be able to move the node.
 +
 
 +
'''SOLUTION:''' There is more than one solution. The following solution overrides activate() in XYZEditPart.
 +
<pre>/**
 +
* @generated NOT
 +
*/
 +
@Override
 +
public void activate() {
 +
super.activate();
 +
 +
AbstractEMFOperation emfOp = new AbstractEMFOperation(getEditingDomain(), "Location setting") {
 +
 
 +
@Override
 +
protected IStatus doExecute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
 +
Location lc = (Location) ((Node) getModel()).getLayoutConstraint();
 +
lc.setX(x);
 +
lc.setY(y);
 +
return Status.OK_STATUS;
 +
}
 +
 
 +
};
 +
 
 +
IStatus status;
 +
 
 +
try {
 +
status = OperationHistoryFactory.getOperationHistory().execute(emfOp, null, null);
 +
} catch (ExecutionException e) {
 +
status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Setting location failed", e);
 +
}
 +
 
 +
if (status.getCode() == IStatus.WARNING || status.getCode() == IStatus.ERROR) {
 +
Activator.getDefault().getLog().log(status);
 +
}
 +
}
 +
</pre>
 +
<br>
 +
 
 +
= Setting default Routing for connections =
 +
 
 +
'''TASK:''' You want to set the routing of the connections to be Rectilinear (90 degrees angles) or Tree style, as default.
 +
 
 +
'''SOLUTION:''' On your connection's EditPart, modify the createConnectionFigure method to:
 +
<pre>/**
 +
* @generated NOT
 +
*/
 +
protected Connection createConnectionFigure() {
 +
    XYZFigure figure = new XYZFigure();
 +
   
 +
    AbstractEMFOperation emfOp = new AbstractEMFOperation(getEditingDomain(), "line routing setting") {
 +
        @Override
 +
        protected IStatus doExecute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
 +
        RoutingStyle style = (RoutingStyle) ((View) getModel()).getStyle(NotationPackage.Literals.ROUTING_STYLE);
 +
        style.setRouting(Routing.RECTILINEAR_LITERAL);  //or Routing.TREE_LITERAL           
 +
        return Status.OK_STATUS;
 +
        }
 +
    };
 +
 
 +
    try {         
 +
        OperationHistoryFactory.getOperationHistory().execute(emfOp, null, null);
 +
    } catch (ExecutionException e) {}
 +
 
 +
    return figure;
 +
}
 +
</pre>
 +
<br>
 +
 
 +
If you want to set the routing globally for all of your connections you can modify the DiagramConnectionsPreferencePage class in the *.diagram.preferences package with the following code.
 +
<pre>
 +
/**
 +
* Initializes the default preference values for this preference store.
 +
*
 +
* @param preferenceStore
 +
*            the preference store
 +
*
 +
* @generated NOT
 +
*/
 +
public static void initDefaults(IPreferenceStore preferenceStore) {
 +
  preferenceStore.setDefault(IPreferenceConstants.PREF_LINE_STYLE, Routing.RECTILINEAR);
 +
}
 +
</pre>
 +
<br>
 +
 
 +
= Removing line drawn at top of compartments  =
 +
'''TASK:''' You want to hide the line that is always displayed ontop of a compartment
 +
 
 +
'''SOLUTION:''' Add the following line setBorder(null) to the createFigure method of your compartment's EditPart. It will look like this:
 +
<pre>
 +
/**
 +
* @generated NOT
 +
*/
 +
public IFigure createFigure() {
 +
  ResizableCompartmentFigure result = (ResizableCompartmentFigure) super.createFigure();
 +
  result.setTitleVisibility(false);
 +
  //changed code
 +
  result.setBorder(null);
 +
  return result;
 +
}
 +
</pre>
 +
 
 +
see [http://www.eclipse.org/forums/index.php?t=msg&th=202222&start=0&S=5059bd448f2bc04757ebccb13e07a184 forums post].
 +
 
 +
 
 +
 
 +
= Implicit node in the middle creation  =
 +
'''TASK:''' You want to create ER diagram like connections. This means draw a connection from node A to B and a node C will be automatically created in the middle of the new connection.
 +
 
 +
'''SOLUTION:''' Well, ok then, I am going to post the proposed solution first and then would be happy to discuss at the [http://www.eclipse.org/forums/index.php?t=msg&S=57cd7f15d71268396073a2e9cd913829&th=201023&goto=642541#msg_642541 forum].
 +
 
 +
[[Image:Implicit-in-the-middle.PNG]]
 +
 
 +
As you probably got by now there are no one-line solutions to this use case. The only good news is that I am going to try to push this solution to GMF-tooling, and with a bit of luck it will be possible to define and generate the code below in Indigo. To be honest, it is one of the reason for me to describe this somewhat long story here.
 +
 
 +
The complete code is available in the public SVN repoistory at http://www.assembla.com/code/gmf-association-class-links/subversion/nodes
 +
 
 +
There are 4 plugins, 2 of them are just pure EMF plugins (inthemiddle and inthemiddle.edit, nothing interesting there), + supporting .gmf plugin containing helper code, GMF artifacts and custom templates, and finally the resulting .diagram plugin.
 +
 
 +
There is very simple diagram definition in the *.gmf/gmf/def/ folder. There is nothing special here, except that we set the incoming link creations to true for both GenLink’s in the gmfgen model, to allow user to start drawing of the link from the Rectangle instead of not-yet-created Rhomb.
 +
 
 +
Also I was somewhat lazy to play with gmfgraph today, so don’t be surprised that Rhomb model elements are rendered as rounded rectangles at the diagram :). 
 +
 
 +
Proposed solution consists of 2 parts: 
 +
a) modifying the GraphicalNodeEditPolicy (GNEP) to collect and pass the diagram information into the semantic creation command and
 +
b) implementing the composite creation command itself
 +
 
 +
a) So we are going to replace org.eclipse.gmf.runtime.diagram.ui.editpolicies.GraphicalNodeEditPolicy with tweaked ITMGraphicalNodeEditPolicy from the *.gmf plugin. The changes made are as follows: 
 +
 
 +
- Default GNEP packs the CompositeTransactionalCommand and then uses the ordering to access the different parts of the pack. E.g, lines 508-515 in the default GNEP looks like:
 +
<pre>
 +
CompositeCommand cc = (CompositeCommand) proxy.getICommand();
 +
//....
 +
Iterator commandItr = cc.iterator();
 +
commandItr.next(); //0
 +
SetConnectionEndsCommand sceCommand = (SetConnectionEndsCommand) commandItr.next(); //1
 +
sceCommand.setNewTargetAdaptor(new EObjectAdapter(((IGraphicalEditPart) targetEP).getNotationView()));
 +
SetConnectionAnchorsCommand scaCommand = (SetConnectionAnchorsCommand) commandItr.next(); //2
 +
//....
 +
</pre>
 +
 
 +
This is clearly bad, so the changed code introduces ITMCreateLinkCommand with explicit access to its parts. It also has a code to register the results pack in the parameters of original request, so our semantic command will be able to access the whole pack at the execution time.
 +
 
 +
The changed code also computes an additional ITMCreateParameters describing the diagram information about the link source and target, but, while useful in general, it may be out of scope of this particular use case.
 +
 
 +
To register all the ITM* staff above in the generated diagram I used the following custom templates (*.gmf plugin, /gmf/templates/...)
 +
 
 +
<pre>
 +
itm::graphicalnodeep::Guard.qvto
 +
    it is the guard that prevents generation of the new code for unrelated diagrams (I had more than 1):
 +
 
 +
helper requiresSpecificGraphicalNodeEditPolicy(diagram : genModel::GenDiagram) : Boolean {
 +
 
 +
    return
 +
 
 +
        'your.diagram.plugin.id = diagram.editorGen.plugin.iD
 +
 
 +
}
 +
 
 +
This guard allows every other affected template to have something like
 +
 
 +
«EXTENSION itm::graphicalnodeep::Guard»
 +
 
 +
...
 +
 
 +
«IF requiresSpecificGraphicalNodeEditPolicy(self.getDiagram())-»
 +
 
 +
//....some changes
 +
 
 +
«ENDIF-»
 +
 
 +
</pre>
 +
 
 +
which, after all, allowed me to find all the affected parts and write this post :)
 +
 
 +
itm::graphicalnodeep::graphicalNodeEditPolicy.xpt - here comes the code to register the tweaked version of the GNEP
 +
aspects::impl::diagram::editparts::LinkEditPart.xpt and aspects::impl::diagram::editparts::NodeEditPart.xpt - just registering the tweaked editpolicies for all nodes and links by calling
 +
 
 +
<pre>
 +
«EXPAND itm::graphicalnodeep::graphicalNodeEditPolicy::install-»
 +
</pre>
 +
 
 +
finally, if you also have notes at your diagram you also need to replace the NoteEditPart because in its default GMF version it expects the default GNEP. This is done by:
 +
 
 +
<pre>
 +
aspects::xpt::diagram::editparts::EditPartFactory.xpt
 +
 
 +
«IF requiresSpecificGraphicalNodeEditPolicy(self)-»
 +
      if (org.eclipse.gmf.runtime.diagram.core.util.ViewType.NOTE.equals(view.getType())) {
 +
 
 +
return new «EXPAND itm::graphicalnodeep::graphicalNodeEditPolicy::noteEditPartFQN»(view);
 +
      }
 +
      «ENDIF-»
 +
 
 +
and
 +
aspects::xpt::providers::extensions.xpt
 +
 
 +
«AROUND extensions FOR gmfgen::GenDiagram-»
 +
 
 +
    «targetDef.proceed()»
 +
 
 +
«IF requiresSpecificGraphicalNodeEditPolicy(self)-»
 +
 
 +
    «EXPAND noteEditPartProvider»
 +
 
 +
«ENDIF-»
 +
 
 +
«ENDAROUND»
 +
 
 +
 
 +
«DEFINE noteEditPartProvider FOR gmfgen::GenDiagram-»
 +
 
 +
<extension point="org.eclipse.gmf.runtime.diagram.ui.editpartProviders" id="noteep-provider">
 +
 
 +
    «EXPAND xpt::Common::xmlGeneratedTag»
 +
 
 +
    <editpartProvider class="«getEditPartProviderQualifiedClassName()»">
 +
 
 +
        <Priority name="Low"/>
 +
 
 +
        <object class="org.eclipse.gmf.runtime.notation.Node" id="generated-note">
 +
 
 +
            <method name="getType()" value="Note"/>
 +
 
 +
        </object>
 +
 
 +
        <context views="generated-note"/>
 +
 
 +
    </editpartProvider>
 +
 
 +
</extension>
 +
 
 +
«ENDDEFINE»
 +
</pre>
 +
 
 +
Ok, at this point we can regenerate the diagram, and the new one will install the tweaked GNEPs to all the right places. Now its time time to use it:
 +
 
 +
(*.gmf plugin)AbstractImplicitMiddleElementLinkCreateCommand.java is the base class for all creation commands for use cases like that. It does not have dependencies to our diagram and as such prepared to be moved somewhere into GMF runtime.
 +
 
 +
IElementTypeAwareAdapter.java, EObjectAndElementTypeAdapter.java and ElementTypeOnlyAdapter are the helper interface and 2 implementations to describe the concrete element type for node in the middle and links.
 +
 
 +
and finally the concrete implementation ImplicitRhombCreateCommand (*.diagram plugin, custom-src root) that contains the configuration (what are the visual ids of links, what is the node in the middle, etc) specific for my own diagram.
 +
 
 +
Finally, this all is bound together by the @generated NOT code in the RectangleItemSemanticEditPolicy -- this is the only place in the code where @generated and custom code lives together:
 +
 
 +
<pre>
 +
/**
 +
* @generated NOT
 +
*/
 +
protected Command getCompleteCreateRelationshipCommand(CreateRelationshipRequest req) {
 +
    if (InthemiddleElementTypes.RhombRight_4002 == req.getElementType() && req.getSource() instanceof Rectangle) {
 +
        return getGEFWrapper(new ImplicitRhombCreateCommand(req, req.getSource(), req.getTarget()));
 +
    }
 +
    // escape to generated code otherwise
 +
    return getCompleteCreateRelationshipCommandGen(req);
 +
}
 +
</pre>
 +
 
 +
Thats it, if you run this code in the helios, in the generated diagram it should be possible to choose the Link tool at palette, draw the link directly from Artist to Song rectangles and have the ‘rhomb’ auto-created in the middle:
 +
 
 +
 
 +
= Using EEF and GMF  =
 +
 
 +
'''TASK:''' You want to use EEF and GMF together
 +
 
 +
'''Solution:''' from http://www.eclipse.org/forums/index.php/t/466110/
 +
 
 +
GMF does not know which plugin provides the property tabs for the editor, it just declares the contributor ID for someone who wants to provide its contents here. So there is no code in diagram plugin to change. Instead, you need to reference this diagram controbutor ID from EEF plugin:
 +
 
 +
*in the diagram plugin. search for: org.eclipse.ui.views.properties.tabbed.propertyContributor extension, it will be like this:
 +
<pre>&lt;extension point="org.eclipse.ui.views.properties.tabbed.propertyContributor" id="prop-contrib"&gt;
 +
&lt;?gmfgen generated="true"?&gt;
 +
&lt;propertyContributor
 +
contributorId="org.eclipse.gmf.examples.ocldriven.diagram"
 +
labelProvider="org.eclipse.gmf.examples.ocldriven.toe.diagram.sheet.TOESheetLabelProvider"&gt;
 +
&lt;propertyCategory category="domain"/&gt;
 +
&lt;propertyCategory category="visual"/&gt;
 +
&lt;propertyCategory category="extra"/&gt;
 +
&lt;/propertyContributor&gt;
 +
&lt;/extension&gt;</pre>
 +
*note the contributorId (contributorId="org.eclipse.gmf.examples.ocldriven.diagram")
 +
*note one of the existing categories ( &lt;propertyCategory category="domain"/&gt;)
 +
*copy/paste the "org.eclipse.ui.views.properties.tabbed.propertyTabs" and "org.eclipse.ui.views.properties.tabbed.propertySections" extensions from EEF-generated plugin.xml fragment to the real plugin.xml for edit plugin (if you followed the EEF tutorial, you may already have one copy of these 2 extensions there for EMF tree editor)
 +
*in the BOTH copy/pasted duplicated extensions replace the EEF contributorID to the GMF contributor id (first bold from step1), so in my sample it would be:
 +
<pre>&lt;extension point="org.eclipse.ui.views.properties.tabbed.propertySections"&gt;
 +
&lt;propertySections contributorId="org.eclipse.gmf.examples.ocldriven.diagram"&gt;
 +
...
 +
</pre><pre>&lt;extension point="org.eclipse.ui.views.properties.tabbed.propertyTabs"&gt;
 +
&lt;propertyTabs contributorId="org.eclipse.gmf.examples.ocldriven.diagram"&gt;
 +
...
 +
 
 +
</pre>
 +
*in the copy/pasted "org.eclipse.ui.views.properties.tabbed.propertyTabs" extension replace the category="&lt;whatever&gt;" by the actual category from GMF (second bold from step1 -- e.g "domain"):
 +
<pre> &lt;propertyTabs
 +
            contributorId="org.eclipse.gmf.examples.ocldriven.diagram"&gt;
 +
        &lt;propertyTab
 +
              id="Base"
 +
              label="EEF-Base"
 +
              category="domain"&gt;
 +
        &lt;/propertyTab&gt;
 +
        ....
 +
 
 +
</pre>
 +
I just made exactly these steps for GMF-T OCL/driven sample, and that all I needed to get an attached screenshot.
 +
 
 +
[[Image:EEF-GMF-Sample.png]]
  
[[Category:Modeling]][[Category:Snippets]]
+
[[Category:Modeling]] [[Category:Snippets]] [[Category:GMF]]

Revision as of 09:11, 3 April 2013

A collection of snippets and tips...

Creating New Elements And Corresponding Views

DeviceEditPart selectedElement = ...;

String compartemntsSemanticHint = NetworkVisualIDRegistry.getType(tests.mindmap.network.diagram.edit.parts.Device_ModulesCompartmentEditPart.VISUAL_ID);
Device_ModulesCompartmentEditPart modulesCompartment = (Device_ModulesCompartmentEditPart)selectedElement.getChildBySemanticHint(compartemntsSemanticHint);

IElementType type = NetworkElementTypes.Module_3001;

ViewAndElementDescriptor viewDescriptor = new ViewAndElementDescriptor(
		new CreateElementRequestAdapter(new CreateElementRequest(type)),
		Node.class,
		((IHintedType) type).getSemanticHint(), 
		selectedElement.getDiagramPreferencesHint());

CreateViewAndElementRequest req = new CreateViewAndElementRequest(viewDescriptor);
CompoundCommand cmd = new CompoundCommand("Create 10 modules");
for (int i=0; i<10; i++) {
	cmd.add(modulesCompartment.getCommand(req));
}
selectedElement.getDiagramEditDomain().getDiagramCommandStack().execute(cmd);

Change Names Of Newly Created Elements

Collection results = DiagramCommandStack.getReturnValues(cmd);
for (Object res: results) {
	if (res instanceof IAdaptable) {
		IAdaptable adapter = (IAdaptable) res;
		View view = (View) adapter.getAdapter(View.class);
		if (view != null) {
			Module newMod = (Module)view.getElement();
			SetRequest reqSet = new SetRequest(selectedElement.getEditingDomain(),
					newMod, NetworkPackage.eINSTANCE.getNamedElement_Name(),
					"ModX");
			SetValueCommand operation = new SetValueCommand(reqSet);
			selectedElement.getDiagramEditDomain().getDiagramCommandStack().execute(new 
					ICommandProxy(operation));
		}
	}
}

Create New Elements Using RecordingCommand and CanonicalEditPolicies

final Device dev = (Device)((View)selectedElement.getModel()).getElement();
TransactionalEditingDomain editingDomain = selectedElement.getEditingDomain();
editingDomain.getCommandStack().execute(new RecordingCommand(editingDomain) {
	@SuppressWarnings("unchecked")
	protected void doExecute() {
		dev.setName("Morda13");
		for (int i = 0; i < 5; i++) {
			Module newMod = NetworkFactory.eINSTANCE.createModule();
			newMod.setName("X26 - " + i);
			dev.getModules().add(newMod);
		}
	}
});

Remove Property Sheet altogether

Add next method to the generated diagram editor class (usually <ModelName>DiagramEditor)

	public Object getAdapter(Class type) {
		if (type == IPropertySheetPage.class) {
			return null;
		}
		return super.getAdapter(type);
	}

Sharing single EditingDomain instance across several diagrams

NOTE: This problem (and a solution) is also discussed here: http://code.google.com/p/gmftools/wiki/SharedEditingDomain.

Generated code always creates new (own) instance of EditingDomain for each opened diagram editor. Since EditingDomain owns ResourceSet, different instances of domain model elements will be loaded for each opened diagram editor. Sometimes it is important to share the same domain model instances across several diagram editors (for example to get rid of synchronization problems). This leads us to the question of sharing same instance of EditingDomain across several generated diagram editors. Below you can find step by step description of necessary modifications you have to apply to the generated code to share the same EditingDomain instance between two diagrams of different types.

1. Make sure you have org.eclipse.gmf.bridge.trace plugin installed as a part of GMF SDK. (It is provided with the GMF Experimental SDK.)

2. Create diagram1.gmfmap, diagram2.gmfmap files and generate diagram1.gmfgen, diagram2.gmfgen. Ensure diagram1.trace and diagram2.trace files created.

3. Modify generated diagram1.gmfgen and diagram2.gmfgen files to use different Model IDs, Plugin IDs, Editor IDs. Optionally two different diagrams could has different diagram file extensions as well:

Model ID.PNG Plugin ID.PNG Editor ID.PNG


4. Modify diagram2.trace file to use different visualID values then specified in diagram1.trace file - the easiest way is to replace “"200”-> “"210”, .., “"800” -> “"810” – and regenerate diagram2.gmfgen from diagram2.gmfmap

5. Open diagram2.gmfgen and modify visualID property for GenDiagram to make it different then visualID of GenDiagram in diagram1.gmfgen:

Diagram2Visual ID.PNG


6. Ensure both diagram1.gmfgen and diagram2.gmfgen are using the same EditingDomain ID:

EditingDomain ID.PNG


7. Generate code for both diagram plugins.

8. In both generated ???DocumentProvider classes substitute EditingDomain creation code:

/**
 * @generated
 */
private TransactionalEditingDomain createEditingDomain() {
	TransactionalEditingDomain editingDomain = DiagramEditingDomainFactory.getInstance().createEditingDomain();
	editingDomain.setID("SharedEditingDomain"); //$NON-NLS-1$
	…

with the following call to the central EditingDomain registry:

/**
 * @generated NOT
 */
private TransactionalEditingDomain createEditingDomain() {
	// Modification started
	TransactionalEditingDomain editingDomain;
	editingDomain = TransactionalEditingDomain.Registry.INSTANCE.getEditingDomain("SharedEditingDomain"); //$NON-NLS-1$
	if (editingDomain == null) {
		editingDomain = DiagramEditingDomainFactory.getInstance().createEditingDomain();
		editingDomain.setID("SharedEditingDomain"); //$NON-NLS-1$
	}
	// Continue with the original code.
	…

NOTE-1: If you are using GMF older then 2.0 M5 you have to modify generated ???DiagramEditor.createEditingDomain() instead to simply call the superclass implementation of following method:

/**
 * @generated NOT
 */
protected TransactionalEditingDomain createEditingDomain() {
	return super.createEditingDomain();
}

NOTE-2: The idea of this step is to let two connected instances of GMF editors using the same instance of EditingDomain. You can easily create your own EditingDomain registry/accessor and call it from the corresponding methods instead of calling TransactionalEditingDomain.Registry.

You can see an example of alternative ???DocumentProvider modification in the following article: http://www.eclipse.org/articles/Article-Integrating-EMF-GMF-Editors/index.html#extending_the_editor_input.


9. Declare EditingDomain with the shared EditingDomain ID (see step 5.) inside plugin.xml for generated for diagram1:

<!-- gmf generator persistent region begin -->
   <extension
         point="org.eclipse.emf.transaction.editingDomains">
      <editingDomain
            factory="org.eclipse.emf.workspace.WorkspaceEditingDomainFactory"
            id="SharedEditingDomain"/>
   </extension>
<!-- gmf generator persistent region end -->

NOTE: This step is necessary to initialize TransactionalEditingDomain.Registry used above with the proper EditingDomain id. If you are not using TransactionalEditingDomain.Registry to access/create EditingDomain instance (e.g. following the article: http://www.eclipse.org/articles/Article-Integrating-EMF-GMF-Editors/index.html#extending_the_editor_input) then you should skip this step.

10. Only one metamodelType could be registered for each EClass in scope of one EditingDomain. During this step you have to replace by specializationType declaration all the metamodelType declarations inside plugin.xml for diagram2 duplicating metamodelType declarations inside plugin.xml for diagram1 (having the same eclass attribute). Following part of plugin.xml:

<metamodelType
      id="shape.diagram2.Diagram_1001"
      name="Undefined"
      kind="org.eclipse.gmf.runtime.emf.type.core.IHintedType"
      eclass="Diagram"
      edithelper="shape.diagram.edit.helpers.DiagramEditHelper">
   <param name="semanticHint" value="1001"/>
</metamodelType>

Should be replaced with:

<specializationType
      id="shape.diagram2.Diagram_1001"
      name="Undefined"
      kind="org.eclipse.gmf.runtime.emf.type.core.IHintedType"
      edithelperadvice="org.eclipse.gmf.runtime.emf.type.core.edithelper.AbstractEditHelperAdvice">
   <specializes id="shape.diagram1.Diagram_1000"/>
   <param name="semanticHint" value="1001"/>
</specializationType>

<specializes> attribute for newly creates specializationTypes should point to the corresponding metamodeType id from plugin.xml generated for diagram1.

11. Patch plugin.xml generated for diagram2 and specify the same clientContext for elementTypeBindings extension point as it is specified for diagram1. Instead of:

<extension point="org.eclipse.gmf.runtime.emf.type.core.elementTypeBindings">
   <clientContext id="Diagram2_IDClientContext">
      …
   </clientContext> 
   <binding context="Diagram2_IDClientContext">
      …
   </binding>
</extension>

type:

<extension point="org.eclipse.gmf.runtime.emf.type.core.elementTypeBindings">
   <clientContext id="Diagram1_IDClientContext">
      …
   </clientContext> 
   <binding context="Diagram1_IDClientContext">
      …
   </binding>
</extension>

12. To correctly unload (model) resources associated with diagram you can add method to generated ???DocumentProvider:

protected void disposeElementInfo(Object element, ElementInfo info) {
	super.disposeElementInfo(element, info);
	// Unload all the resources associated with diagram here if necessary.
}

Making figures sensitive to attributes of semantic elements

TASK:

Change a diagram editor generated by GMF to make its figures sensitive to changes of semantic elements’ attributes’ values. Changes of attributes’ values should be automatically detected and reflected in figures’ look.

SOLUTION:

You have to make a little change in *EditPart class. I’m going to change edit part for the semantic model element named Port, so I have to edit my.package.diagram.edit.parts.PortEditPart class.

1. Add a new method to the PortFigure class, which is a PortEditPart’s inner class:

	public void updateFace() {
		Port port = (Port) ((Node) PortEditPart.this.getModel()).getElement();

		// set line width according to number of some children
		int lineWidth = 1;
		if (port.getLogicalUnits().size() > 1) {
			lineWidth = 2;
		}
		this.setLineWidth(lineWidth);

		// update tooltip
		String tooltipText;
		if (port.getDescription() != null
				&& port.getDescription().length() > 0) {
			tooltipText = port.getDescription();
		} else {
			tooltipText = port.getName();
		}
		if (getToolTip() == null) {
			setToolTip(new Label(tooltipText));
		} else if (getToolTip() instanceof Label) {
			((Label) getToolTip()).setText(tooltipText);
		}
	}

The updateFace method makes actual changes to the figure’s look. This example implementation sets the line’s width according to the number of Port’s children (port.getLogicalUnits().size()) and sets the tool-tip text to the value of element’s attribute named description.

Then you have to hook up this method in two places: PortFigure’s constructor to update the look when editor starts and the handleNotificationEvent method to react to live changes.

2. Change PortFigure’s constructor to make it use new method.

	/**
	 * @generated NOT
	 */
	public PortFigure() {
		this.setFill(true);
		this.setFillXOR(false);
		this.setOutline(true);
		this.setOutlineXOR(false);
		this.setLineStyle(Graphics.LINE_SOLID);
		//this.setLineWidth(1);
		//this.setForegroundColor(PORTFIGURE_FORE);
		//this.setBackgroundColor(PORTFIGURE_BACK);
		updateFace();
		createContents();
	}

3. Override handleNotificationEvent(Notification notification) method in PortEditPart.

	@Override
	protected void handleNotificationEvent(Notification notification) {
		if (notification.getNotifier() instanceof Port) {
			getPrimaryShape().updateFace();
		}
		super.handleNotificationEvent(notification);
	}

Without this method you would need to restart editor to reflect element's changes in figure parameters.

Tested on GMF version 2.0. → Original post

Printing multiple diagrams

See this blog posting by Anoop Menon.

Rotating a border item

From a newsgroup posting by Jaap Reitsma on 01/31/08:

I have fond a solution that rotates my bordered triangle (always pointing inwards in a container) as well as being initialized properly when opening the diagram. In my first attempt I used MyTriangleEditPart.refreshBounds() to manipulate the orientation. Although it works when dragging the triangle around the container, it does not get initialized properly as the refreshBounds of the bordered item is called when the layout of the parent has not been done yet.

The seemingly elegant solution is to hook into the layout of the border item container, i.e. the parent of my bordered triangle figure. The code I have used follows below:

/* (non-Javadoc)
* @see 
org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart#activate()
*/
@Override
public void activate() {
    IBorderItemLocator locator = getBorderItemLocator();
    if (locator instanceof BorderItemLocator) {
        ((BorderItemLocator)locator).setBorderItemOffset(new Dimension(8, 
8));
    }
    IFigure figure = getFigure();
    if (figure instanceof BorderedNodeFigure) {
        BorderedNodeFigure bnf = (BorderedNodeFigure)figure;
        IFigure bicf = bnf.getBorderItemContainer();
        bicf.addLayoutListener(new LayoutListener.Stub() {
            @Override
            public void postLayout(IFigure container) {
                getBorderItemLocator().relocate(getFigure());
                int position = 
getBorderItemLocator().getCurrentSideOfParent();
                int rotation = 0;
                switch (position) {
                    case PositionConstants.WEST:
                        break;
                    case PositionConstants.NORTH:
                        rotation = 90;
                        break;
                    case PositionConstants.EAST:
                        rotation = 180;
                        break;
                    case PositionConstants.SOUTH:
                        rotation = 270;
                        break;
                    default:
                        break;
                }
                getPrimaryShape().setRotationInDegrees(rotation);
            }
        });
    }
    super.activate();
}

The setRotationInDegrees is a method in my custom RotableScalablePolygon which is actually a copy of the implementation of the inner figure generated by GMF when using a scalable polygon as figure.

New-small.gif Different Figures for one Ecore class

TASK:

You want to have different looking diagram figures but all backed by the same Ecore class.

SOLUTION:

 Class: CommonClass
 Attribute: CommonClass.type:CommonClassType
 Enumeration: CommonClassType
 Values: TypeA = 0, TypeB = 1

For CommonClass of CommonClassType.TypeA display Figure 'A' if CommonClassType.TypeB disaply Figure B.

 My.gmfmap
 Mapping
   Top Node Reference              Diagram Node Node TypeAFigure
     Node Mapping
       Constraint                  Body self.type = CommonClassType::TypeA, Language ocl
       Feature Seq Initalizer      Element Class CommonClass
         Feature Value Spec        Feature CommonClass.type:CommonClassType
           Value Expression        Body CommonClass.type::TypeA, Language ocl
 
   Top Node Reference              Diagram Node Node TypeBFigure
     Node Mapping
       Constraint                  Body self.type = CommonClassType::TypeB, Language ocl
       Feature Seq Initalizer      Element Class CommonClass
         Feature Value Spec        Feature CommonClass.type:CommonClassType
           Value Expression        Body CommonClass.type::TypeB, Language ocl

You will of course have to define two figures, two creation tool items, etc..

Setting Initial Position of a Node

TASK: You want to set the initial position of a node, but the user should still be able to move the node.

SOLUTION: There is more than one solution. The following solution overrides activate() in XYZEditPart.

/**
 * @generated NOT
 */
@Override
public void activate() {
	super.activate();
	
	AbstractEMFOperation emfOp = new AbstractEMFOperation(getEditingDomain(), "Location setting") {

		@Override
		protected IStatus doExecute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
			Location lc = (Location) ((Node) getModel()).getLayoutConstraint();
			lc.setX(x);
			lc.setY(y);
			return Status.OK_STATUS;
		}

	};

	IStatus status;

	try {
		status = OperationHistoryFactory.getOperationHistory().execute(emfOp, null, null);
	} catch (ExecutionException e) {
		status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Setting location failed", e);
	}

	if (status.getCode() == IStatus.WARNING || status.getCode() == IStatus.ERROR) {
		Activator.getDefault().getLog().log(status);
	}
}


Setting default Routing for connections

TASK: You want to set the routing of the connections to be Rectilinear (90 degrees angles) or Tree style, as default.

SOLUTION: On your connection's EditPart, modify the createConnectionFigure method to:

/**
* @generated NOT
*/ 
protected Connection createConnectionFigure() {
    XYZFigure figure = new XYZFigure();
    
    AbstractEMFOperation emfOp = new AbstractEMFOperation(getEditingDomain(), "line routing setting") {
        @Override
        protected IStatus doExecute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
        RoutingStyle style = (RoutingStyle) ((View) getModel()).getStyle(NotationPackage.Literals.ROUTING_STYLE);
        style.setRouting(Routing.RECTILINEAR_LITERAL);   //or Routing.TREE_LITERAL            
        return Status.OK_STATUS;
        }
    };

    try {           
        OperationHistoryFactory.getOperationHistory().execute(emfOp, null, null);
    } catch (ExecutionException e) {}

    return figure;
}


If you want to set the routing globally for all of your connections you can modify the DiagramConnectionsPreferencePage class in the *.diagram.preferences package with the following code.

/**
* Initializes the default preference values for this preference store.
* 
* @param preferenceStore
*            the preference store
*
* @generated NOT
*/
public static void initDefaults(IPreferenceStore preferenceStore) {
  preferenceStore.setDefault(IPreferenceConstants.PREF_LINE_STYLE, Routing.RECTILINEAR);
}


Removing line drawn at top of compartments

TASK: You want to hide the line that is always displayed ontop of a compartment

SOLUTION: Add the following line setBorder(null) to the createFigure method of your compartment's EditPart. It will look like this:

/**
 * @generated NOT
 */
public IFigure createFigure() {
   ResizableCompartmentFigure result = (ResizableCompartmentFigure) super.createFigure();
   result.setTitleVisibility(false);
   //changed code
   result.setBorder(null);
   return result;
}

see forums post.


Implicit node in the middle creation

TASK: You want to create ER diagram like connections. This means draw a connection from node A to B and a node C will be automatically created in the middle of the new connection.

SOLUTION: Well, ok then, I am going to post the proposed solution first and then would be happy to discuss at the forum.

Implicit-in-the-middle.PNG

As you probably got by now there are no one-line solutions to this use case. The only good news is that I am going to try to push this solution to GMF-tooling, and with a bit of luck it will be possible to define and generate the code below in Indigo. To be honest, it is one of the reason for me to describe this somewhat long story here.

The complete code is available in the public SVN repoistory at http://www.assembla.com/code/gmf-association-class-links/subversion/nodes

There are 4 plugins, 2 of them are just pure EMF plugins (inthemiddle and inthemiddle.edit, nothing interesting there), + supporting .gmf plugin containing helper code, GMF artifacts and custom templates, and finally the resulting .diagram plugin.

There is very simple diagram definition in the *.gmf/gmf/def/ folder. There is nothing special here, except that we set the incoming link creations to true for both GenLink’s in the gmfgen model, to allow user to start drawing of the link from the Rectangle instead of not-yet-created Rhomb.

Also I was somewhat lazy to play with gmfgraph today, so don’t be surprised that Rhomb model elements are rendered as rounded rectangles at the diagram :).

Proposed solution consists of 2 parts: a) modifying the GraphicalNodeEditPolicy (GNEP) to collect and pass the diagram information into the semantic creation command and b) implementing the composite creation command itself

a) So we are going to replace org.eclipse.gmf.runtime.diagram.ui.editpolicies.GraphicalNodeEditPolicy with tweaked ITMGraphicalNodeEditPolicy from the *.gmf plugin. The changes made are as follows:

- Default GNEP packs the CompositeTransactionalCommand and then uses the ordering to access the different parts of the pack. E.g, lines 508-515 in the default GNEP looks like:

CompositeCommand cc = (CompositeCommand) proxy.getICommand();
//....
Iterator commandItr = cc.iterator();
commandItr.next(); //0
SetConnectionEndsCommand sceCommand = (SetConnectionEndsCommand) commandItr.next(); //1
sceCommand.setNewTargetAdaptor(new EObjectAdapter(((IGraphicalEditPart) targetEP).getNotationView()));
SetConnectionAnchorsCommand scaCommand = (SetConnectionAnchorsCommand) commandItr.next(); //2
//....

This is clearly bad, so the changed code introduces ITMCreateLinkCommand with explicit access to its parts. It also has a code to register the results pack in the parameters of original request, so our semantic command will be able to access the whole pack at the execution time.

The changed code also computes an additional ITMCreateParameters describing the diagram information about the link source and target, but, while useful in general, it may be out of scope of this particular use case.

To register all the ITM* staff above in the generated diagram I used the following custom templates (*.gmf plugin, /gmf/templates/...)

itm::graphicalnodeep::Guard.qvto
    it is the guard that prevents generation of the new code for unrelated diagrams (I had more than 1): 

helper requiresSpecificGraphicalNodeEditPolicy(diagram : genModel::GenDiagram) : Boolean {

    return

        'your.diagram.plugin.id = diagram.editorGen.plugin.iD

}

This guard allows every other affected template to have something like 

«EXTENSION itm::graphicalnodeep::Guard»

...

«IF requiresSpecificGraphicalNodeEditPolicy(self.getDiagram())-»

//....some changes 

«ENDIF-»

which, after all, allowed me to find all the affected parts and write this post :)

itm::graphicalnodeep::graphicalNodeEditPolicy.xpt - here comes the code to register the tweaked version of the GNEP aspects::impl::diagram::editparts::LinkEditPart.xpt and aspects::impl::diagram::editparts::NodeEditPart.xpt - just registering the tweaked editpolicies for all nodes and links by calling

«EXPAND itm::graphicalnodeep::graphicalNodeEditPolicy::install-»

finally, if you also have notes at your diagram you also need to replace the NoteEditPart because in its default GMF version it expects the default GNEP. This is done by:

aspects::xpt::diagram::editparts::EditPartFactory.xpt 

«IF requiresSpecificGraphicalNodeEditPolicy(self)-»
       if (org.eclipse.gmf.runtime.diagram.core.util.ViewType.NOTE.equals(view.getType())) {

return new «EXPAND itm::graphicalnodeep::graphicalNodeEditPolicy::noteEditPartFQN»(view);
       }
       «ENDIF-»

and 
aspects::xpt::providers::extensions.xpt

«AROUND extensions FOR gmfgen::GenDiagram-»

    «targetDef.proceed()»

«IF requiresSpecificGraphicalNodeEditPolicy(self)-»

    «EXPAND noteEditPartProvider»

«ENDIF-»

«ENDAROUND»


«DEFINE noteEditPartProvider FOR gmfgen::GenDiagram-»

<extension point="org.eclipse.gmf.runtime.diagram.ui.editpartProviders" id="noteep-provider">

    «EXPAND xpt::Common::xmlGeneratedTag»

    <editpartProvider class="«getEditPartProviderQualifiedClassName()»">

        <Priority name="Low"/>

        <object class="org.eclipse.gmf.runtime.notation.Node" id="generated-note">

            <method name="getType()" value="Note"/>

        </object>

        <context views="generated-note"/>

    </editpartProvider>

</extension>

«ENDDEFINE»

Ok, at this point we can regenerate the diagram, and the new one will install the tweaked GNEPs to all the right places. Now its time time to use it:

(*.gmf plugin)AbstractImplicitMiddleElementLinkCreateCommand.java is the base class for all creation commands for use cases like that. It does not have dependencies to our diagram and as such prepared to be moved somewhere into GMF runtime.

IElementTypeAwareAdapter.java, EObjectAndElementTypeAdapter.java and ElementTypeOnlyAdapter are the helper interface and 2 implementations to describe the concrete element type for node in the middle and links.

and finally the concrete implementation ImplicitRhombCreateCommand (*.diagram plugin, custom-src root) that contains the configuration (what are the visual ids of links, what is the node in the middle, etc) specific for my own diagram.

Finally, this all is bound together by the @generated NOT code in the RectangleItemSemanticEditPolicy -- this is the only place in the code where @generated and custom code lives together:

/**
 * @generated NOT
 */
protected Command getCompleteCreateRelationshipCommand(CreateRelationshipRequest req) {
    if (InthemiddleElementTypes.RhombRight_4002 == req.getElementType() && req.getSource() instanceof Rectangle) {
        return getGEFWrapper(new ImplicitRhombCreateCommand(req, req.getSource(), req.getTarget()));
    }
    // escape to generated code otherwise
    return getCompleteCreateRelationshipCommandGen(req);
}

Thats it, if you run this code in the helios, in the generated diagram it should be possible to choose the Link tool at palette, draw the link directly from Artist to Song rectangles and have the ‘rhomb’ auto-created in the middle:


Using EEF and GMF

TASK: You want to use EEF and GMF together

Solution: from http://www.eclipse.org/forums/index.php/t/466110/

GMF does not know which plugin provides the property tabs for the editor, it just declares the contributor ID for someone who wants to provide its contents here. So there is no code in diagram plugin to change. Instead, you need to reference this diagram controbutor ID from EEF plugin:

  • in the diagram plugin. search for: org.eclipse.ui.views.properties.tabbed.propertyContributor extension, it will be like this:
<extension point="org.eclipse.ui.views.properties.tabbed.propertyContributor" id="prop-contrib">
 <?gmfgen generated="true"?>
 <propertyContributor
 contributorId="org.eclipse.gmf.examples.ocldriven.diagram"
 labelProvider="org.eclipse.gmf.examples.ocldriven.toe.diagram.sheet.TOESheetLabelProvider">
 <propertyCategory category="domain"/>
 <propertyCategory category="visual"/>
 <propertyCategory category="extra"/>
 </propertyContributor>
 </extension>
  • note the contributorId (contributorId="org.eclipse.gmf.examples.ocldriven.diagram")
  • note one of the existing categories ( <propertyCategory category="domain"/>)
  • copy/paste the "org.eclipse.ui.views.properties.tabbed.propertyTabs" and "org.eclipse.ui.views.properties.tabbed.propertySections" extensions from EEF-generated plugin.xml fragment to the real plugin.xml for edit plugin (if you followed the EEF tutorial, you may already have one copy of these 2 extensions there for EMF tree editor)
  • in the BOTH copy/pasted duplicated extensions replace the EEF contributorID to the GMF contributor id (first bold from step1), so in my sample it would be:
<extension point="org.eclipse.ui.views.properties.tabbed.propertySections">
 <propertySections contributorId="org.eclipse.gmf.examples.ocldriven.diagram">
 ...
<extension point="org.eclipse.ui.views.properties.tabbed.propertyTabs">
<propertyTabs contributorId="org.eclipse.gmf.examples.ocldriven.diagram">
...
  • in the copy/pasted "org.eclipse.ui.views.properties.tabbed.propertyTabs" extension replace the category="<whatever>" by the actual category from GMF (second bold from step1 -- e.g "domain"):
 <propertyTabs
            contributorId="org.eclipse.gmf.examples.ocldriven.diagram">
         <propertyTab
               id="Base"
               label="EEF-Base"
               category="domain">
         </propertyTab>
         ....

I just made exactly these steps for GMF-T OCL/driven sample, and that all I needed to get an attached screenshot.

EEF-GMF-Sample.png

Copyright © Eclipse Foundation, Inc. All Rights Reserved.