]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram/src/org/simantics/diagram/handler/ConnectionCommandHandler.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / handler / ConnectionCommandHandler.java
diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/handler/ConnectionCommandHandler.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/handler/ConnectionCommandHandler.java
new file mode 100644 (file)
index 0000000..129cdf1
--- /dev/null
@@ -0,0 +1,311 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.diagram.handler;\r
+\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Line2D;\r
+import java.awt.geom.Point2D;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.HashSet;\r
+import java.util.Set;\r
+\r
+import org.simantics.Simantics;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.request.WriteRequest;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.diagram.content.ConnectionUtil;\r
+import org.simantics.diagram.content.EdgeResource;\r
+import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.diagram.synchronization.graph.RemoveBranchpoint;\r
+import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;\r
+import org.simantics.g2d.connection.ConnectionEntity;\r
+import org.simantics.g2d.diagram.DiagramHints;\r
+import org.simantics.g2d.diagram.IDiagram;\r
+import org.simantics.g2d.diagram.handler.PickRequest;\r
+import org.simantics.g2d.diagram.handler.Topology;\r
+import org.simantics.g2d.diagram.handler.Topology.Connection;\r
+import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant;\r
+import org.simantics.g2d.diagram.participant.Selection;\r
+import org.simantics.g2d.element.ElementHints;\r
+import org.simantics.g2d.element.ElementUtils;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.g2d.elementclass.BranchPoint;\r
+import org.simantics.g2d.elementclass.BranchPoint.Direction;\r
+import org.simantics.g2d.participant.MouseUtil;\r
+import org.simantics.g2d.participant.MouseUtil.MouseInfo;\r
+import org.simantics.g2d.participant.WorkbenchStatusLine;\r
+import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;\r
+import org.simantics.scenegraph.g2d.events.command.CommandEvent;\r
+import org.simantics.scenegraph.g2d.events.command.Commands;\r
+import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;\r
+import org.simantics.ui.SimanticsUI;\r
+import org.simantics.utils.ui.ErrorLogger;\r
+\r
+/**\r
+ * A participant for handling:\r
+ * <ul>\r
+ * <li>DELETE for deleting route points from DIA.Connection type connections\r
+ * <li>SPLIT_CONNECTION for creating new route points into DIA.Connection type connections\r
+ * <li>ROTATE_ELEMENT_CW & ROTATE_ELEMENT_CCW for rotating route points orientations.\r
+ * </ul>\r
+ * \r
+ *  NOTE: this participant does nothing useful with DIA.RouteGraphConnection type connections.\r
+ * \r
+ * @author Tuukka Lehtonen\r
+ * \r
+ * TODO: start using {@link WorkbenchStatusLine}\r
+ */\r
+public class ConnectionCommandHandler extends AbstractDiagramParticipant {\r
+\r
+    @Dependency MouseUtil mouseUtil;\r
+    @Dependency Selection selection;\r
+    //@Dependency WorkbenchStatusLine statusLine;\r
+\r
+    @EventHandler(priority = 100)\r
+    public boolean handleCommand(CommandEvent event) {\r
+        if (Commands.DELETE.equals(event.command)) {\r
+            try {\r
+                return joinConnection();\r
+            } catch (DatabaseException e) {\r
+                ErrorLogger.defaultLogError(e);\r
+                return false;\r
+            }\r
+        } else if (Commands.SPLIT_CONNECTION.equals(event.command) && routePointsEnabled()) {\r
+            return splitConnection();\r
+        } else if (Commands.ROTATE_ELEMENT_CW.equals(event.command) || Commands.ROTATE_ELEMENT_CCW.equals(event.command)) {\r
+            boolean clockWise = Commands.ROTATE_ELEMENT_CW.equals(event.command);\r
+            try {\r
+                return rotateBranchPoint(clockWise);\r
+            } catch (DatabaseException e) {\r
+                ErrorLogger.defaultLogError(e);\r
+                return false;\r
+            }\r
+        }\r
+        return false;\r
+    }\r
+\r
+    private boolean routePointsEnabled() {\r
+        return Boolean.TRUE.equals(diagram.getHint(DiagramHints.KEY_ALLOW_ROUTE_POINTS));\r
+    }\r
+\r
+    boolean joinConnection() throws DatabaseException {\r
+\r
+        final Set<IElement> routePoints = getBranchPoints(2);\r
+\r
+        if (routePoints.isEmpty())\r
+            return false;\r
+\r
+        selection.clear(0);\r
+\r
+        Simantics.getSession().sync(new WriteRequest() {\r
+            @Override\r
+            public void perform(WriteGraph graph) throws DatabaseException {\r
+                for (IElement routePoint : routePoints) {\r
+                    Object node = ElementUtils.getObject(routePoint);\r
+                    if (node instanceof Resource) {\r
+                        new RemoveBranchpoint(routePoint).perform(graph);\r
+                    }\r
+                }\r
+            }\r
+        });\r
+        setDirty();\r
+\r
+        //statusLine.message("Deleted " + routePoints.size() + " route points");\r
+        return true;\r
+    }\r
+\r
+    private boolean rotateBranchPoint(final boolean clockWise) throws DatabaseException {\r
+        final Set<IElement> routePoints = getBranchPoints(Integer.MAX_VALUE);\r
+        if (routePoints.isEmpty())\r
+            return false;\r
+\r
+        // Rotate branch points.\r
+        try {\r
+            SimanticsUI.getSession().syncRequest(new WriteRequest() {\r
+                @Override\r
+                public void perform(WriteGraph graph) throws DatabaseException {\r
+                    DiagramResource DIA = DiagramResource.getInstance(graph);\r
+                    for (IElement bp : routePoints) {\r
+                        Resource bpr = (Resource) ElementUtils.getObject(bp);\r
+                        boolean vertical = graph.hasStatement(bpr, DIA.Vertical, bpr);\r
+                        boolean horizontal = graph.hasStatement(bpr, DIA.Horizontal, bpr);\r
+                        Direction dir = Direction.toDirection(horizontal, vertical);\r
+                        Direction newDir = clockWise ? dir.cycleNext() : dir.cyclePrevious();\r
+                        switch (newDir) {\r
+                            case Any:\r
+                                graph.deny(bpr, DIA.Vertical);\r
+                                graph.deny(bpr, DIA.Horizontal);\r
+                                break;\r
+                            case Horizontal:\r
+                                graph.deny(bpr, DIA.Vertical);\r
+                                graph.claim(bpr, DIA.Horizontal, bpr);\r
+                                break;\r
+                            case Vertical:\r
+                                graph.deny(bpr, DIA.Horizontal);\r
+                                graph.claim(bpr, DIA.Vertical, bpr);\r
+                                break;\r
+                        }\r
+                    }\r
+                }\r
+            });\r
+        } catch (DatabaseException e) {\r
+            ErrorLogger.defaultLogError(e);\r
+        }\r
+\r
+        return true;\r
+    }\r
+\r
+    boolean splitConnection() {\r
+\r
+        final IElement edge = getSingleConnectionSegment();\r
+        if (edge == null)\r
+            return false;\r
+        final IDiagram diagram = ElementUtils.peekDiagram(edge);\r
+        if (diagram == null)\r
+            return false;\r
+\r
+        MouseInfo mi = mouseUtil.getMouseInfo(0);\r
+        final Point2D mousePos = mi.canvasPosition;\r
+\r
+        ISnapAdvisor snap = getHint(DiagramHints.SNAP_ADVISOR);\r
+        if (snap != null)\r
+            snap.snap(mousePos);\r
+\r
+        final AffineTransform splitPos = AffineTransform.getTranslateInstance(mousePos.getX(), mousePos.getY());\r
+\r
+        try {\r
+            SimanticsUI.getSession().syncRequest(new WriteRequest() {\r
+                @Override\r
+                public void perform(WriteGraph graph) throws DatabaseException {\r
+                    DiagramResource DIA = DiagramResource.getInstance(graph);\r
+\r
+                    // Split the edge with a new branch point\r
+                    ConnectionUtil cu = new ConnectionUtil(graph);\r
+                    EdgeResource segment = (EdgeResource) ElementUtils.getObject(edge);\r
+                    Resource bp = cu.split(segment, splitPos);\r
+\r
+                    Line2D nearestLine = ConnectionUtil.resolveNearestEdgeLineSegment(mousePos, edge);\r
+                    if (nearestLine != null) {\r
+                        double angle = Math.atan2(\r
+                                Math.abs(nearestLine.getY2() - nearestLine.getY1()),\r
+                                Math.abs(nearestLine.getX2() - nearestLine.getX1())\r
+                        );\r
+\r
+                        if (angle >= 0 && angle < Math.PI / 4) {\r
+                            graph.claim(bp, DIA.Horizontal, bp);\r
+                        } else if (angle > Math.PI / 4 && angle <= Math.PI / 2) {\r
+                            graph.claim(bp, DIA.Vertical, bp);\r
+                        }\r
+                    }\r
+                }\r
+            });\r
+\r
+            selection.clear(0);\r
+\r
+        } catch (DatabaseException e) {\r
+            ErrorLogger.defaultLogError(e);\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * @return all route points in the current diagram selection if and only if\r
+     *         only route points are selected. If anything else is selected,\r
+     *         even branch points, an empty set will be returned.\r
+     */\r
+    Set<IElement> getBranchPoints(int maxDegree) throws DatabaseException {\r
+\r
+        Set<IElement> ss = selection.getSelection(0);\r
+        if (ss.isEmpty())\r
+            return Collections.emptySet();\r
+\r
+        final Set<IElement> result = new HashSet<IElement>();\r
+        Collection<Connection> connections = new ArrayList<Connection>();\r
+        for (IElement e : ss) {\r
+            if (!e.getElementClass().containsClass(BranchPoint.class))\r
+                return Collections.emptySet();\r
+            ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
+            if (ce == null)\r
+                return Collections.emptySet();\r
+            IDiagram diagram = ElementUtils.peekDiagram(e);\r
+            if (diagram == null)\r
+                return Collections.emptySet();\r
+            for (Topology topology : diagram.getDiagramClass().getItemsByClass(Topology.class)) {\r
+                connections.clear();\r
+                topology.getConnections(e, ElementUtils.getSingleTerminal(e), connections);\r
+                int degree = connections.size();\r
+                if (degree < 2 || degree > maxDegree)\r
+                    return Collections.emptySet();\r
+                result.add(e);\r
+            }\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * @return if a single edge is selected, return the edge. If a single\r
+     *         connection is selected and it contains only one edge segment,\r
+     *         return it. Otherwise return <code>null</code>.\r
+     */\r
+    IElement getSingleConnectionSegment() {\r
+        Set<IElement> s = selection.getSelection(0);\r
+        if (s.size() != 1)\r
+            return null;\r
+        IElement e = s.iterator().next();\r
+        if (PickRequest.PickFilter.FILTER_CONNECTIONS.accept(e)) {\r
+            // Does this connection have only one edge and no branch points?\r
+            ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
+            Collection<IElement> coll = ce.getBranchPoints(null);\r
+            if (!coll.isEmpty())\r
+                return null;\r
+            ce.getSegments(coll);\r
+            if (coll.size() != 1)\r
+                return null;\r
+            // Return the only edge element of the whole connection.\r
+            return coll.iterator().next();\r
+        }\r
+        if (PickRequest.PickFilter.FILTER_CONNECTION_EDGES.accept(e))\r
+            return e;\r
+        return null;\r
+    }\r
+\r
+    /**\r
+     * @return the selected top-level connection element if and only if the\r
+     *         selection contains the connection or parts of the same connection\r
+     *         (edge segments or branch/route points) and nothing else.\r
+     */\r
+    IElement getSingleConnection() {\r
+\r
+        Set<IElement> ss = selection.getSelection(0);\r
+        if (ss.isEmpty())\r
+            return null;\r
+\r
+        IElement result = null;\r
+\r
+        for (IElement e : ss) {\r
+            ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
+            if (ce == null)\r
+                continue;\r
+            if (result != null && !ce.getConnection().equals(result))\r
+                return null;\r
+            result = ce.getConnection();\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+}\r