This example will demonstrate how to create custom views for stereotyped elements.
Extend the
org.eclipse.gmf.runtime.diagram.core.viewProviders
extension-point and
set the viewProvider class
to be a class which extends
org.eclipse.gmf.runtime.diagram.core.providers.AbstractViewProvider
.
The custom view needs to be provided for two types of actions:
To satisfy the first scenario, create an object context for class
com.ibm.xtools.uml.type.IStereotypedElementType
. For this context object,
getStereotypeName()
should return the fully qualified name of the expected stereotype,
in this example the expected qualified name is MyProfile::MyStereotype.
To satisfy the second scenario, create another object context for class
org.eclipse.gmf.runtime.emf.core.util.IProxyEObject
.
IProxyEObject
is used because it provides a method to obtain the class ID without resolving
the EObject if it happens to be a proxy. For this context object,
getProxyClassID()
should return the UML element ID for which the stereotype may be
applied. A further check for the expected applied stereotype can either be
implemented inside the view provider or by using a static method reference
in the xml. This example implements this additional check in the view provider code.
<extension point="org.eclipse.gmf.runtime.diagram.core.viewProviders"> <viewProvider class="com.ibm.xtools.examples.atm.profiles.providers.MyViewProvider"> <Priority name="Medium"/> <object class="com.ibm.xtools.uml.type.IStereotypedElementType(com.ibm.xtools.uml.type)" id="MyNode1"> <method name="getStereotypeName()" value="MyProfile::MyStereotype"> </method> </object> <!-- check for the applied stereotype in the provider code --> <object class="org.eclipse.gmf.runtime.emf.core.util.IProxyEObject(org.eclipse.gmf.runtime.emf.core)" id="MyNode2"> <method name="getProxyClassID()" value="uml.Class"> </method> </object> <context elements="MyNode1, MyNode2" viewClass="org.eclipse.gmf.runtime.notation.Node"/> </viewProvider> </extension>
The MyViewProvider
implementation should
provide a view factory class for each of the above scenarios.
To satisfy the first scenario, attempt to get an adapter for
IStereotypedElementType.class
.
If the adapted
IStereotypedElementType
is the MyStereotype element type, then return the
corresponding view factory class.
To satisfy the second scenario, get the semantic element and check if the expected applied stereotype can be found, and if so return the corresponding view factory class.
public class MyViewProvider extends AbstractViewProvider { IElementType MY_STEREOTYPE_TYPE = ElementTypeRegistry.getInstance() .getType("com.ibm.examples.type.myStereotype"); protected Class getNodeViewClass(IAdaptable semanticAdapter, View containerView, String semanticHint) { Class clazz = null; if (semanticHint != null && semanticHint.length() == 0) { Object elementType = semanticAdapter.getAdapter(IStereotypedElementType.class); if (elementType == MY_STEREOTYPE_TYPE) { clazz = MyViewFactory.class; } else { // search for applied stereotype EObject eObject = getSemanticElement(semanticAdapter); if (eObject instanceof Element) { Element element = (Element)eObject; List stereotypes = element.getAppliedStereotypes(); for (Iterator i = stereotypes.iterator(); i.hasNext(); ) { Stereotype stereotype = (Stereotype)i.next(); String name = stereotype.getQualifiedName(); if ("MyProfile::MyStereotype".equals(name)) { clazz = MyViewFactory.class; break; } } } } } return clazz; } }
The MyViewFactory
extends
org.eclipse.gmf.runtime.diagram.ui.view.factories.AbstractShapeViewFactory.
Override the decorateView
method to set the type for the view and to append
view children. In this example the MyStereotype view will make use of the UML Name
text compartment by appending a child to the stereotype view using the
CommonParserHint#NAME
semantic hint.
public class MyViewFactory extends AbstractShapeViewFactory { protected void decorateView(View containerView, View view, IAdaptable element, String semanticHint, int index, boolean persisted) { // call super.decorateView to initialize view from preferences super.decorateView(containerView, view, element, semanticHint, index, persisted); // set view type view.setType("MyProfile::MyStereotype"); // append the UML name text compartment getViewService().createNode(element, view, CommonParserHint.NAME, ViewUtil.APPEND, persisted, getPreferencesHint()); } }
In order to create a visual representations of the notation view provided by the view provider,
extend the
org.eclipse.gmf.runtime.diagram.ui.editpartProviders
extension-point and set the editpartProvider class
to be a class which extends
org.eclipse.gmf.runtime.diagram.ui.services.editpart.AbstractEditPartProvider
.
Set the enablement critiera to be a org.eclipse.gmf.runtime.notation.Node
class whose
type is the expected MyProfile::MyStereotype type.
<extension point="org.eclipse.gmf.runtime.diagram.ui.editpartProviders"> <editpartProvider class="com.ibm.examples.providers.MyEditPartProvider"> <Priority name="Medium"/> <object class="org.eclipse.gmf.runtime.notation.Node(org.eclipse.gmf.runtime.notation)" id="MyNode"> <method name="getType()" value="MyProfile::MyStereotype"/> </object> <context views="MyNode"/> </editpartProvider> </extension>
Type MyEditPartProvider
returns the
MyEditPart
class if the view type is the expected MyStereotype type.
public class MyEditPartProvider extends AbstractEditPartProvider { private Map nodeMap = new HashMap(); { nodeMap.put("MyProfile::MyStereotype", MyEditPart.class); } protected Class getNodeEditPartClass(View view) { return (Class) nodeMap.get(view.getType()); } }
The MyEditPart
class should extend
org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeNodeEditPart
to inherit the basic functions of a shape node.
Override createNodeFigure()
to return a NodeFigure
.
Since the MyStereotype view has a text compartment child, override getPrimaryChildEditPart()
to return the corresponding text compartment edit part. This will force the text
compartment into direct edit mode when the edit part is created on the diagram.
public class MyEditPart extends ShapeNodeEditPart { public MyEditPart(View view) { super(view); } protected NodeFigure createNodeFigure() { return new CustomFigure(); } public EditPart getPrimaryChildEditPart() { return getChildBySemanticHint(CommonParserHint.NAME); } }
The CustomFigure class, for the MyEditPart
above, extends
org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure
.
The figure is responsible for drawing the visual aspect of the edit part via the
NodeFigure.paintFigure(Graphics graphics)
method. In this
example, paintFigure
creates a rectangle with a triangular roof to represent the MyStereotype shape.
Using a non-rectangular shape for the figure surfaces a couple complications.
The first complication is that the the text compartment will show up on the roof, therefore a margin border with sufficient padding is added to push the text compartment down into the main area of the figure.
The second complication is that in order to allow connections to anchor to the slanted roof,
the class must implement org.eclipse.gmf.runtime.draw2d.ui.figures.IPolygonAchorableFigure
and specify a list of points representing the outline of the figure otherwise it is assumed that the figure is
a rectangular shape. This would result in connection ends appearing to be unattached.
Finally, the figure needs a layout manager to handle the layout of child figures.
public class CustomFigure extends NodeFigure implements IPolygonAnchorableFigure { private static int DPtoLP_1 = MapModeUtil.getMapMode().DPtoLP(1); private static int DPtoLP_3 = MapModeUtil.getMapMode().DPtoLP(3); private static int DPtoLP_10 = MapModeUtil.getMapMode().DPtoLP(10); private static int DPtoLP_12 = MapModeUtil.getMapMode().DPtoLP(12); private static final int MARGIN_TOP_BOTTOM = DPtoLP_3; private static final int MARGIN_LEFT_RIGHT = DPtoLP_10; private static final int ROOF_HEIGHT = DPtoLP_12; public CustomFigure() { setOpaque(true); setLayoutManager(new ConstrainedToolbarLayout()); setBorder(new MarginBorder(ROOF_HEIGHT, MARGIN_LEFT_RIGHT, MARGIN_TOP_BOTTOM, MARGIN_LEFT_RIGHT)); } protected void paintFigure(Graphics g) { Rectangle r = getHandleBounds().getCopy().expand(DPtoLP_1,DPtoLP_1); RGB newForeRGB = ColorUtil.blend(g.getForegroundColor() .getRGB(), g.getBackgroundColor().getRGB()); g.setBackgroundColor( DiagramColorRegistry.getInstance() .getColor(newForeRGB)); // draw the roof Point p1 = r.getTopLeft().getTranslated(0, ROOF_HEIGHT); Point p2 = r.getTopLeft().getTranslated((int)(r.width * 0.5), 0); Point p3 = r.getTopRight().getTranslated(0, ROOF_HEIGHT); PointList pList = new PointList(); pList.addPoint(p1); pList.addPoint(p2); pList.addPoint(p3); g.fillPolygon(pList); g.drawPolygon(pList); // draw the outline Rectangle outline = new Rectangle(); outline.setLocation(r.getLocation().translate(DPtoLP_1, ROOF_HEIGHT)); // width and height - DPtoLP_1 due to line width of 1 outline.setSize(r.width - DPtoLP_1*2, r.height - ROOF_HEIGHT - DPtoLP_1); g.drawRectangle(outline); } public PointList getPolygonPoints() { PointList ptList = new PointList(); Rectangle rect = getHandleBounds().getCopy().expand(DPtoLP_1,DPtoLP_1); Point p1 = rect.getBottomRight(); Point p2 = rect.getBottomLeft(); Point p3 = rect.getTopLeft().getTranslated(0, ROOF_HEIGHT); Point p4 = rect.getTopLeft().getTranslated((int)(rect.width*0.5), 0); Point p5 = rect.getTopRight().getTranslated(0, ROOF_HEIGHT); ptList.addPoint(p1); ptList.addPoint(p2); ptList.addPoint(p3); ptList.addPoint(p4); ptList.addPoint(p5); ptList.addPoint(p1); return ptList; } }
Adding compartments is done in the same way as adding any other view child. The compartments require a view and corresponding edit part. Compartments can be made to either contain other shapes or list items:
To add compartments as children to a view, override the decorateView
method
of the compartments parent view factory and append the compartment views as children.
public class MyParentViewFactory extends AbstractShapeViewFactory { protected void decorateView(View containerView, View view, IAdaptable element, String semanticHint, int index, boolean persisted) { super.decorateView(containerView, view, element, semanticHint, index, persisted); getViewService().createNode(element, view, "myListCompartment", ViewUtil.APPEND, persisted, getPreferencesHint()); getViewService().createNode(element, view, "myShapeCompartment", ViewUtil.APPEND, persisted, getPreferencesHint()); } }
There needs to be a view provider which will provide a view for each of the compartments.
Extend the
org.eclipse.gmf.runtime.diagram.core.viewProviders
extension-point and
set the viewProvider class
to be a class which extends
org.eclipse.gmf.runtime.diagram.core.providers.AbstractViewProvider
.
Add a context to the view provider extension for the myShapeCompartment
and
myListCompartment
semanticHints
.
<extension point="org.eclipse.gmf.runtime.diagram.core.viewProviders"> <viewProvider class="com.ibm.examples.providers.MyCompartmentsViewProvider"> <Priority name="Medium"/> <context semanticHints="myShapeCompartment, myListCompartment" viewClass="org.eclipse.gmf.runtime.notation.Node"/> </viewProvider> </extension>
The MyCompartmentsViewProvider
class should
return a view factory class for the myShapeCompartment
and
myListCompartment
semantic hints.
org.eclipse.gmf.runtime.diagram.ui.view.factories.ResizableCompartmentViewFactory
class, or instance of,
for the myShapeCompartment
.org.eclipse.gmf.runtime.diagram.ui.view.factories.ListCompartmentViewFactory
class, or instance of,
for the myListCompartment
.public class MyCompartmentsViewProvider extends AbstractViewProvider { private Map nodeMap = new HashMap(); { nodeMap.put("myShapeCompartment", ResizableCompartmentViewFactory.class); nodeMap.put("myListCompartment", ListCompartmentViewFactory.class); } protected Class getNodeViewClass(IAdaptable semanticAdapter, View containerView, String semanticHint) { return (Class)nodeMap.get(semanticHint); } }
In order to create a visual representations of the notation view provided by the view provider,
extend the
org.eclipse.gmf.runtime.diagram.ui.editpartProviders
extension-point and set the editpartProvider class
to be a class which extends
org.eclipse.gmf.runtime.diagram.ui.services.editpart.AbstractEditPartProvider
.
Set the enablement critiera to be a org.eclipse.gmf.runtime.notation.Node
class whose
type is the expected myShapeCompartment
or myListCompartment
type.
<extension point="org.eclipse.gmf.runtime.diagram.ui.editpartProviders"> <editpartProvider class="com.ibm.examples.providers.MyCompartmentsEditPartProvider"> <Priority name="Medium"/> <object class="org.eclipse.gmf.runtime.notation.Node(org.eclipse.gmf.runtime.notation)" id="myCompartments"> <method name="getType()" value="myShapeCompartment, myListCompartment"/> </object> <context views="myCompartments"/> </editpartProvider> </extension>
The MyCompartmentEditPartProvider
class should
return an edit part class for the myShapeCompartment
and
myListCompartment
view types.
org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeCompartmentEditPart
for the
myShapeCompartment
view type.org.eclipse.gmf.runtime.diagram.ui.editparts.ListCompartmentEditPart
for the
myListCompartment
view type.public class MyCompartmentEditPartProvider extends AbstractEditPartProvider { private Map nodeMap = new HashMap(); { nodeMap.put("myShapeCompartment", MyShapeCompartmentEditPart.class); nodeMap.put("myListCompartment", MyListCompartmentEditPart.class); } protected Class getNodeEditPartClass(View view) { return (Class) nodeMap.get(view.getType()); } }
The MyShapeCompartmentEditPart
class can
be as simple as extending
ShapeCompartmentEditPart
and implementing a constructor.
ShapeCompartmentEditPart
is a generic (sub) shape container that holds instances of
ShapeNodeEditPart
s and manages the display of
ConnectionNodeEditPart
s anchored to these shape edit part instances.
public class MyShapeCompartmentEditPart extends ShapeCompartmentEditPart { public MyShapeCompartmentEditPart(View view) { super(view); } }
List compartments are generally created to be canonical. In order to add canonical behavior
to the MyListCompartmentEditPart
class
install a canonical edit policy for the
EditPolicyRoles#CANONICAL_ROLE
. Override the
hasModelChildrenChanged(Notification evt)
method to
determine if a given event affects the semantic model children.
public class MyListCompartmentEditPart extends ListCompartmentEditPart { public MyListCompartmentEditPart(EObject model) { super(model); } protected void createDefaultEditPolicies() { super.createDefaultEditPolicies(); installEditPolicy(EditPolicyRoles.CANONICAL_ROLE, new MyCanonicalEditPolicy()); } protected boolean hasModelChildrenChanged(Notification evt) { Object feature = evt.getFeature(); return feature == UMLPackage.eINSTANCE.getClass_OwnedOperation(); } }
The MyCanonicalEditPolicy
class should extend
org.eclipse.gmf.runtime.diagram.ui.editpolicies.CanonicalEditPolicy
and implement
the
CanonicalEditPolicy#getSemanticChildrenList()
method.
CanonicalEditPolicy#getSemanticChildrenList()
should return a list of
semantic elements to be displayed in the list compartment.
public class MyCanonicalEditPolicy extends CanonicalEditPolicy { protected List getSemanticChildrenList() { return ((Class) resolveSemanticElement()).getOwnedOperations(); } protected String getDefaultFactoryHint() { return CommonParserHint.NAME; } }
Note: An edit part needs to be provided for each semantic child. The edit part for list
items generally extends the
org.eclipse.gmf.runtime.diagram.ui.editparts.TextCompartmentEditPart
class.
Canonical: Canonical is the terminology used to describe a container that keeps its view of semantic data synchronized with the semantic children. This means that the child views of canonical containers are created on demand whenever the semantic children change. When the diagram is saved, canonical views are not persisted in the diagram model.
Start by creating a class which extends
org.eclipse.gmf.runtime.diagram.ui.editpolicies.DragDropEditPolicy
. Override
DragDropEditPolicy.getDropElementCommand(EObject element, DropObjectsRequest request)
to return the drop command.
In this example, a command which adds a dropped UML Package to the container UML Package is returned.
public class MyDragDropEditPolicy extends DragDropEditPolicy { protected Command getDropElementCommand(EObject element, DropObjectsRequest request) { if (element instanceof Package) { final Package item = (Package)element; return new ICommandProxy( new AbstractTransactionalCommand(getEditingDomain(), "Add Item", null) { protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info) { Package container = (Package)ViewUtil.resolveSemanticElement((View)getHost().getModel()) container.getNestedPackages().add(item); return CommandResult.newOKCommandResult(); } }); } return null; } }
This edit policy needs to be installed on an edit part. In the edit part class, install the
MyDragDropEditPolicy
edit policy for the
EditPolicyRoles.DRAG_DROP_ROLE
role.
public class MyShapeNodeEditPart extends ShapeNodeEditPart { ... protected void createDefaultEditPolicies() { super.createDefaultEditPolicies(); installEditPolicy(EditPolicyRoles.DRAG_DROP_ROLE, new MyDragDropEditPolicy()); } ... }
To install this, or any other, edit policy onto an existing edit part, see the Installing Edit Policies on Existing Edit Parts section for more info
Adding double-clicking behaviour to edit parts is generally useful to open another editor using model element of the edit part as the context. However it can be used to perform any sort of command. This example will demonstrate how to add double-click behaviour to an edit part to be executed which will open a message dialog.
Start by creating a class which extends
org.eclipse.gmf.runtime.diagram.ui.editpolicies.OpenEditPolicy
. Override
OpenEditPolicy.getOpenCommand(Request request)
to return the open command. In this example, a command which opens a
message dialog is returned.
public class MyOpenEditPolicy extends OpenEditPolicy { protected Command getOpenCommand(Request request) { return new Command("Test double-click") { public void execute() { Shell shell = new Shell(); MessageDialog.openInformation( shell, "My Title", "Double-click command executed."); } }; } }
This edit policy needs to be installed on an edit part. In the edit part class, install the
MyOpenEditPolicy
edit policy for the
EditPolicyRoles.OPEN_ROLE
role.
public class MyShapeNodeEditPart extends ShapeNodeEditPart { ... protected void createDefaultEditPolicies() { super.createDefaultEditPolicies(); installEditPolicy(EditPolicyRoles.OPEN_ROLE, new MyOpenEditPolicy()); } ... }
To install this, or any other, edit policy onto an existing edit part, see the Installing Edit Policies on Existing Edit Parts section for more info
In the above examples for Adding Drag and Drop Behaviour and
Adding Double-Click Behaviour, it is possible to install these
edit policies onto existing edit parts in the RMP UML Modeler. This is done by extending the
org.eclipse.gmf.runtime.diagram.ui.editpolicyProviders
extension-point. Defining the extension is similar to that for view and edit part providers.
editpolicyprovider
and set the class
to be a class which implements
org.eclipse.gmf.runtime.diagram.ui.services.editpolicy.IEditPolicyProvider
.IGraphicalEditPart
and additionally
check the semantic element of the edit part. In this example, edit policies will be provided
for any edit part whose semantic element is a UML Interface.<extension point="org.eclipse.gmf.runtime.diagram.ui.editpolicyProviders"> <editpolicyProvider class="com.ibm.examples.providers.MyEditPolicyProvider"> <Priority name="Medium"/> <object class="org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart(org.eclipse.gmf.runtime.diagram.ui)" id="InterfaceEP"> <method name="resolveSemanticElement().eClass().getName()" value="Interface"/> </object> <context editparts="InterfaceEP"/> </editpolicyProvider> </extension>
The MyEditPolicyProvider
should include
the same, or stricter, provides criteria defined in the xml extension. It should also implement
createEditPolicies(EditPart ep)
to create and install the edit policy on the
given edit part. In this example, the MyOpenEditPolicy
is
installed on all edit parts with semantic element of type UML Interface.
public class MyEditPolicyProvider extends AbstractProvider implements IEditPolicyProvider { public void createEditPolicies(EditPart ep) { if (ep instanceof IGraphicalEditPart) { EObject element = ((IGraphicalEditPart)ep).resolveSemanticElement(); if (element instanceof Interface) { ep.installEditPolicy(EditPolicyRoles.OPEN_ROLE, new MyOpenEditPolicy()); } } } public boolean provides(IOperation operation) { if (operation instanceof CreateEditPoliciesOperation) { EditPart ep = ((CreateEditPoliciesOperation) operation).getEditPart(); if (ep instanceof IGraphicalEditPart && ep instanceof IPrimaryEditPart) { EObject element = ((IGraphicalEditPart)ep).resolveSemanticElement(); return element instanceof Interface; } } return false; } }
The Decoration Service gives clients the ability to decorate diagram elements with an image or figure. A provider of the decoration service will be able to add an adornment to any diagram element. The decoration is typically an image, but can be any sort of graphics object or figure. A provider of the decoration service is not restricted to any specific graphic type.
To add decorations extend the
org.eclipse.gmf.runtime.diagram.ui.decoratorProviders
extension-point.
decoratorProvider
.decoratorProvider class
to be a class which extends
org.eclipse.gmf.runtime.common.core.service.AbstractProvider
and implements
org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoratorProvider
.decoratorTargets
for which the decorator provider provides for. In this example
the decorator provider will provide only for an
IPrimaryEditPart
whose semantic element is of type UML Class
.
The IPrimaryEditPart
interface is a common interface implemented by
ConnectionEditPart
and ShapeEditPart
.<extension point="org.eclipse.gmf.runtime.diagram.ui.decoratorProviders"> <decoratorProvider class="com.ibm.examples.providers.MyDecoratorProvider"> <Priority name="Lowest"/> <object class="org.eclipse.gmf.runtime.diagram.ui.editparts.IPrimaryEditPart(org.eclipse.gmf.runtime.diagram.ui)" id="class"> <method name="resolveSemanticElement().eClass().getName()" value="Class"/> </object> <context decoratorTargets="class"/> </decoratorProvider> </extension>
The MyDecoratorProvider
should provide for the same, or stricter,
criteria which was used in the xml extension.
In the MyDecoratorProvider#createDecorators(IDecoratorTarget decoratorTarget)
method, install the decorator on the decoratorTarget
using a unique key.
If another decorator is installed on the same target with the same key then it will override the previous one installed.
GMF defines a few standard decorator keys in the
IDecoratorKeys
interface:
BOOKMARK
, CROSS_MODEL_REFERENCE
, and UNRESOLVED_VIEW
. Since this example decorator will not replace any of
the decorators for the pre-defined keys, an arbitrary unique key is used.
public class MyDecoratorProvider extends AbstractProvider implements IDecoratorProvider { public void createDecorators(IDecoratorTarget decoratorTarget) { IGraphicalEditPart editPart = (IGraphicalEditPart)decoratorTarget.getAdapter(IGraphicalEditPart.class); if (editPart instanceof IPrimaryEditPart) { EObject eObject = editPart.resolveSemanticElement(); if (eObject instanceof Class) { decoratorTarget.installDecorator("myDecoratorKey", new MyDecorator(decoratorTarget)); } } } public boolean provides(IOperation operation) { if (!(operation instanceof CreateDecoratorsOperation)) { return false; } IDecoratorTarget decoratorTarget = ((CreateDecoratorsOperation) operation).getDecoratorTarget(); IGraphicalEditPart editPart = (IGraphicalEditPart)decoratorTarget.getAdapter(IGraphicalEditPart.class); if (editPart instanceof IPrimaryEditPart) { EObject eObject = editPart.resolveSemanticElement(); return eObject instanceof Class; } return false; } }
The installed decorator should extend
org.eclipse.gmf.runtime.diagram.ui.services.decorator.AbstractDecorator
.
The IDecorator#refresh()
method is responsible for setting / removing the decoration from the target.
In this example, a NotificationListener
is added to the DiagramEventBroker
for the element of interest in the
MyDecorator#activate()
method.
The DiagramEventBroker
is a model server listener that broadcasts EObject events to all registered listeners.
This will allow the listener in the decorator to refresh the decoration whenever the model changes.
The listener is removed from the element in the MyDecorator#deactivate()
deactivate method.
The DiagramEventBroker
is a model server listener that broadcasts EObject events to all registered listeners.
The MyDecorator#refresh()
method has the responsibility
of setting / removing the decoration from the target. In this example, shape decorations are added using the
IDecoratorTarget.addShapeDecoration(..)
method. Adding shape decorations requires passing a direction for the location of the decoration.
The available directions are found in the
IDecoratorTarget.Direction
enumeration. The possible directions are:
CENTER
, EAST
, NORTH
, NORTH_EAST
, NORTH_WEST
, SOUTH
, SOUTH_EAST
, SOUTH_WEST
, and WEST
.
If the decoration target is a connection, use the
IDecoratorTarget.addConnectionDecoration(..)
method to add decorations along any point of the connection.
public class MyDecorator extends AbstractDecorator { private final Image ABSTRACT_IMG = ImageDescriptor.createFromURL("file://abstract.gif").createImage(); private ChangeListener changeListener; protected EObject notifier; private class ChangeListener implements NotificationListener { IDecorator decorator; public ChangeListener(IDecorator decorator) { this.decorator = decorator; } public void notifyChanged(Notification evt) { if (evt.getFeature() == UMLPackage.eINSTANCE.getClassifier_IsAbstract()) { decorator.refresh(); } } } public MyDecorator(IDecoratorTarget decoratorTarget) { super(decoratorTarget); changeListener = new ChangeListener(this); } public void activate() { IGraphicalEditPart editPart = (IGraphicalEditPart) getDecoratorTarget().getAdapter(IGraphicalEditPart.class); EObject element = editPart.resolveSemanticElement(); if (element instanceof Class) { notifier = element; DiagramEventBroker.getInstance(UMLModeler.getEditingDomain()). addNotificationListener(notifier, changeListener); } } public void deactivate() { if (notifier != null) { DiagramEventBroker.getInstance(UMLModeler.getEditingDomain()). removeNotificationListener(notifier, changeListener); notifier = null; } super.deactivate(); } public void refresh() { IGraphicalEditPart editPart = (IGraphicalEditPart) getDecoratorTarget().getAdapter(IGraphicalEditPart.class); EObject element = editPart.resolveSemanticElement(); if (element instanceof Class) { Class clazz = (Class)element; removeDecoration(); if (clazz.isAbstract()) { setDecoration(getDecoratorTarget().addShapeDecoration(ABSTRACT_IMG, IDecoratorTarget.Direction.NORTH_EAST, 0, false)); } } } }