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 (a little text update)
Line 347: Line 347:
 
</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..
  
 
[[Category:Modeling]][[Category:Snippets]]
 
[[Category:Modeling]][[Category:Snippets]]

Revision as of 13:33, 9 July 2009

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);
		}
	}
});

New-small.gif 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);
	}


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.

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..

Back to the top