/* * Copyright (c) 2013, IETR/INSA of Rennes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the IETR/INSA of Rennes nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ package net.sf.orcc.xdf.ui.diagram; import net.sf.orcc.df.Port; import net.sf.orcc.graph.Vertex; import net.sf.orcc.util.OrccLogger; import net.sf.orcc.xdf.ui.features.CopyFeature; import net.sf.orcc.xdf.ui.features.DropInstanceFromFileFeature; import net.sf.orcc.xdf.ui.features.PasteFeature; import net.sf.orcc.xdf.ui.features.UpdateDiagramFeature; import net.sf.orcc.xdf.ui.layout.AutoLayoutFeature; import net.sf.orcc.xdf.ui.patterns.ConnectionPattern; import net.sf.orcc.xdf.ui.patterns.InputNetworkPortPattern; import net.sf.orcc.xdf.ui.patterns.InstancePattern; import net.sf.orcc.xdf.ui.patterns.OutputNetworkPortPattern; import net.sf.orcc.xdf.ui.util.PropsUtil; import org.eclipse.core.resources.IFile; import org.eclipse.graphiti.dt.IDiagramTypeProvider; import org.eclipse.graphiti.features.IAddFeature; import org.eclipse.graphiti.features.ICopyFeature; import org.eclipse.graphiti.features.IFeature; import org.eclipse.graphiti.features.IPasteFeature; import org.eclipse.graphiti.features.IReconnectionFeature; import org.eclipse.graphiti.features.IRemoveFeature; import org.eclipse.graphiti.features.IUpdateFeature; import org.eclipse.graphiti.features.context.IAddContext; import org.eclipse.graphiti.features.context.IContext; import org.eclipse.graphiti.features.context.ICopyContext; import org.eclipse.graphiti.features.context.ICustomContext; import org.eclipse.graphiti.features.context.IPasteContext; import org.eclipse.graphiti.features.context.IPictogramElementContext; import org.eclipse.graphiti.features.context.IReconnectionContext; import org.eclipse.graphiti.features.context.IRemoveContext; import org.eclipse.graphiti.features.context.IUpdateContext; import org.eclipse.graphiti.features.context.impl.CreateConnectionContext; import org.eclipse.graphiti.features.context.impl.ReconnectionContext; import org.eclipse.graphiti.features.custom.ICustomFeature; import org.eclipse.graphiti.features.impl.DefaultReconnectionFeature; import org.eclipse.graphiti.features.impl.DefaultRemoveFeature; import org.eclipse.graphiti.mm.pictograms.Anchor; import org.eclipse.graphiti.mm.pictograms.Connection; import org.eclipse.graphiti.mm.pictograms.Diagram; import org.eclipse.graphiti.pattern.DefaultFeatureProviderWithPatterns; import de.cau.cs.kieler.kiml.klayoutdata.KShapeLayout; import de.cau.cs.kieler.kiml.options.EdgeRouting; import de.cau.cs.kieler.kiml.options.LayoutOptions; /** * This is a default class, from a new Graphiti project. It should be modified * to fit our needs. * * @author Antoine Lorence * */ public class XdfDiagramFeatureProvider extends DefaultFeatureProviderWithPatterns { private final PasteFeature pasteFeature; private final CopyFeature copyFeature; private final UpdateDiagramFeature updateFeature; private final DropInstanceFromFileFeature dropInstanceFeature; private final ICustomFeature[] layoutFeatures; public XdfDiagramFeatureProvider(IDiagramTypeProvider dtp) { super(dtp); addPattern(new InstancePattern()); addPattern(new InputNetworkPortPattern()); addPattern(new OutputNetworkPortPattern()); addConnectionPattern(new ConnectionPattern()); copyFeature = new CopyFeature(this); pasteFeature = new PasteFeature(this); updateFeature = new UpdateDiagramFeature(this); dropInstanceFeature = new DropInstanceFromFileFeature(this); final ICustomFeature orthogonal = new AutoLayoutFeature(this, "Orthogonal routing (default)") { @Override protected void configureDiagramNode(KShapeLayout diagramLayout) { super.configureDiagramNode(diagramLayout); diagramLayout.setProperty(LayoutOptions.EDGE_ROUTING, EdgeRouting.ORTHOGONAL); } }; final ICustomFeature polyline = new AutoLayoutFeature(this, "Polyline routing") { @Override protected void configureDiagramNode(KShapeLayout diagramLayout) { super.configureDiagramNode(diagramLayout); diagramLayout.setProperty(LayoutOptions.EDGE_ROUTING, EdgeRouting.POLYLINE); } }; // The first entry is the default layoutFeatures = new ICustomFeature[] { orthogonal, polyline }; } /** * Return the default layout feature. It is the first in the layout features * array * * @return */ public ICustomFeature getDefaultLayoutFeature() { return layoutFeatures[0]; } /** * Return the list of available layout features. * * @return An array of ICustomFeatures */ public ICustomFeature[] getLayoutFeatures() { return layoutFeatures; } @Override public ICustomFeature[] getCustomFeatures(ICustomContext context) { // Custom features are created in XdfDiagramToolBehaviorProvider. This allows to arrange // these features in sub-menus return super.getCustomFeatures(context); } @Override protected IUpdateFeature getUpdateFeatureAdditional(IUpdateContext context) { return updateFeature; } /** * Implements creation of new connection by dragging instance port. */ @Override public IFeature[] getDragAndDropFeatures(IPictogramElementContext context) { return getCreateConnectionFeatures(); } @Override public IAddFeature getAddFeature(IAddContext context) { if (context.getNewObject() instanceof IFile) { return dropInstanceFeature; } return super.getAddFeature(context); } @Override public ICopyFeature getCopyFeature(ICopyContext context) { return copyFeature; } @Override public IPasteFeature getPasteFeature(IPasteContext context) { return pasteFeature; } /** * We never want to remove elements from the diagram. We always want to * delete them. So it is useless to display the menu entry, the button in * the contextual palette. This feature is globally disabled */ @Override public IRemoveFeature getRemoveFeature(IRemoveContext context) { return new DefaultRemoveFeature(this) { @Override public boolean isAvailable(IContext context) { return false; } }; } /** * We want to allow reconnection, even if an existing one is in a bad state. * If the source or target of a connection is a null or non-existing object, * we don't care this is this side the user wants to reconnect. * * This method also prevent bad connection by reusing logic from * ConnectionPattern.canXXX methods */ @Override public IReconnectionFeature getReconnectionFeature( IReconnectionContext context) { return new DefaultReconnectionFeature(this) { @Override public boolean canReconnect(IReconnectionContext context) { final Anchor newAnchor = getNewAnchor(context); final Connection connection = context.getConnection(); // Classical checks. Same as original method, without checking // null value of start/end in the original connection if ((connection == null) || (newAnchor == null) || (newAnchor.getParent() instanceof Diagram)) { return false; } // Delegate to ConnectionPattern to decide if a reconnection can // be performed or not final ConnectionPattern conPattern = (ConnectionPattern) getConnectionPatterns().get(0); final CreateConnectionContext ctxt = new CreateConnectionContext(); if (context.getReconnectType().equals(ReconnectionContext.RECONNECT_TARGET)) { ctxt.setSourceAnchor(connection.getStart()); ctxt.setTargetAnchor(newAnchor); return conPattern.canCreate(ctxt); } else { ctxt.setSourceAnchor(newAnchor); ctxt.setTargetAnchor(connection.getEnd()); return conPattern.canStartConnection(ctxt); } } @Override public void postReconnect(IReconnectionContext context) { // On reconnection, we have to update the business model object // source or target final net.sf.orcc.df.Connection dfConnection = (net.sf.orcc.df.Connection) getBusinessObjectForPictogramElement(context .getConnection()); final Anchor newAnchor = context.getNewAnchor(); final Vertex vertex = (Vertex) getBusinessObjectForPictogramElement(newAnchor .getParent()); if (context.getReconnectType().equals( ReconnectionContext.RECONNECT_SOURCE)) { if (PropsUtil.isInputPort(newAnchor.getParent())) { dfConnection.setSource(vertex); dfConnection.setSourcePort(null); } else if (PropsUtil.isInstanceOutPort(newAnchor)) { dfConnection.setSource(vertex); final Port port = (Port) getBusinessObjectForPictogramElement(newAnchor); dfConnection.setSourcePort(port); } else { OrccLogger .severeln("Unable to get the new source type."); } } else { if (PropsUtil.isOutputPort(newAnchor.getParent())) { dfConnection.setTarget(vertex); dfConnection.setTargetPort(null); } else if (PropsUtil.isInstanceInPort(newAnchor)) { dfConnection.setTarget(vertex); final Port port = (Port) getBusinessObjectForPictogramElement(newAnchor); dfConnection.setTargetPort(port); } else { OrccLogger .severeln("Unable to get the new target type."); } } } }; } }