Customizing Tools

Tools are used in conjunction with the active diagram editor. They allow the user to quickly create new elements on the diagram or within compartments. Connection tools make creating relationships easy by providing visual cues and tool feedback.

Palette Tools

Adding Palette Tools to Create UML Elements

This example will demonstrate how to add a tool to the palette that will create a UML Class.

The first step is to extend the org.eclipse.gmf.runtime.diagram.ui.paletteProviders extension-point. The following example shows an extension that will add a new palette drawer and a palette tool within the drawer.

   <extension
         point="org.eclipse.gmf.runtime.diagram.ui.paletteProviders">
      <paletteProvider
            class="org.eclipse.gmf.runtime.diagram.ui.providers.DefaultPaletteProvider">
         <Priority name="Lowest"/>
         
         <!-- Used to constrain the palette to a specific context -->
         <content>
            <method
                  name="getDiagram().getType()"
                  value="Freeform, Class"/>
         </content>
         
         <contribution
               factoryClass="com.ibm.examples.providers.MyPaletteFactory">
            <entry
                  id="MyDrawer"
                  kind="drawer"
                  label="My Drawer"
                  path="/"/>
            <entry
                  description="My description"
                  id="myToolId"
                  kind="tool"
                  label="My Label"
                  small_icon="icons/myIconSmall.gif"
                  large_icon="icons/myIconLarge.gif"
                  path="/MyDrawer/"/>
         </contribution>
      </paletteProvider>
   </extension>

The MyPaletteFactory class should extend org.eclipse.gmf.runtime.diagram.ui.services.palette.PaletteFactory.Adapter. It simply provides a CreationTool for the given toolId. The toolId comes from the tool entry id specified in the paletteProviders extension. An element type is passed in the CreationTool constructor. For this example, because the palette tools will create a UML Class, the Class element type is obtained through UMLElementTypes.CLASS.

public class MyPaletteFactory extends Adapter {

    private final Map tools = new HashMap();
    {
        tools.put("myToolId", new CreationTool(UMLElementTypes.CLASS);
    }

    public Tool createTool(String toolId) {
        return (Tool)tools.get(toolId);
    }
}
Note the editor content object is of type IPaletteContent. You can use its getDiagram() method to specify the diagram types where the palette tool is to be added. In the example above, the tool is added to both Class diagram and Freeform diagram.

Adding Palette Tools to Create Stereotyped Elements

This example will demonstrate how to add a tool to the palette that will create a stereotyped element. This example assumes that a profile named MyProfile with a stereotype named MyStereotype is deployed. Substitute the profile and stereotype names with those from your own deployed profile where applicable.

The process for defining the palette is done similarly to Adding Palette Tools to Create UML Elements.

The first step is to extend the org.eclipse.gmf.runtime.diagram.ui.paletteProviders extension-point. The following example shows an extension that will add a new palette drawer and a palette tool within the drawer.

   <extension
         point="org.eclipse.gmf.runtime.diagram.ui.paletteProviders">
      <paletteProvider
            class="org.eclipse.gmf.runtime.diagram.ui.providers.DefaultPaletteProvider">
         <Priority name="Lowest"/>
         
         <!-- Used to constrain the palette to a specific context -->
         <content>
            <method
                  name="getDiagram().getType()"
                  value="Freeform, Class"/>
         </content>
         
         <contribution
               factoryClass="com.ibm.examples.providers.MyPaletteFactory">
            <entry
                  id="MyDrawer"
                  kind="drawer"
                  label="My Drawer"
                  path="/"/>
            <entry
                  description="My description"
                  id="myToolId"
                  kind="tool"
                  label="My Label"
                  small_icon="icons/myIconSmall.gif"
                  large_icon="icons/myIconLarge.gif"
                  path="/MyDrawer/"/>
         </contribution>
      </paletteProvider>
   </extension>

The MyPaletteFactory class should extend org.eclipse.gmf.runtime.diagram.ui.services.palette.PaletteFactory.Adapter. It simply provides a CreationTool for the given toolId.

Since the palette tool will create a stereotyped element, a corresponding element type is retrieved from the ElementTypeRegistry by type id, com.ibm.examples.myStereotypeTypeId

public class MyPaletteFactory extends Adapter {

    private final Map tools = new HashMap();
    {
        tools.put("myToolId", new CreationTool(
            ElementTypeRegistry.getInstance().getType("com.ibm.examples.myStereotypeTypeId"));
    }

    public Tool createTool(String toolId) {
        return (Tool)tools.get(toolId);
    }
}

If the stereotype element type does not already exist for the type id, then one must be created by using the org.eclipse.gmf.runtime.emf.type.core.elementTypes extension point, otherwise this step may be omitted.

By specifying the kind to be com.ibm.xtools.uml.type.IStereotypedElementType, a param with name="stereotype" must be specified to identify the stereotype this element type represents. The value of this parameter should equal the fully qualified name of the stereotype.

   <extension
         point="org.eclipse.gmf.runtime.emf.type.core.elementTypes">
      <specializationType
            id="com.ibm.examples.myStereotypeTypeId"
            kind="com.ibm.xtools.uml.type.IStereotypedElementType"
            name="My Stereotype Type"
            icon="icons/myTypeIcon.gif">
         <specializes id="com.ibm.xtools.uml.class"/>
         <param
               name="stereotype"
               value="MyProfile::MyStereotype"/>
      </specializationType>
   </extension>

Element types and advice are organized by client context. In order for the new stereotype element type to participate in the UML Modeler application it must be bound to the UML Modeler client context through the org.eclipse.gmf.runtime.emf.type.core.elementTypeBindings extention point.

   <extension 
         point="org.eclipse.gmf.runtime.emf.type.core.elementTypeBindings">
      <binding context="com.ibm.xtools.uml.type.context">
         <elementType ref="com.ibm.examples.myStereotypeTypeId"/>
      </binding>
   </extension>

Adding Palette Tools to Create Custom Connectors

This example will assume that the stereotype above is to extend the UML Association instead of the UML Class.

The palette factory will remain the same as above. The element type will differ by specializing com.ibm.xtools.uml.association and implementing an edithelperadvice which will define what elements this connector can connect between.

   <extension
         point="org.eclipse.gmf.runtime.emf.type.core.elementTypes">
      <specializationType
            id="com.ibm.examples.myStereotypeTypeId"
            edithelperadvice="com.ibm.examples.MyConnectionEditHelperAdvice"
            kind="com.ibm.xtools.uml.type.IStereotypedElementType"
            name="My Stereotype Type"
            icon="icons/myTypeIcon.gif">
         <specializes id="com.ibm.xtools.uml.association"/>
         <param
               name="stereotype"
               value="MyProfile::MyStereotype"/>
      </specializationType>
   </extension>

Override getBeforeEditContextCommand(GetEditContextRequest request) to define what elements this connector can connect between. If the source and target are correct, create and return a new GetEditContextCommand. If the source is correct, but the target has yet to be chosen, return IdentityCommand.INSTANCE. If all checks fail, return UnexecutableCommand.INSTANCE.

In this example, the connection tool will only be able to connect between two UML Classes.

public class MyConnectionEditHelper extends AbstractEditHelper {

    protected ICommand getEditContextCommand(GetEditContextRequest req) {
        IEditCommandRequest editRequest = req.getEditCommandRequest();

        if (editRequest instanceof CreateRelationshipRequest) {
            CreateRelationshipRequest crr = (CreateRelationshipRequest) editRequest;

            EObject source = crr.getSource();
            if (source != null && !(source instanceof Class)) {
                return UnexecutableCommand.INSTANCE;
            }
            
            EObject target = crr.getTarget();
            if (target == null) {
                return IdentityCommand.INSTANCE;
            }
            if (!(target instanceof Class)) {
                return UnexecutableCommand.INSTANCE;
            }
            
            GetEditContextCommand result = new GetEditContextCommand(req);
            result.setEditContext(crr.getContainer());
            return result;
        }
        return null;
    }
}

Modeling Assistant

Popup Bar Contribution

Popup bars appear when hovering over shapes (including inner shapes) on a diagram. The popup bar can be filled with actions to create new elements within the context of the shape that is hovered over. These actions are contributed by extending the org.eclipse.gmf.runtime.emf.ui.modelingAssistantProviders extension-point.

For this example, popup bar actions will be contributed for a shape on the diagram which represents a UML Class using the FORWARD execution strategy (the popup bar action contribution will be added to the existing popup bar actions). A UML Class can contain Operations. Therefore the popup action that will be contributed, will be an action to create Operations within the Class.

Begin by extending the org.eclipse.gmf.runtime.emf.ui.modelingAssistantProviders extension-point and set the modelingAssistantProvider class to be a class which extends org.eclipse.gmf.runtime.emf.ui.services.modelingassistant.ModelingAssistantProvider

The enablement criteria for the modeling assistant is usually done by comparing the context object either to a specific edit part or by comparing the semantic element of the edit part to a specific model element. This example will illustrate both of these ways.

   <extension
         point="org.eclipse.gmf.runtime.emf.ui.modelingAssistantProviders">
      <modelingAssistantProvider class="com.ibm.examples.providers.MyModelingAssistantProvider">
         <Priority name="Medium"/>
         <object
               class="com.ibm.examples.editparts.MyEditPart(com.ibm.examples)"
               id="editPart">
         </object>
         <object
               class="org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart(org.eclipse.gmf.runtime.diagram.ui)"
               id="modelObject">
            <method
                  name="resolveSemanticElement().eClass().getEPackage().getNsURI()"
                  value="http://www.eclipse.org/uml2/3.0.0/UML"/>
            <method
                  name="resolveSemanticElement().eClass().getName()"
                  value="Class"/>
         </object>
         <context elements="editPart, modelObject"/>
      </modelingAssistantProvider>
   </extension>

Override the getTypesForPopupBar(IAdaptable host) method in the MyModelingAssistantProvider class. The return result of this method should be a list of element types. For each element type in the list, a new popup bar action will be contributed.

public class MyModelingAssistantProvider extends ModelingAssistantProvider {

    public List getTypesForPopupBar(IAdaptable host) {
    
        // can check by edit part
        IGraphicalEditPart hostEP = (IGraphicalEditPart)host.getAdapter(IGraphicalEditPart.class);
        if (hostEP != null) {
            if (hostEP instanceof ClassEditPart) {
                return Collections.singletonList(UMLElementTypes.OPERATION);
            }
        }
        
        // or can check by semantic element
        EObject eObject = (EObject)host.getAdapter(EObject.class);
        if (eObject != null) {
            if (eObject instanceof Class) {
                return Collections.singletonList(UMLElementTypes.OPERATION);
            }
        }
        
        return Collections.EMPTY_LIST;
    }
}

Connection Handles Contribution

Connection handles appear when hovering over shapes on a diagram. The connection handles provide various functions through easy to access popup menus. These actions are contributed by extending the org.eclipse.gmf.runtime.emf.ui.modelingAssistantProviders extension-point.

The following connection handles examples will simply add functionality to the MyModelingAssistantProvider class from above.

Adding Support for Connection Creation with Connection Handles

Clicking and dragging either the inbound or outbound connection handle to the diagram surface, results in a popup menu which displays a list of available relationship types to be created between the source or target element (depending on if the inbound or outbound connection handle was used).

To contribute to this list, implement the getRelTypesOnSource() and getRelTypesOnTarget() methods. Each method should return a list of available relationship element types.

    public List getRelTypesOnSource(IAdaptable source) {
        EObject eObject = (EObject)source.getAdapter(EObject.class);
        if (eObject instanceof Class) {
            return Collections.singletonList(UMLElementTypes.INTERFACE_REALIZATION);
        }
        return Collections.EMPTY_LIST;
    }

    public List getRelTypesOnTarget(IAdaptable target) {
        EObject eObject = (EObject)target.getAdapter(EObject.class);
        if (eObject instanceof Interface) {
            return Collections.singletonList(UMLElementTypes.INTERFACE_REALIZATION);
        }
        return Collections.EMPTY_LIST;
    }

Clicking and dragging either the inbound or outbound connection handle to another shape on the diagram (as oppose to the diagram surface), results in a popup menu which displays a list of available relationship types to be created between the source and target.

To contribute to this list, implement the getRelTypesOnSourceAndTarget() method. This method should return a list of available relationship element types for the given source and target elements.

    public List getRelTypesOnSourceAndTarget(IAdaptable source, IAdaptable target) {
        EObject sourceEObject = (EObject)source.getAdapter(EObject.class);
        EObject targetEObject = (EObject)target.getAdapter(EObject.class);
        if (sourceEObject instanceof Class
                && targetEObject instanceof Interface) {
            return Collections.singletonList(UMLElementTypes.INTERFACE_REALIZATION);
        }
        return Collections.EMPTY_LIST;
    }

Adding Support for Connection Creation to an Unspecified Source or Target

Utilizing a connection palette tool from a shape to the diagram surface, results in a popup menu which displays a list of available source and target elements for the relationship represented by the palette tool.

To contribute to this list, implement the getTypesForSource() and getTypesForTarget() methods. These methods should return a list of available element types which can be used as either the source or target type respectively.

    public List getTypesForSource(IAdaptable target, IElementType relationshipType) {
        EObject eObject = (EObject)target.getAdapter(EObject.class);
        if (eObject instanceof Interface) {
            if (relationshipType == UMLElementTypes.INTERFACE_REALIZATION) {
                return Collections.singletonList(UMLElementTypes.CLASS);
            }
        }
        return Collections.EMPTY_LIST;
    }

    public List getTypesForTarget(IAdaptable source, IElementType relationshipType) {
        EObject eObject = (EObject)source.getAdapter(EObject.class);
        if (eObject instanceof Class) {
            if (relationshipType == UMLElementTypes.INTERFACE_REALIZATION) {
                return Collections.singletonList(UMLElementTypes.INTERFACE);
            }
        }
        return Collections.EMPTY_LIST;
    }

Adding Support for Selecting an Existing Element as the Source or Target

By implementing the selectExistingElementForSource() and selectExistingElementForTarget() methods, the create to existing element action will be available. When the user selects this entry, the selectExistingElementForSource() and selectExistingElementForTarget() methods will be called (depending on whether the inbound or outbound connection handle was used) in the provider that provided for this operation with the highest priority. It is up to the client to implement a mechanism to allow the user to select an existing element.

    public EObject selectExistingElementForSource(IAdaptable target, IElementType relationshipType) {
        // Show a dialog from which the user can select an existing element.
        // Return the element.
    }
    
    public EObject selectExistingElementForTarget(IAdaptable source, IElementType relationshipType) {
        // Show a dialog from which the user can select an existing element.
        // Return the element.
    }

Adding Support for Showing Related Elements with the Connection Handles

Double-clicking either the inbound or outbound connection handle, results in a popup menu which displays a list of relationship element types for the source or target. After the user selects a type, the related elements will appear on the diagram with connections to the source or target.

To contribute to this list, implement the getRelTypesForSREOnSource() and getRelTypesForSREOnTarget() methods. These methods should return a list of relationship element types which can connect to either the source or target type respectively.

    public List getRelTypesForSREOnSource(IAdaptable source) {
        EObject eObject = (EObject)source.getAdapter(EObject.class);
        if (eObject instanceof Class) {
            return Collections.singletonList(UMLElementTypes.INTERFACE_REALIZATION);
        }
        return Collections.EMPTY_LIST;
    }

    public List getRelTypesForSREOnTarget(IAdaptable target) {
        EObject eObject = (EObject)target.getAdapter(EObject.class);
        if (eObject instanceof Interface) {
            return Collections.singletonList(UMLElementTypes.INTERFACE_REALIZATION);
        }
        return Collections.EMPTY_LIST;
    }


For more information on element types see Creating Element Types.


Legal notices