/* * Copyright (c) 2014, 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.layout; import java.util.HashMap; import java.util.Map; import net.sf.orcc.OrccRuntimeException; import net.sf.orcc.df.Instance; import net.sf.orcc.df.Port; import net.sf.orcc.xdf.ui.patterns.NetworkPortPattern; import net.sf.orcc.xdf.ui.util.PropsUtil; import org.eclipse.emf.ecore.EObject; import org.eclipse.graphiti.mm.algorithms.GraphicsAlgorithm; import org.eclipse.graphiti.mm.algorithms.Polyline; import org.eclipse.graphiti.mm.algorithms.styles.Point; 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.mm.pictograms.FixPointAnchor; import org.eclipse.graphiti.mm.pictograms.FreeFormConnection; import org.eclipse.graphiti.mm.pictograms.PictogramElement; import org.eclipse.graphiti.mm.pictograms.Shape; import org.eclipse.graphiti.services.Graphiti; import de.cau.cs.kieler.core.kgraph.KEdge; import de.cau.cs.kieler.core.kgraph.KGraphElement; import de.cau.cs.kieler.core.kgraph.KNode; import de.cau.cs.kieler.core.kgraph.KPort; import de.cau.cs.kieler.kiml.klayoutdata.KEdgeLayout; import de.cau.cs.kieler.kiml.klayoutdata.KPoint; import de.cau.cs.kieler.kiml.klayoutdata.KShapeLayout; import de.cau.cs.kieler.kiml.util.KimlUtil; /** * This class has 2 main functions. * <ol> * <li>Build a KGraph suitable for Kieler layout algorithm from an existing * Diagram</li> * <li>Transfer the computed layout to the diagram objects</li> * </ol> * * @author Antoine Lorence * */ public class XdfDiagramLayoutManager { private final KNode diagramKNode; private final Map<PictogramElement, KGraphElement> peKGraphMap; private int xScale = 0; /** * Build the KGraph/KNode structure from the given diagram. * * This class should not be public but 'friend' and instantiated by an * AutoLayoutFeature subclass * * @param diagram */ XdfDiagramLayoutManager(final Diagram diagram) { peKGraphMap = new HashMap<PictogramElement, KGraphElement>(); diagramKNode = KimlUtil.createInitializedNode(); final GraphicsAlgorithm diagramGa = diagram.getGraphicsAlgorithm(); final KShapeLayout rootLayout = diagramKNode.getData(KShapeLayout.class); rootLayout.setSize(diagramGa.getWidth(), diagramGa.getHeight()); rootLayout.setPos(diagramGa.getX(), diagramGa.getY()); peKGraphMap.put(diagram, diagramKNode); for (final Shape shape : diagram.getChildren()) { final EObject eObject = Graphiti.getLinkService().getBusinessObjectForLinkedPictogramElement(shape); if (eObject instanceof Instance) { registerInstance(shape); } else if (eObject instanceof Port) { registerPort(shape); } else { // ?? } } for (final Connection connection : diagram.getConnections()) { registerConnection(connection); } } /** * Get the KNode object corresponding to the current Diagram * * @return */ public KNode getTopLevelNode() { return diagramKNode; } /** * Get the map which stores all correspondences between PictogramElements * and KGraphElement. * * @return */ public Map<PictogramElement, KGraphElement> getPeKGraphMap() { return peKGraphMap; } private void registerInstance(final Shape instanceShape) { final GraphicsAlgorithm instanceGa = instanceShape.getGraphicsAlgorithm(); final KNode instanceKNode = KimlUtil.createInitializedNode(); final KShapeLayout instanceLayout = instanceKNode.getData(KShapeLayout.class); instanceLayout.setSize(instanceGa.getWidth(), instanceGa.getHeight()); instanceLayout.setPos(instanceGa.getX(), instanceGa.getY()); diagramKNode.getChildren().add(instanceKNode); peKGraphMap.put(instanceShape, instanceKNode); for (final Anchor anchor : instanceShape.getAnchors()) { registerInstancePort((FixPointAnchor) anchor, instanceKNode); } } private void registerInstancePort(final FixPointAnchor anchor, final KNode instanceNode) { final GraphicsAlgorithm portRect = anchor.getGraphicsAlgorithm(); final Point anchorLocation = anchor.getLocation(); final KPort kport = KimlUtil.createInitializedPort(); final KShapeLayout portLayout = kport.getData(KShapeLayout.class); portLayout.setSize(portRect.getWidth(), portRect.getHeight()); portLayout.setPos(anchorLocation.getX() + portRect.getX(), anchorLocation.getY() + portRect.getY()); instanceNode.getPorts().add(kport); peKGraphMap.put(anchor, kport); } private void registerPort(final Shape portShape) { final GraphicsAlgorithm topLevelInvisibleRect = portShape.getGraphicsAlgorithm(); final GraphicsAlgorithm portPolygon = (GraphicsAlgorithm) PropsUtil.findPcFromIdentifier( topLevelInvisibleRect, NetworkPortPattern.SHAPE_ID); final KNode portKNode = KimlUtil.createInitializedNode(); final KShapeLayout portLayout = portKNode.getData(KShapeLayout.class); portLayout.setSize(NetworkPortPattern.SHAPE_WIDTH, NetworkPortPattern.SHAPE_HEIGHT); portLayout.setPos(topLevelInvisibleRect.getX() + portPolygon.getX(), topLevelInvisibleRect.getY() + portPolygon.getY()); diagramKNode.getChildren().add(portKNode); peKGraphMap.put(portShape, portKNode); } private void registerConnection(final Connection connection) { final KEdge edge = KimlUtil.createInitializedEdge(); final Anchor sourceAnchor = connection.getStart(); final Anchor targetAnchor = connection.getEnd(); final KNode sourceNode = (KNode) peKGraphMap.get(sourceAnchor.getParent()); KPort sourcePort = null; if (sourceAnchor instanceof FixPointAnchor) { sourcePort = (KPort) peKGraphMap.get(sourceAnchor); } final KNode targetNode = (KNode) peKGraphMap.get(targetAnchor.getParent()); KPort targetPort = null; if (targetAnchor instanceof FixPointAnchor) { targetPort = (KPort) peKGraphMap.get(targetAnchor); } edge.setSource(sourceNode); edge.setTarget(targetNode); edge.setSourcePort(sourcePort); edge.setTargetPort(targetPort); peKGraphMap.put(connection, edge); } public void applyLayout() { xScale = 0; for (Map.Entry<PictogramElement, KGraphElement> entry : peKGraphMap.entrySet()) { final PictogramElement pe = entry.getKey(); final KGraphElement ge = entry.getValue(); if (ge instanceof KNode) { if (PropsUtil.isInstance(pe)) { applyLayoutToInstanceNode(pe, (KNode) ge); } else if (PropsUtil.isPort(pe)) { applyLayoutToPortNode(pe, (KNode) ge); } } else if (ge instanceof KEdge) { applyLayoutToConnection(pe, (KEdge) ge); } else if (ge instanceof KPort) { // We don't want to change ports position inside instances } } // At least 1 port has a negative coordinate. We need to increase X // coordinate of all objects if (xScale != 0) { for (Map.Entry<PictogramElement, KGraphElement> entry : peKGraphMap.entrySet()) { if (entry.getValue() instanceof KNode) { if (entry.getKey() instanceof Diagram) { continue; } final GraphicsAlgorithm ga = entry.getKey() .getGraphicsAlgorithm(); ga.setX(ga.getX() + xScale); } else if (entry.getValue() instanceof KEdge && entry.getKey() instanceof FreeFormConnection) { final FreeFormConnection connection = (FreeFormConnection) entry.getKey(); for (final Point point : connection.getBendpoints()) { point.setX(point.getX() + xScale); } } } } } private void applyLayoutToInstanceNode(final PictogramElement pe, final KNode node) { final KShapeLayout shapeLayout = node.getData(KShapeLayout.class); final GraphicsAlgorithm ga = pe.getGraphicsAlgorithm(); final int x = Math.round(shapeLayout.getXpos()); final int y = Math.round(shapeLayout.getYpos()); Graphiti.getGaService().setLocation(ga, x, y); } private void applyLayoutToPortNode(final PictogramElement pe, final KNode node) { final KShapeLayout shapeLayout = node.getData(KShapeLayout.class); final GraphicsAlgorithm ga = pe.getGraphicsAlgorithm(); int x = Math.round(shapeLayout.getXpos()); int y = Math.round(shapeLayout.getYpos()); // If port text is larger than port shape, the external invisible // rectangle X coordinate needs to be fixed x -= (ga.getWidth() - NetworkPortPattern.SHAPE_WIDTH) / 2; // The new coordinate is negative, we need to move all obects if (x < 0) { xScale = Math.max(xScale, -x); } Graphiti.getGaService().setLocation(ga, x, y); } private void applyLayoutToConnection(final PictogramElement pe, final KEdge edge) { final KEdgeLayout edgeLayout = edge.getData(KEdgeLayout.class); final GraphicsAlgorithm ga = pe.getGraphicsAlgorithm(); // Check unsupported types if (!(pe instanceof FreeFormConnection)) { throw new OrccRuntimeException(pe.getClass() + " connection type is not supported."); } else if (!(ga instanceof Polyline)) { throw new OrccRuntimeException(ga.getClass() + " connection graphics type is not supported."); } final FreeFormConnection connection = (FreeFormConnection) pe; // Reset existing bendpoints for this connection connection.getBendpoints().clear(); for (final KPoint kpoint : edgeLayout.getBendPoints()) { final Point point = Graphiti.getGaService().createPoint(Math.round(kpoint.getX()), Math.round(kpoint.getY())); connection.getBendpoints().add(point); } } }