]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram/src/org/simantics/diagram/handler/ConnectionSplitAndJoin.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / handler / ConnectionSplitAndJoin.java
index d46c427320c7b06e8c13a6a77112202146a6d50f..a5cd5f10911e49ac3339ac8b79890aeb0589966e 100644 (file)
-/*******************************************************************************\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.Point2D;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.Iterator;\r
-import java.util.Set;\r
-\r
-import org.eclipse.jface.action.Action;\r
-import org.eclipse.jface.action.ActionContributionItem;\r
-import org.eclipse.jface.action.IContributionItem;\r
-import org.eclipse.swt.widgets.Menu;\r
-import org.eclipse.ui.IWorkbenchPart;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.Session;\r
-import org.simantics.db.WriteGraph;\r
-import org.simantics.db.common.request.IndexRoot;\r
-import org.simantics.db.common.request.WriteRequest;\r
-import org.simantics.db.common.utils.OrderedSetUtils;\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.flag.FlagUtil;\r
-import org.simantics.diagram.flag.Splitter;\r
-import org.simantics.diagram.internal.Activator;\r
-import org.simantics.diagram.stubs.DiagramResource;\r
-import org.simantics.g2d.canvas.ICanvasContext;\r
-import org.simantics.g2d.diagram.DiagramHints;\r
-import org.simantics.g2d.diagram.participant.Selection;\r
-import org.simantics.g2d.element.IElement;\r
-import org.simantics.scl.commands.Command;\r
-import org.simantics.scl.commands.Commands;\r
-import org.simantics.structural.stubs.StructuralResource2;\r
-import org.simantics.ui.contribution.DynamicMenuContribution;\r
-import org.simantics.utils.threads.ThreadUtils;\r
-import org.simantics.utils.ui.AdaptionUtils;\r
-import org.simantics.utils.ui.ExceptionUtils;\r
-import org.simantics.utils.ui.workbench.WorkbenchUtils;\r
-\r
-/**\r
- * @author Tuukka Lehtonen\r
- */\r
-public class ConnectionSplitAndJoin extends DynamicMenuContribution {\r
-\r
-    private static final IContributionItem[] NONE = {};\r
-\r
-    private ICanvasContext canvas;\r
-\r
-    @Override\r
-    public void fill(Menu menu, int index) {\r
-        // Need to grab active part here, we're still in the SWT thread.\r
-        IWorkbenchPart activePart = WorkbenchUtils.getActiveWorkbenchPart();\r
-        if (activePart == null)\r
-            return;\r
-        ICanvasContext ctx = (ICanvasContext) activePart.getAdapter(ICanvasContext.class);\r
-        if (ctx == null)\r
-            return;\r
-\r
-        this.canvas = ctx;\r
-        try {\r
-            super.fill(menu, index);\r
-        } finally {\r
-            this.canvas = null;\r
-        }\r
-    }\r
-\r
-    @Override\r
-    protected IContributionItem[] getContributionItems(ReadGraph graph, Object[] selection) throws DatabaseException {\r
-        // If selection contains:\r
-        // a) only diagram-locally connected flags, that each are connected to something, allow joining of those flag into connections\r
-        // b) a single connection edge element, allow splitting into a connected flag pair\r
-\r
-        Collection<Resource> localConnectedFlags = Collections.emptyList();\r
-        Resource routeGraphConnection = null;\r
-        EdgeResource edge = null;\r
-        if (selection.length == 1) {\r
-            routeGraphConnection = getRouteGraphConnection(graph, selection[0]);\r
-            if (routeGraphConnection == null) {\r
-                edge = getEdge(graph, selection[0]);\r
-                if (edge == null)\r
-                    localConnectedFlags = getLocalConnectedFlags(graph, selection);\r
-            }\r
-        } else {\r
-            localConnectedFlags = getLocalConnectedFlags(graph, selection);\r
-        }\r
-\r
-        if (!localConnectedFlags.isEmpty()) {\r
-            return new IContributionItem[] {\r
-                    new ActionContributionItem(new Join(graph.getSession(), canvas, localConnectedFlags))\r
-            };\r
-        } else if (edge != null) {\r
-            Selection sel = canvas.getAtMostOneItemOfClass(Selection.class);\r
-            if (sel == null)\r
-                return NONE;\r
-            Set<IElement> elems = sel.getSelection(0);\r
-            if (elems.size() != 1)\r
-                return NONE;\r
-            IElement edgeElement = ConnectionUtil.getSingleEdge(elems.iterator().next());\r
-            if (edgeElement == null)\r
-                return NONE;\r
-            Point2D canvasPosition = canvas.getHintStack().getHint(DiagramHints.POPUP_MENU_CANVAS_POSITION);\r
-            if (canvasPosition == null)\r
-                return NONE;\r
-\r
-            return new IContributionItem[] {\r
-                    new ActionContributionItem(new Split(graph.getSession(), canvas, canvasPosition, edgeElement, edge))\r
-            };\r
-        } else if (routeGraphConnection != null) {\r
-            Selection sel = canvas.getAtMostOneItemOfClass(Selection.class);\r
-            if (sel == null)\r
-                return NONE;\r
-            Set<IElement> elems = sel.getSelection(0);\r
-            if (elems.size() != 1)\r
-                return NONE;\r
-            Point2D canvasPosition = canvas.getHintStack().getHint(DiagramHints.POPUP_MENU_CANVAS_POSITION);\r
-            if (canvasPosition == null)\r
-                return NONE;\r
-\r
-            return new IContributionItem[] {\r
-                    new ActionContributionItem(new SplitRouteGraph(graph.getSession(), canvas, canvasPosition, routeGraphConnection))\r
-            };\r
-        }\r
-        return NONE;\r
-    }\r
-\r
-    private Resource getRouteGraphConnection(ReadGraph graph, Object object) throws DatabaseException {\r
-        DiagramResource DIA = DiagramResource.getInstance(graph);\r
-        Resource connection = AdaptionUtils.adaptToSingle(object, Resource.class);\r
-        if (connection != null && graph.isInstanceOf(connection, DIA.RouteGraphConnection))\r
-            return connection;\r
-        return null;\r
-    }\r
-\r
-    private Collection<Resource> getLocalConnectedFlags(ReadGraph graph, Object[] selection) throws DatabaseException {\r
-        DiagramResource DIA = DiagramResource.getInstance(graph);\r
-        ArrayList<Resource> result = new ArrayList<Resource>(4);\r
-        for (Object s : selection) {\r
-            Resource r = AdaptionUtils.adaptToSingle(s, Resource.class);\r
-            if (r == null || !graph.isInstanceOf(r, DIA.Flag))\r
-                return Collections.emptyList();\r
-            if (!isConnectedToSomething(graph, r))\r
-                return Collections.emptyList();\r
-            Collection<Resource> counterparts = FlagUtil.getCounterparts(graph, r);\r
-            if (counterparts.isEmpty())\r
-                return Collections.emptyList();\r
-            Collection<Resource> flagDiagrams = OrderedSetUtils.getOwnerLists(graph, r, DIA.Diagram);\r
-            for (Resource counterpart : counterparts) {\r
-                boolean joinedWithinSingleDiagram = !Collections.disjoint(flagDiagrams,\r
-                        OrderedSetUtils.getOwnerLists(graph, counterpart, DIA.Diagram));\r
-                if (!joinedWithinSingleDiagram)\r
-                    return Collections.emptyList();\r
-                if (!isConnectedToSomething(graph, counterpart))\r
-                    return Collections.emptyList();\r
-            }\r
-            result.add(r);\r
-        }\r
-        return result;\r
-    }\r
-\r
-    private boolean isConnectedToSomething(ReadGraph graph, Resource flag) throws DatabaseException {\r
-        DiagramResource DIA = DiagramResource.getInstance(graph);\r
-        StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
-        for (Resource connector : graph.getObjects(flag, STR.IsConnectedTo)) {\r
-            Resource cntr = graph.getPossibleObject(connector, DIA.AreConnected);\r
-            Resource conn = ConnectionUtil.getConnection(graph, cntr);\r
-            if (cntr == null || conn == null)\r
-                return false;\r
-            return true;\r
-        }\r
-        return false;\r
-    }\r
-\r
-    private EdgeResource getEdge(ReadGraph graph, Object object) throws DatabaseException {\r
-        DiagramResource DIA = DiagramResource.getInstance(graph);\r
-\r
-        Resource connection = null;\r
-        EdgeResource edge = AdaptionUtils.adaptToSingle(object, EdgeResource.class);\r
-        if (edge != null)\r
-            //connection = ConnectionUtil.getConnection(graph, edge);\r
-            return edge;\r
-        else {\r
-            connection = AdaptionUtils.adaptToSingle(object, Resource.class);\r
-        }\r
-\r
-        if (connection != null && graph.isInstanceOf(connection, DiagramResource.getInstance(graph).Connection)) {\r
-            Collection<Resource> connectors = graph.getObjects(connection, DIA.HasConnector);\r
-            Collection<Resource> branchPoints = graph.getObjects(connection, DIA.HasBranchPoint);\r
-            if (branchPoints.isEmpty() && connectors.size() == 2) {\r
-                Iterator<Resource> it = connectors.iterator();\r
-                Resource connector1 = it.next();\r
-                Resource connector2 = it.next();\r
-                return new EdgeResource(connector1, connector2);\r
-            }\r
-        }\r
-\r
-        return null;\r
-    }\r
-\r
-    public static abstract class Helper extends Action {\r
-\r
-        protected final Session        session;\r
-        protected final ICanvasContext context;\r
-\r
-        public Helper(String label, Session session, ICanvasContext context) {\r
-            super(label);\r
-            this.session = session;\r
-            this.context = context;\r
-        }\r
-\r
-        @Override\r
-        public void run() {\r
-            try {\r
-                session.syncRequest(new WriteRequest() {\r
-                    @Override\r
-                    public void perform(WriteGraph graph) throws DatabaseException {\r
-                        graph.markUndoPoint();\r
-                        performAction(graph);\r
-                    }\r
-                });\r
-                ThreadUtils.asyncExec(context.getThreadAccess(), new Runnable() {\r
-                    @Override\r
-                    public void run() {\r
-                        if (context.isDisposed())\r
-                            return;\r
-                        Selection selection = context.getAtMostOneItemOfClass(Selection.class);\r
-                        if (selection != null) {\r
-                            // This prevents workbench selection from being left over.\r
-                            // Also prevents scene graph crap from being left on the screen.\r
-                            selection.clear(0);\r
-                        }\r
-                    }\r
-                });\r
-            } catch (DatabaseException e) {\r
-                ExceptionUtils.logError(e);\r
-            }\r
-        }\r
-\r
-        /**\r
-         * @param graph\r
-         */\r
-        protected abstract void performAction(WriteGraph graph) throws DatabaseException;\r
-\r
-    }\r
-\r
-    public static class Split extends Helper {\r
-        private final Point2D      splitCanvasPos;\r
-        private final IElement     edgeElement;\r
-        private final EdgeResource edge;\r
-\r
-        public Split(Session session, ICanvasContext context, Point2D splitCanvasPos, IElement edgeElement, EdgeResource edge) {\r
-            super("Split Connection", session, context);\r
-            setImageDescriptor(Activator.LINK_BREAK_ICON);\r
-            this.splitCanvasPos = splitCanvasPos;\r
-            this.edgeElement = edgeElement;\r
-            this.edge = edge;\r
-        }\r
-\r
-        @Override\r
-        protected void performAction(WriteGraph graph) throws DatabaseException {\r
-            new Splitter(graph).split(graph, edgeElement, edge, splitCanvasPos);\r
-        }\r
-    }\r
-\r
-    public static class SplitRouteGraph extends Helper {\r
-        private final Point2D  splitCanvasPos;\r
-        private final Resource connection;\r
-\r
-        public SplitRouteGraph(Session session, ICanvasContext context, Point2D splitCanvasPos, Resource connection) {\r
-            super("Split Connection", session, context);\r
-            setImageDescriptor(Activator.LINK_BREAK_ICON);\r
-            this.splitCanvasPos = splitCanvasPos;\r
-            this.connection = connection;\r
-        }\r
-\r
-        @Override\r
-        protected void performAction(WriteGraph graph) throws DatabaseException {\r
-            Command command = Commands.get(graph, "Simantics/Diagram/splitConnection");\r
-            command.execute(graph, graph.syncRequest(new IndexRoot(connection)),\r
-                    connection, splitCanvasPos.getX(), splitCanvasPos.getY());\r
-        }\r
-    }\r
-\r
-    public static class Join extends Helper {\r
-        private final Collection<Resource> flags;\r
-\r
-        public Join(Session session, ICanvasContext context, Collection<Resource> flags) {\r
-            super("Join Flags", session, context);\r
-            setImageDescriptor(Activator.LINK_ICON);\r
-            this.flags = flags;\r
-        }\r
-\r
-        @Override\r
-        protected void performAction(WriteGraph graph) throws DatabaseException {\r
-            Command command = Commands.get(graph, "Simantics/Diagram/joinFlagsLocal");\r
-            command.execute(graph, graph.syncRequest(new IndexRoot(flags.iterator().next())), flags);\r
-        }\r
-    }\r
-\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.diagram.handler;
+
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ActionContributionItem;
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.ui.IWorkbenchPart;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.Session;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.IndexRoot;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.common.utils.OrderedSetUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.diagram.content.ConnectionUtil;
+import org.simantics.diagram.content.EdgeResource;
+import org.simantics.diagram.flag.FlagUtil;
+import org.simantics.diagram.flag.Splitter;
+import org.simantics.diagram.internal.Activator;
+import org.simantics.diagram.stubs.DiagramResource;
+import org.simantics.g2d.canvas.ICanvasContext;
+import org.simantics.g2d.diagram.DiagramHints;
+import org.simantics.g2d.diagram.participant.Selection;
+import org.simantics.g2d.element.IElement;
+import org.simantics.scl.commands.Command;
+import org.simantics.scl.commands.Commands;
+import org.simantics.structural.stubs.StructuralResource2;
+import org.simantics.ui.contribution.DynamicMenuContribution;
+import org.simantics.utils.threads.ThreadUtils;
+import org.simantics.utils.ui.AdaptionUtils;
+import org.simantics.utils.ui.ExceptionUtils;
+import org.simantics.utils.ui.workbench.WorkbenchUtils;
+
+/**
+ * @author Tuukka Lehtonen
+ */
+public class ConnectionSplitAndJoin extends DynamicMenuContribution {
+
+    private static final IContributionItem[] NONE = {};
+
+    private ICanvasContext canvas;
+
+    @Override
+    public void fill(Menu menu, int index) {
+        // Need to grab active part here, we're still in the SWT thread.
+        IWorkbenchPart activePart = WorkbenchUtils.getActiveWorkbenchPart();
+        if (activePart == null)
+            return;
+        ICanvasContext ctx = (ICanvasContext) activePart.getAdapter(ICanvasContext.class);
+        if (ctx == null)
+            return;
+
+        this.canvas = ctx;
+        try {
+            super.fill(menu, index);
+        } finally {
+            this.canvas = null;
+        }
+    }
+
+    @Override
+    protected IContributionItem[] getContributionItems(ReadGraph graph, Object[] selection) throws DatabaseException {
+        // If selection contains:
+        // a) only diagram-locally connected flags, that each are connected to something, allow joining of those flag into connections
+        // b) a single connection edge element, allow splitting into a connected flag pair
+
+        Collection<Resource> localConnectedFlags = Collections.emptyList();
+        Resource routeGraphConnection = null;
+        EdgeResource edge = null;
+        if (selection.length == 1) {
+            routeGraphConnection = getRouteGraphConnection(graph, selection[0]);
+            if (routeGraphConnection == null) {
+                edge = getEdge(graph, selection[0]);
+                if (edge == null)
+                    localConnectedFlags = getLocalConnectedFlags(graph, selection);
+            }
+        } else {
+            localConnectedFlags = getLocalConnectedFlags(graph, selection);
+        }
+
+        if (!localConnectedFlags.isEmpty()) {
+            return new IContributionItem[] {
+                    new ActionContributionItem(new Join(graph.getSession(), canvas, localConnectedFlags))
+            };
+        } else if (edge != null) {
+            Selection sel = canvas.getAtMostOneItemOfClass(Selection.class);
+            if (sel == null)
+                return NONE;
+            Set<IElement> elems = sel.getSelection(0);
+            if (elems.size() != 1)
+                return NONE;
+            IElement edgeElement = ConnectionUtil.getSingleEdge(elems.iterator().next());
+            if (edgeElement == null)
+                return NONE;
+            Point2D canvasPosition = canvas.getHintStack().getHint(DiagramHints.POPUP_MENU_CANVAS_POSITION);
+            if (canvasPosition == null)
+                return NONE;
+
+            return new IContributionItem[] {
+                    new ActionContributionItem(new Split(graph.getSession(), canvas, canvasPosition, edgeElement, edge))
+            };
+        } else if (routeGraphConnection != null) {
+            Selection sel = canvas.getAtMostOneItemOfClass(Selection.class);
+            if (sel == null)
+                return NONE;
+            Set<IElement> elems = sel.getSelection(0);
+            if (elems.size() != 1)
+                return NONE;
+            Point2D canvasPosition = canvas.getHintStack().getHint(DiagramHints.POPUP_MENU_CANVAS_POSITION);
+            if (canvasPosition == null)
+                return NONE;
+
+            return new IContributionItem[] {
+                    new ActionContributionItem(new SplitRouteGraph(graph.getSession(), canvas, canvasPosition, routeGraphConnection))
+            };
+        }
+        return NONE;
+    }
+
+    private Resource getRouteGraphConnection(ReadGraph graph, Object object) throws DatabaseException {
+        DiagramResource DIA = DiagramResource.getInstance(graph);
+        Resource connection = AdaptionUtils.adaptToSingle(object, Resource.class);
+        if (connection != null && graph.isInstanceOf(connection, DIA.RouteGraphConnection))
+            return connection;
+        return null;
+    }
+
+    private Collection<Resource> getLocalConnectedFlags(ReadGraph graph, Object[] selection) throws DatabaseException {
+        DiagramResource DIA = DiagramResource.getInstance(graph);
+        ArrayList<Resource> result = new ArrayList<Resource>(4);
+        for (Object s : selection) {
+            Resource r = AdaptionUtils.adaptToSingle(s, Resource.class);
+            if (r == null || !graph.isInstanceOf(r, DIA.Flag))
+                return Collections.emptyList();
+            if (!isConnectedToSomething(graph, r))
+                return Collections.emptyList();
+            Collection<Resource> counterparts = FlagUtil.getCounterparts(graph, r);
+            if (counterparts.isEmpty())
+                return Collections.emptyList();
+            Collection<Resource> flagDiagrams = OrderedSetUtils.getOwnerLists(graph, r, DIA.Diagram);
+            for (Resource counterpart : counterparts) {
+                boolean joinedWithinSingleDiagram = !Collections.disjoint(flagDiagrams,
+                        OrderedSetUtils.getOwnerLists(graph, counterpart, DIA.Diagram));
+                if (!joinedWithinSingleDiagram)
+                    return Collections.emptyList();
+                if (!isConnectedToSomething(graph, counterpart))
+                    return Collections.emptyList();
+            }
+            result.add(r);
+        }
+        return result;
+    }
+
+    private boolean isConnectedToSomething(ReadGraph graph, Resource flag) throws DatabaseException {
+        DiagramResource DIA = DiagramResource.getInstance(graph);
+        StructuralResource2 STR = StructuralResource2.getInstance(graph);
+        for (Resource connector : graph.getObjects(flag, STR.IsConnectedTo)) {
+            Resource cntr = graph.getPossibleObject(connector, DIA.AreConnected);
+            Resource conn = ConnectionUtil.getConnection(graph, cntr);
+            if (cntr == null || conn == null)
+                return false;
+            return true;
+        }
+        return false;
+    }
+
+    private EdgeResource getEdge(ReadGraph graph, Object object) throws DatabaseException {
+        DiagramResource DIA = DiagramResource.getInstance(graph);
+
+        Resource connection = null;
+        EdgeResource edge = AdaptionUtils.adaptToSingle(object, EdgeResource.class);
+        if (edge != null)
+            //connection = ConnectionUtil.getConnection(graph, edge);
+            return edge;
+        else {
+            connection = AdaptionUtils.adaptToSingle(object, Resource.class);
+        }
+
+        if (connection != null && graph.isInstanceOf(connection, DiagramResource.getInstance(graph).Connection)) {
+            Collection<Resource> connectors = graph.getObjects(connection, DIA.HasConnector);
+            Collection<Resource> branchPoints = graph.getObjects(connection, DIA.HasBranchPoint);
+            if (branchPoints.isEmpty() && connectors.size() == 2) {
+                Iterator<Resource> it = connectors.iterator();
+                Resource connector1 = it.next();
+                Resource connector2 = it.next();
+                return new EdgeResource(connector1, connector2);
+            }
+        }
+
+        return null;
+    }
+
+    public static abstract class Helper extends Action {
+
+        protected final Session        session;
+        protected final ICanvasContext context;
+
+        public Helper(String label, Session session, ICanvasContext context) {
+            super(label);
+            this.session = session;
+            this.context = context;
+        }
+
+        @Override
+        public void run() {
+            try {
+                session.syncRequest(new WriteRequest() {
+                    @Override
+                    public void perform(WriteGraph graph) throws DatabaseException {
+                        graph.markUndoPoint();
+                        performAction(graph);
+                    }
+                });
+                ThreadUtils.asyncExec(context.getThreadAccess(), new Runnable() {
+                    @Override
+                    public void run() {
+                        if (context.isDisposed())
+                            return;
+                        Selection selection = context.getAtMostOneItemOfClass(Selection.class);
+                        if (selection != null) {
+                            // This prevents workbench selection from being left over.
+                            // Also prevents scene graph crap from being left on the screen.
+                            selection.clear(0);
+                        }
+                    }
+                });
+            } catch (DatabaseException e) {
+                ExceptionUtils.logError(e);
+            }
+        }
+
+        /**
+         * @param graph
+         */
+        protected abstract void performAction(WriteGraph graph) throws DatabaseException;
+
+    }
+
+    public static class Split extends Helper {
+        private final Point2D      splitCanvasPos;
+        private final IElement     edgeElement;
+        private final EdgeResource edge;
+
+        public Split(Session session, ICanvasContext context, Point2D splitCanvasPos, IElement edgeElement, EdgeResource edge) {
+            super("Split Connection", session, context);
+            setImageDescriptor(Activator.LINK_BREAK_ICON);
+            this.splitCanvasPos = splitCanvasPos;
+            this.edgeElement = edgeElement;
+            this.edge = edge;
+        }
+
+        @Override
+        protected void performAction(WriteGraph graph) throws DatabaseException {
+            new Splitter(graph).split(graph, edgeElement, edge, splitCanvasPos);
+        }
+    }
+
+    public static class SplitRouteGraph extends Helper {
+        private final Point2D  splitCanvasPos;
+        private final Resource connection;
+
+        public SplitRouteGraph(Session session, ICanvasContext context, Point2D splitCanvasPos, Resource connection) {
+            super("Split Connection", session, context);
+            setImageDescriptor(Activator.LINK_BREAK_ICON);
+            this.splitCanvasPos = splitCanvasPos;
+            this.connection = connection;
+        }
+
+        @Override
+        protected void performAction(WriteGraph graph) throws DatabaseException {
+            Command command = Commands.get(graph, "Simantics/Diagram/splitConnection");
+            command.execute(graph, graph.syncRequest(new IndexRoot(connection)),
+                    connection, splitCanvasPos.getX(), splitCanvasPos.getY());
+        }
+    }
+
+    public static class Join extends Helper {
+        private final Collection<Resource> flags;
+
+        public Join(Session session, ICanvasContext context, Collection<Resource> flags) {
+            super("Join Flags", session, context);
+            setImageDescriptor(Activator.LINK_ICON);
+            this.flags = flags;
+        }
+
+        @Override
+        protected void performAction(WriteGraph graph) throws DatabaseException {
+            Command command = Commands.get(graph, "Simantics/Diagram/joinFlagsLocal");
+            command.execute(graph, graph.syncRequest(new IndexRoot(flags.iterator().next())), flags);
+        }
+    }
+
 }
\ No newline at end of file