| <?xml version='1.0' encoding='utf-8' ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| <html xmlns="http://www.w3.org/1999/xhtml"> |
| <head> |
| <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> |
| <title>extensions-provide_custom_style</title> |
| <link type="text/css" rel="stylesheet" href="../resources/bootstrap.css"/> |
| <link type="text/css" rel="stylesheet" href="../resources/custom.css"/> |
| |
| </head> |
| <body> |
| <h1 id="SiriusProvidecustomstyle">Sirius – Provide custom style</h1> |
| <h2 id="Goals">Goals</h2> |
| <p>Sirius provides tools to design and display diagrams. These diagrams are essentially composed of figures of distinct styles |
| <br/>and shapes and, even though the tooling initially allows for the usage of a wide array of styles, many diagrams will still require |
| <br/>customized styles to conform to the company’s theme. |
| </p> |
| <p>Sirius offers three distinct ways of customizing its graphical styles:</p> |
| <ol> |
| <li>the |
| <code>StyleConfiguration</code> extension point. |
| </li> |
| <li>the |
| <code>CustomStyle</code> in addition to the |
| <code>EditPartProvider</code> GMF extension point. |
| </li> |
| <li>the GMF-provided extension points.</li> |
| </ol> |
| <h2 id="StyleConfiguration">StyleConfiguration</h2> |
| <p>StyleConfiguration is a mechanism that enables the customization of a specific style. It aims at providing support for different |
| <br/>ways of displaying labels, anchors, ... |
| </p> |
| <h3 id="TheStyleConfigurationinterface">The StyleConfiguration interface</h3> |
| <p>This interface defines a set of methods that need be implemented in order for the syle to be customizable:</p> |
| <ul> |
| <li> |
| <code>void adaptNodeLabel(DNode node, WrapLabel nodeLabel)</code> |
| </li> |
| <li> |
| <code>BorderItemLocatorProvider getBorderItemLocatorProvider()</code> |
| </li> |
| <li> |
| <code>IBorderItemLocator getNameBorderItemLocator(DNode node, IFigure mainFigure)</code> |
| </li> |
| <li> |
| <code>int adaptViewNodeSizeWithLabel(DNode viewNode, WrapLabel nodeLabel, int nodeWidth)</code> |
| </li> |
| <li> |
| <code>AnchorProvider getAnchorProvider()</code> |
| </li> |
| <li> |
| <code>Image getLabelIcon(DDiagramElement : element)</code> |
| </li> |
| <li> |
| <code>Dimension fitToText(DNode node, WrapLabel nodeLabel, DefaultSizeNodeFigure defaultSizeNodeFigure)</code> |
| </li> |
| </ul> |
| <h4 id="adaptNodeLabel">adaptNodeLabel</h4> |
| <p>This method can be used to alter the size and location of a given label according to a given node.</p> |
| <ul> |
| <li>Parameters |
| <ul> |
| <li> |
| <code>viewNode</code> : The node representation. |
| </li> |
| <li> |
| <code>nodeLabel</code> : Label of the node. |
| </li> |
| </ul> |
| </li> |
| </ul> |
| <h4 id="getBorderItemLocatorProvider">getBorderItemLocatorProvider</h4> |
| <p>This method returns the instance currently providing border item locators for the node. A border item is a node located on the border of another node.</p> |
| <h4 id="getNameBorderItemLocator">getNameBorderItemLocator</h4> |
| <p>This method returns the item locator for the label of the node. This method is meaningless if the position of the label isn’t |
| <code>BORDER</code>. |
| </p> |
| <ul> |
| <li>Parameters |
| <ul> |
| <li> |
| <code>node</code> : The node representation. |
| </li> |
| <li> |
| <code>mainFigure</code> : The Draw2D figure representing the node. |
| </li> |
| </ul> |
| </li> |
| </ul> |
| <h4 id="adaptViewNodeSizeWithLabel">adaptViewNodeSizeWithLabel</h4> |
| <p>This method returns the minimum width a node needs to be correctly displayed with its label.</p> |
| <ul> |
| <li>Parameters |
| <ul> |
| <li> |
| <code>viewNode</code> : The node representation. |
| </li> |
| <li> |
| <code>nodeLabel</code> : Label of the node. |
| </li> |
| <li> |
| <code>nodeWidth</code> : Current width of the node. |
| </li> |
| </ul> |
| </li> |
| </ul> |
| <h4 id="getAnchorProvider">getAnchorProvider</h4> |
| <p>This method returns the instance providing connections anchors.</p> |
| <h4 id="getLabelIcon">getLabelIcon</h4> |
| <p>This method returns the icon that will displayed for this element’s representations.</p> |
| <ul> |
| <li>Parameters |
| <ul> |
| <li> |
| <code>vpElement</code> : The element for which we need an icon. |
| </li> |
| </ul> |
| </li> |
| </ul> |
| <h4 id="fitToText">fitToText</h4> |
| <p>This method returns the optimal dimension of the node.</p> |
| <ul> |
| <li>Parameters |
| <ul> |
| <li> |
| <code>node</code> : The node representation. |
| </li> |
| <li> |
| <code>nodeLabel</code> : Label of the node. |
| </li> |
| <li> |
| <code>defaultSizeNodeFigure</code> : The draw2d representation of the node. |
| </li> |
| </ul> |
| </li> |
| </ul> |
| <h3 id="ProvidingcustomStyleConfiguration">Providing custom StyleConfiguration</h3> |
| <p>The |
| <code>StyleConfiguration</code> mechanism is only available for |
| <code>ViewNode</code> at the moment. Default configurations are initially installed on nodes. |
| </p> |
| <p> |
| <b>SimpleStyleConfiguration</b> |
| </p> |
| <p>This configuration is the simplest implementation.</p> |
| <p> |
| <b>SquareStyleConfiguration</b> |
| </p> |
| <p>This is the minimal configuration for nodes using the |
| <code>BundledImage</code> style with a square shape. |
| </p> |
| <p> |
| <b>SimpleSquareStyleConfiguration</b> |
| </p> |
| <p>This is the minimal configuration for nodes with a |
| <code>Square</code> style. |
| </p> |
| <h4 id="ProvidersofStyleConfiguration">Providers of StyleConfiguration</h4> |
| <p>Installing your own configurations can be done through the |
| <code>org.eclipse.sirius.diagram.styleConfigurationProvider</code> extension |
| <br/>point. This extension point requires only one attribute : the qualified name of the provider class. |
| </p> |
| <pre><code><extension point="org.eclipse.sirius.diagram.styleConfigurationProvider"> |
| <styleConfigurationProvider providerClass="com.example.diagseq.style.DiagSeqStyleConfigurationProvider" /> |
| </extension> |
| |
| </code></pre> |
| <p>Here is a sample of the extension tab of the PDE editor when filling in this extension point :</p> |
| <p> |
| <img border="0" src="./images/style/styleConfigurationProviderExt.png"/> |
| </p> |
| <p>This example records the |
| <code>DiagSeqStyleConfigurationProvider</code> as a configuration provider. This class must implement the |
| <br/> |
| <code>IStyleConfigrationProvider</code> interface. |
| </p> |
| <p>The latter interface defines two methods :</p> |
| <p> |
| <strong> |
| <code>boolean provides(DiagramElementMapping mapping, Style style)</code> |
| </strong> |
| </p> |
| <p>This method returns true if the provider provides a configuration for the given mapping and style.</p> |
| <ul> |
| <li>Parameters |
| <ul> |
| <li> |
| <code>mapping</code> : The mapping of the node. |
| </li> |
| <li> |
| <code>style</code> : Style of the node. |
| </li> |
| </ul> |
| </li> |
| </ul> |
| <p> |
| <strong> |
| <code>StyleConfiguration createStyleConfiguration(DiagramElementMapping mapping, Style style)</code> |
| </strong> |
| </p> |
| <p>This method creates a StyleConfiguration for the specified mapping and style.</p> |
| <ul> |
| <li>Parameters |
| <ul> |
| <li> |
| <code>mapping</code> : Mapping of the node. |
| </li> |
| <li> |
| <code>style</code> : Style of the node. |
| </li> |
| </ul> |
| </li> |
| </ul> |
| <p>Here is an example of a provider :</p> |
| <pre><code>public class DiagSeqStyleConfigurationProvider implements IStyleConfigurationProvider { |
| public StyleConfiguration createStyleConfiguration(DiagramElementMapping mapping, Style style) { |
| if (mapping instanceof NodeMapping) { |
| NodeMapping nodeMapping = (NodeMapping) mapping; |
| if (nodeMapping.getName() != null && nodeMapping.getName().equals(DiagSeqConstants.INSTANCE_ROLE_MAPPING_NAME)) { |
| return new InstanceRoleStyleConfiguration(); |
| } |
| if (nodeMapping.getName() != null && nodeMapping.getName().equals(DiagSeqConstants.EXECUTION_MAPPING_NAME)) { |
| return new ExecutionSpecificationStyleConfiguration(); |
| } |
| } |
| return null; |
| } |
| |
| public boolean provides(DiagramElementMapping mapping, Style style) { |
| return mapping instanceof NodeMapping && ((NodeMapping) mapping).getName() != null |
| && (((NodeMapping) mapping).getName().equals(DiagSeqConstants.INSTANCE_ROLE_MAPPING_NAME) |
| || DiagSeqConstants.EXECUTION_MAPPING_NAME.equals(((NodeMapping) mapping).getName())); |
| } |
| } |
| |
| |
| </code></pre> |
| <h3 id="ExampleofaStyleConfigurationimplementation">Example of a StyleConfiguration implementation</h3> |
| <pre><code>public class SimpleStyleConfiguration implements StyleConfiguration { |
| public void adaptNodeLabel(DNode node, WrapLabel nodeLabel) { |
| if (nodeLabel.getParent() != null) { |
| Rectangle constraint = nodeLabel.getParent().getBounds().getCopy(); |
| |
| Insets borderDimension = this.getBorderDimension(node); |
| constraint.height -= (borderDimension.top + borderDimension.bottom); |
| constraint.width -= (borderDimension.left + borderDimension.right); |
| constraint.x += borderDimension.left; |
| constraint.y += borderDimension.top; |
| |
| nodeLabel.setBounds(constraint); |
| nodeLabel.getParent().setConstraint(nodeLabel, constraint); |
| } |
| } |
| |
| public int adaptViewNodeSizeWithLabel(DNode viewNode, WrapLabel nodeLabel, int nodeWidth) { |
| if (viewNode.getResizeKind() != ResizeKind.NONE_LITERAL) { |
| |
| } |
| return nodeWidth; |
| } |
| |
| public AnchorProvider getAnchorProvider() { |
| return null; |
| } |
| |
| public BorderItemLocatorProvider getBorderItemLocatorProvider() { |
| return DefaultBorderItemLocatorProvider.getInstance(); |
| } |
| |
| public IBorderItemLocator getNameBorderItemLocator(DNode node, IFigure mainFigure) { |
| BorderItemLocator locator = new AirBorderItemLocator(mainFigure, PositionConstants.NSEW); |
| locator.setBorderItemOffset(new Dimension(1, 1)); |
| return locator; |
| } |
| |
| public Image getLabelIcon(DDiagramElement vpElement) { |
| EObject target = vpElement; |
| if (vpElement instanceof DSemanticDecorator) { |
| target = ((DSemanticDecorator) vpElement).getTarget(); |
| } |
| if (isShowIcon(vpElement)) { |
| if (target != null) { |
| IItemLabelProvider labelProvider = (IItemLabelProvider) SiriusDiagramEditorPlugin.getInstance() |
| .getItemProvidersAdapterFactory().adapt(target, IItemLabelProvider.class); |
| if (labelProvider != null) { |
| ImageDescriptor descriptor = ExtendedImageRegistry.getInstance().getImageDescriptor(labelProvider.getImage(target)); |
| if (descriptor == null) { |
| descriptor = ImageDescriptor.getMissingImageDescriptor(); |
| } |
| return SiriusDiagramEditorPlugin.getInstance().getImage(descriptor); |
| } |
| } |
| } |
| return null; |
| } |
| |
| protected boolean isShowIcon(DDiagramElement vpElement) { |
| if (vpElement instanceof MappingBased) { |
| DiagramElementMapping vpElementMapping = ((MappingBased) vpElement).getMapping(); |
| if (vpElementMapping instanceof NodeMapping) { |
| return ((NodeMapping) vpElementMapping).isShowIcon(); |
| } |
| if (vpElementMapping instanceof EdgeMapping) { |
| return ((EdgeMapping) vpElementMapping).isShowIcon(); |
| } |
| if (vpElementMapping instanceof ContainerMapping) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public Dimension fitToText(DNode node, WrapLabel nodeLabel, DefaultSizeNodeFigure defaultSizeNodeFigure) { |
| if (nodeLabel.getFont() != null) { |
| String text = node.getName(); |
| |
| int charHeight = FigureUtilities.getStringExtents("ABCDEF", nodeLabel.getFont()).height + 5; |
| int charWidth = FigureUtilities.getTextWidth("ABCDEFGHIJKLMNOPQRSTUVWXYZ", nodeLabel.getFont()) / 26; |
| |
| double ratio = charHeight / charWidth; |
| |
| int nbLines = (int) (Math.sqrt(text.length()) / ratio) + 1; |
| int nbCols = (int) (Math.sqrt(text.length()) * ratio) + 1; |
| |
| int longestWord = this.getTheLongestWord(text.split("\\s")); |
| nbCols = Math.max(nbCols, longestWord); |
| |
| int hHeight = nbLines * charHeight; |
| int hWidth = nbCols * charWidth; |
| |
| Dimension size = nodeLabel.getPreferredSize(hWidth + nodeLabel.getIconBounds().width + |
| nodeLabel.getIconTextGap(), hHeight).getCopy(); |
| |
| size.width += 20; |
| size.height += 30; |
| |
| Insets borderDimension = this.getBorderDimension(node); |
| size.width += (borderDimension.left + borderDimension.right); |
| size.height += (borderDimension.top + borderDimension.bottom); |
| |
| // |
| // Square ? |
| if (node.getHeight().intValue() == node.getWidth().intValue()) { |
| // size.width = Math.max(size.height, size.width); |
| // size.height = Math.max(size.height, size.width); |
| } |
| |
| return size; |
| } |
| return defaultSizeNodeFigure.getBounds().getSize().getCopy(); |
| } |
| |
| private int getTheLongestWord(String[] strings) { |
| int max = -1; |
| for (int i = 0; i < strings.length; i++) { |
| if (strings[i].length() > max) { |
| max = strings[i].length(); |
| } |
| } |
| return max; |
| } |
| |
| /** |
| * Return the dimension of the border. |
| * |
| * @param nodeth |
| * view node. |
| * @return the dimension of the border. |
| */ |
| public Insets getBorderDimension(DNode node) { |
| return new Insets(0, 0, 0, 0); |
| } |
| |
| } |
| |
| </code></pre> |
| <p>The easiest way to implement your own configuration is to make it a sub class of |
| <code>SimpleStyleConfiguration</code>. |
| </p> |
| <h2 id="GMFextensionpointsCustomStyleandEditPartProvider">GMF extension points, CustomStyle and EditPartProvider</h2> |
| <p> |
| <code>CustomStyle</code> is a style that can be applied to a node. Its only property is |
| <code>id</code> that is a string value. By default, |
| <br/>applying a CustomStyle will have a Green Square be displayed. A custom |
| <code>EditPart</code> describing the appearance of the node |
| <br/>must be implemented. |
| </p> |
| <p>Here is how to apply a |
| <code>CustomStyle</code>: |
| </p> |
| <p> |
| <img border="0" src="./images/style/customStyle.png"/> |
| </p> |
| <p>Next step is to create and record an |
| <code>EditPart</code> to define the way this style will be managed. |
| </p> |
| <p>An |
| <code>EditPart</code> is a GEF class. It references both a model element and the shape that represents it. GMF adds a layer to GEF |
| <br/>and exposes its own |
| <strong>EditPart</strong> API. The base type of this API is |
| <code>IGraphicalEditPart</code> (in package |
| <br/> |
| <code>org.eclipse.gmf.runtime.diagram.ui.editparts</code>). Here are the main methods this interface defines : |
| </p> |
| <ul> |
| <li> |
| <code>EObject resolveSemanticElement()</code>: This method returns the semantic element of the edit part. The type of this semantic element is a type contained by the Sirius package (see |
| <code>SiriusPackage</code>). For instance, if we were to write an |
| <code>EditPart</code> for our |
| <code>CustomStyle</code>, the semantic element would be the said |
| <code>CustomStyle</code>. It is possible to get the ViewNode with it and, then, the target element. |
| </li> |
| <li> |
| <code>View getNotationView()</code>: This method returns the GMF View of the edit part. This is the view that will be saved into the GMF Diagram. |
| </li> |
| </ul> |
| <p>All Custom Style Edit Part must implement the |
| <code>IStyleEditPart</code> interface. Semanticly, a Style Edit Part should not be selectable : it is selected when the Shape or Connector that contains it is selected. Therefore, |
| <strong>all Custom Style Edit Part should override the |
| <code>isSelectable()</code> method and return false |
| </strong>. If you can, inherit from the abstrac class |
| <code>AbstractNotSelectableShapeNodeEditPart</code> that already overrides correctly this method. |
| </p> |
| <p>Here is a sample of a Custom Style Edit Part :</p> |
| <pre><code>public class InstanceRoleStyleEditPart extends AbstractNotSelectableShapeNodeEditPart implements IStyleEditPart { |
| |
| /** |
| * the content pane. |
| */ |
| protected IFigure contentPane; |
| |
| /** |
| * the primary shape. |
| */ |
| protected ImageFigure primaryShape; |
| |
| /** |
| * Create a new {@link ChangingImageEditPart}. |
| * |
| * @param view |
| * the view. |
| */ |
| public InstanceRoleStyleEditPart(View view) { |
| super(view); |
| } |
| |
| public DragTracker getDragTracker(Request request) { |
| return getParent().getDragTracker(request); |
| } |
| |
| protected NodeFigure createNodeFigure() { |
| NodeFigure figure = createNodePlate(); |
| figure.setLayoutManager(new XYLayout()); |
| IFigure shape = createNodeShape(); |
| figure.add(shape); |
| contentPane = setupContentPane(shape); |
| return figure; |
| } |
| |
| private NodeFigure createNodePlate() { |
| DefaultSizeNodeFigure result = new AirStyleDefaultSizeNodeFigure(getMapMode().DPtoLP(40), getMapMode().DPtoLP(40)); |
| return result; |
| } |
| |
| /** |
| * Create the instance role figure. |
| * |
| * @return the created figure. |
| */ |
| protected ImageFigure createNodeShape() { |
| if (primaryShape == null) { |
| primaryShape = new ImageFigure(); |
| } |
| return primaryShape; |
| } |
| |
| /** |
| * Return the instance role figure. |
| * |
| * @return the instance role figure. |
| */ |
| public ImageFigure getPrimaryShape() { |
| return primaryShape; |
| } |
| |
| /** |
| * Default implementation treats passed figure as content pane. Respects |
| * layout one may have set for generated figure. |
| * |
| * @param nodeShape |
| * instance of generated figure class |
| * @return the figure |
| */ |
| protected IFigure setupContentPane(IFigure nodeShape) { |
| return nodeShape; // use nodeShape itself as contentPane |
| } |
| |
| public IFigure getContentPane() { |
| if (contentPane != null) { |
| return contentPane; |
| } |
| return super.getContentPane(); |
| } |
| |
| protected void refreshVisuals() { |
| CustomStyle customStyle = (CustomStyle) this.resolveSemanticElement(); |
| if (customStyle.eContainer() instanceof DNode) { |
| this.getPrimaryShape().setImage(SiriusPlugin.getDefault().getBundledImage(((DNode) customStyle.eContainer()).getName())); |
| } |
| } |
| |
| protected void createDefaultEditPolicies() { |
| // empty. |
| } |
| } |
| |
| </code></pre> |
| <p>We now have to notify Sirius that we are providing a new |
| <code>EditPart</code> for nodes having a CustomStyle of that id. |
| <br/>The GMF extension mechanism can be used to achieve this. |
| </p> |
| <p>plugin.xml:</p> |
| <p> |
| <img border="0" src="./images/style/editPartProvider_plugin1.png"/> |
| </p> |
| <pre><code><extension point="org.eclipse.gmf.runtime.diagram.ui.editpartProviders"> |
| <editpartProvider class="com.example.diagseq.provider.DiagSeqEditPartProvider"> |
| <Priority name="High"/> |
| </editpartProvider> |
| </extension> |
| |
| </code></pre> |
| <p>The provider class ( |
| <code>com.example.diagseq.provider.DiagSeqEditPartProvider</code>) extends the GMF class |
| <br/> |
| <code>org.eclipse.gmf.runtime.diagram.ui.services.editpart.AbstractEditPartProvider</code>. Our custom Edit part will be |
| <br/>provided by overriding |
| <code>getNodeEditPartClass</code>. Here is how this class looks like : |
| </p> |
| <pre><code>public class DiagSeqEditPartProvider extends AbstractEditPartProvider { |
| @Override |
| protected Class getNodeEditPartClass(View view) { |
| if (view.getElement() instanceof CustomStyle) { |
| CustomStyle customStyle = (CustomStyle) view.getElement(); |
| if (customStyle.getId().equals(DiagSeqConstants.INSTANCE_ROLE_STYLE_ID)) { |
| return InstanceRoleStyleEditPart.class; |
| } |
| } |
| return super.getNodeEditPartClass(view); |
| } |
| } |
| |
| </code></pre> |
| <p>Where |
| <code>DiagSeqConstants.INSTANCE_ROLE_STYLE_ID</code> is the id of our custom style. |
| </p> |
| <p>In the modeler, the result looks like this:</p> |
| <p> |
| <img border="0" src="./images/style/instanceRole.png"/> |
| </p> |
| <h2 id="GMFextensionpoints">GMF extension points</h2> |
| <p>GMF exposes all of the necessary API to extend the default behavior of a modeler.</p> |
| <ul> |
| <li>Edit Part provider.</li> |
| <li>Edit Policy Provider.</li> |
| <li>Layout Provider</li> |
| </ul> |
| <h3 id="EditPartProvider">Edit Part Provider</h3> |
| <p>We saw earlier how to provide custom edit parts. Here is how the result looks like in Sirius.</p> |
| <p>Snapshot of the Sirius meta-model:</p> |
| <p> |
| <img border="0" src="./images/style/viewpoint.jpg"/> |
| </p> |
| <p>The types DNode, DNodeContainer, DNodeList, DNodeListElement, etc. have their own edit parts:</p> |
| <p> |
| <img border="0" src="./images/style/editParts.jpg"/> |
| </p> |
| <h3 id="EditPolicyProvider">Edit Policy Provider</h3> |
| <p>Edit Policies are objects responsible for the handling of user actions. There are Edit Policies for different behaviors:</p> |
| <ul> |
| <li>Creation</li> |
| <li>Deletion</li> |
| <li>Move elements</li> |
| <li>Selection</li> |
| <li>etc.</li> |
| </ul> |
| <p>As with edit parts, it is necessary to write an Edit Policy Provider as well as the Edit Policy that is to be provided. The |
| <br/>Edit Policy Provider must implement the |
| <code>IEditPolicyProvider</code> interface: |
| </p> |
| <ul> |
| <li> |
| <code>public void addProviderChangeListener(IProviderChangeListener listener)</code>: Registers a listener on the provider. |
| </li> |
| <li> |
| <code>public boolean provides(IOperation operation)</code>: Returns true if this instance provides edit policies for the specified operation. |
| </li> |
| <li> |
| <code>public void removeProviderChangeListener(IProviderChangeListener listener)</code>: Removes a listener from this provider. |
| </li> |
| <li> |
| <code>public void createEditPolicies(EditPart editPart)</code>: Adds edit policies on the specified edit part. |
| </li> |
| </ul> |
| <p>Here is an example of an Edit Policy Provider</p> |
| <pre><code>/** |
| * Provides Edit Policy for Note Attachment. |
| */ |
| public class AirNoteAttachmentEditPolicyProvider implements IEditPolicyProvider { |
| |
| /** the property change support. */ |
| private List listeners; |
| |
| /** |
| * Create a new {@link AirNoteAttachmentEditPolicyProvider}. |
| */ |
| public AirNoteAttachmentEditPolicyProvider() { |
| this.listeners = new ArrayList(2); |
| } |
| |
| public void createEditPolicies(EditPart editPart) { |
| if (editPart instanceof NoteAttachmentEditPart) { |
| editPart.installEditPolicy(EditPolicy.CONNECTION_ROLE, new AirNoteAttachmentEditPolicy()); |
| } |
| } |
| |
| public void addProviderChangeListener(IProviderChangeListener listener) { |
| this.listeners.add(listener); |
| } |
| |
| public boolean provides(IOperation operation) { |
| if (operation instanceof CreateEditPoliciesOperation) { |
| CreateEditPoliciesOperation castedOperation = (CreateEditPoliciesOperation) operation; |
| EditPart editPart = castedOperation.getEditPart(); |
| Object model = editPart.getModel(); |
| if (model instanceof View) { |
| View view = (View) model; |
| if (view.getDiagram() != null && view.getDiagram().getElement() != null |
| && view.getDiagram().getElement().eClass().getEPackage().getNsURI().equals(ViewpointPackage.eINSTANCE.getNsURI())) { |
| if ("NoteAttachment".equals(view.getType())) { |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| public void removeProviderChangeListener(IProviderChangeListener listener) { |
| this.listeners.remove(listener); |
| } |
| |
| /** |
| * Fire a {@link ProviderChangeEvent}. |
| */ |
| protected void fireProviderChanged() { |
| ProviderChangeEvent event = new ProviderChangeEvent(this); |
| Iterator iterListener = this.listeners.iterator(); |
| while (iterListener.hasNext()) { |
| IProviderChangeListener listener = (IProviderChangeListener) iterListener.next(); |
| listener.providerChanged(event); |
| } |
| } |
| |
| } |
| |
| </code></pre> |
| <p>More flexibility can be achieved by writing an abstract provider :</p> |
| <pre><code>public abstract class AbstractEditPolicyProvider implements IEditPolicyProvider { |
| |
| /** All listeners. */ |
| private List listeners = new ArrayList(1); |
| |
| public void addProviderChangeListener(IProviderChangeListener listener) { |
| this.listeners.add(listener); |
| } |
| |
| public void removeProviderChangeListener(IProviderChangeListener listener) { |
| this.listeners.remove(listener); |
| } |
| |
| protected void fireProviderChanged() { |
| ProviderChangeEvent event = new ProviderChangeEvent(this); |
| Iterator iterListener = this.listeners.iterator(); |
| while (iterListener.hasNext()) { |
| IProviderChangeListener listener = (IProviderChangeListener) iterListener.next(); |
| listener.providerChanged(event); |
| } |
| } |
| } |
| |
| </code></pre> |
| <p>There only remains to override the methods |
| <code>provides()</code> and |
| <code>createEditPolicies()</code> in this provider’s implementations. |
| </p> |
| </body> |
| </html> |