]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/handler/ConnectionSplitAndJoin.java
9e7a1637ebe2eba7ec0605b969de441f3ac3117c
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / handler / ConnectionSplitAndJoin.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.diagram.handler;\r
13 \r
14 import java.awt.geom.Point2D;\r
15 import java.util.ArrayList;\r
16 import java.util.Collection;\r
17 import java.util.Collections;\r
18 import java.util.Iterator;\r
19 import java.util.Set;\r
20 \r
21 import org.eclipse.jface.action.Action;\r
22 import org.eclipse.jface.action.ActionContributionItem;\r
23 import org.eclipse.jface.action.IContributionItem;\r
24 import org.eclipse.swt.widgets.Menu;\r
25 import org.eclipse.ui.IWorkbenchPart;\r
26 import org.simantics.db.ReadGraph;\r
27 import org.simantics.db.Resource;\r
28 import org.simantics.db.Session;\r
29 import org.simantics.db.WriteGraph;\r
30 import org.simantics.db.common.request.IndexRoot;\r
31 import org.simantics.db.common.request.WriteRequest;\r
32 import org.simantics.db.exception.DatabaseException;\r
33 import org.simantics.diagram.content.ConnectionUtil;\r
34 import org.simantics.diagram.content.EdgeResource;\r
35 import org.simantics.diagram.flag.FlagUtil;\r
36 import org.simantics.diagram.flag.Splitter;\r
37 import org.simantics.diagram.internal.Activator;\r
38 import org.simantics.diagram.stubs.DiagramResource;\r
39 import org.simantics.g2d.canvas.ICanvasContext;\r
40 import org.simantics.g2d.diagram.DiagramHints;\r
41 import org.simantics.g2d.diagram.participant.Selection;\r
42 import org.simantics.g2d.element.IElement;\r
43 import org.simantics.scl.commands.Command;\r
44 import org.simantics.scl.commands.Commands;\r
45 import org.simantics.structural.stubs.StructuralResource2;\r
46 import org.simantics.ui.contribution.DynamicMenuContribution;\r
47 import org.simantics.utils.threads.ThreadUtils;\r
48 import org.simantics.utils.ui.AdaptionUtils;\r
49 import org.simantics.utils.ui.ExceptionUtils;\r
50 import org.simantics.utils.ui.workbench.WorkbenchUtils;\r
51 \r
52 /**\r
53  * @author Tuukka Lehtonen\r
54  */\r
55 public class ConnectionSplitAndJoin extends DynamicMenuContribution {\r
56 \r
57     private static final IContributionItem[] NONE = {};\r
58 \r
59     private ICanvasContext canvas;\r
60 \r
61     @Override\r
62     public void fill(Menu menu, int index) {\r
63         // Need to grab active part here, we're still in the SWT thread.\r
64         IWorkbenchPart activePart = WorkbenchUtils.getActiveWorkbenchPart();\r
65         if (activePart == null)\r
66             return;\r
67         ICanvasContext ctx = (ICanvasContext) activePart.getAdapter(ICanvasContext.class);\r
68         if (ctx == null)\r
69             return;\r
70 \r
71         this.canvas = ctx;\r
72         try {\r
73             super.fill(menu, index);\r
74         } finally {\r
75             this.canvas = null;\r
76         }\r
77     }\r
78 \r
79     @Override\r
80     protected IContributionItem[] getContributionItems(ReadGraph graph, Object[] selection) throws DatabaseException {\r
81         // If selection contains:\r
82         // a) only diagram-locally connected flags, that each are connected to something, allow joining of those flag into connections\r
83         // b) a single connection edge element, allow splitting into a connected flag pair\r
84 \r
85         Collection<Resource> localConnectedFlags = Collections.emptyList();\r
86         Resource routeGraphConnection = null;\r
87         EdgeResource edge = null;\r
88         if (selection.length == 1) {\r
89             routeGraphConnection = getRouteGraphConnection(graph, selection[0]);\r
90             if (routeGraphConnection == null) {\r
91                 edge = getEdge(graph, selection[0]);\r
92                 if (edge == null)\r
93                     localConnectedFlags = getLocalConnectedFlags(graph, selection);\r
94             }\r
95         } else {\r
96             localConnectedFlags = getLocalConnectedFlags(graph, selection);\r
97         }\r
98 \r
99         if (!localConnectedFlags.isEmpty()) {\r
100             return new IContributionItem[] {\r
101                     new ActionContributionItem(new Join(graph.getSession(), canvas, localConnectedFlags))\r
102             };\r
103         } else if (edge != null) {\r
104             Selection sel = canvas.getAtMostOneItemOfClass(Selection.class);\r
105             if (sel == null)\r
106                 return NONE;\r
107             Set<IElement> elems = sel.getSelection(0);\r
108             if (elems.size() != 1)\r
109                 return NONE;\r
110             IElement edgeElement = ConnectionUtil.getSingleEdge(elems.iterator().next());\r
111             if (edgeElement == null)\r
112                 return NONE;\r
113             Point2D canvasPosition = canvas.getHintStack().getHint(DiagramHints.POPUP_MENU_CANVAS_POSITION);\r
114             if (canvasPosition == null)\r
115                 return NONE;\r
116 \r
117             return new IContributionItem[] {\r
118                     new ActionContributionItem(new Split(graph.getSession(), canvas, canvasPosition, edgeElement, edge))\r
119             };\r
120         } else if (routeGraphConnection != null) {\r
121             Selection sel = canvas.getAtMostOneItemOfClass(Selection.class);\r
122             if (sel == null)\r
123                 return NONE;\r
124             Set<IElement> elems = sel.getSelection(0);\r
125             if (elems.size() != 1)\r
126                 return NONE;\r
127             Point2D canvasPosition = canvas.getHintStack().getHint(DiagramHints.POPUP_MENU_CANVAS_POSITION);\r
128             if (canvasPosition == null)\r
129                 return NONE;\r
130 \r
131             return new IContributionItem[] {\r
132                     new ActionContributionItem(new SplitRouteGraph(graph.getSession(), canvas, canvasPosition, routeGraphConnection))\r
133             };\r
134         }\r
135         return NONE;\r
136     }\r
137 \r
138     private Resource getRouteGraphConnection(ReadGraph graph, Object object) throws DatabaseException {\r
139         DiagramResource DIA = DiagramResource.getInstance(graph);\r
140         Resource connection = AdaptionUtils.adaptToSingle(object, Resource.class);\r
141         if (connection != null && graph.isInstanceOf(connection, DIA.RouteGraphConnection))\r
142             return connection;\r
143         return null;\r
144     }\r
145 \r
146     private Collection<Resource> getLocalConnectedFlags(ReadGraph graph, Object[] selection) throws DatabaseException {\r
147         DiagramResource DIA = DiagramResource.getInstance(graph);\r
148         ArrayList<Resource> result = new ArrayList<Resource>(4);\r
149         for (Object s : selection) {\r
150             Resource r = AdaptionUtils.adaptToSingle(s, Resource.class);\r
151             if (r == null || !graph.isInstanceOf(r, DIA.Flag))\r
152                 return Collections.emptyList();\r
153             Resource counterpart = FlagUtil.getPossibleCounterpart(graph, r);\r
154             if (counterpart == null)\r
155                 return Collections.emptyList();\r
156             if (!FlagUtil.isJoinedInSingleDiagram(graph, r))\r
157                 return Collections.emptyList();\r
158             if (!isConnectedToSomething(graph, r) || !isConnectedToSomething(graph, counterpart))\r
159                 return Collections.emptyList();\r
160             result.add(r);\r
161         }\r
162         return result;\r
163     }\r
164 \r
165     private boolean isConnectedToSomething(ReadGraph graph, Resource flag) throws DatabaseException {\r
166         DiagramResource DIA = DiagramResource.getInstance(graph);\r
167         StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
168         for (Resource connector : graph.getObjects(flag, STR.IsConnectedTo)) {\r
169             Resource cntr = graph.getPossibleObject(connector, DIA.AreConnected);\r
170             Resource conn = ConnectionUtil.getConnection(graph, cntr);\r
171             if (cntr == null || conn == null)\r
172                 return false;\r
173             return true;\r
174         }\r
175         return false;\r
176     }\r
177 \r
178     private EdgeResource getEdge(ReadGraph graph, Object object) throws DatabaseException {\r
179         DiagramResource DIA = DiagramResource.getInstance(graph);\r
180 \r
181         Resource connection = null;\r
182         EdgeResource edge = AdaptionUtils.adaptToSingle(object, EdgeResource.class);\r
183         if (edge != null)\r
184             //connection = ConnectionUtil.getConnection(graph, edge);\r
185             return edge;\r
186         else {\r
187             connection = AdaptionUtils.adaptToSingle(object, Resource.class);\r
188         }\r
189 \r
190         if (connection != null && graph.isInstanceOf(connection, DiagramResource.getInstance(graph).Connection)) {\r
191             Collection<Resource> connectors = graph.getObjects(connection, DIA.HasConnector);\r
192             Collection<Resource> branchPoints = graph.getObjects(connection, DIA.HasBranchPoint);\r
193             if (branchPoints.isEmpty() && connectors.size() == 2) {\r
194                 Iterator<Resource> it = connectors.iterator();\r
195                 Resource connector1 = it.next();\r
196                 Resource connector2 = it.next();\r
197                 return new EdgeResource(connector1, connector2);\r
198             }\r
199         }\r
200 \r
201         return null;\r
202     }\r
203 \r
204     public static abstract class Helper extends Action {\r
205 \r
206         protected final Session        session;\r
207         protected final ICanvasContext context;\r
208 \r
209         public Helper(String label, Session session, ICanvasContext context) {\r
210             super(label);\r
211             this.session = session;\r
212             this.context = context;\r
213         }\r
214 \r
215         @Override\r
216         public void run() {\r
217             try {\r
218                 session.syncRequest(new WriteRequest() {\r
219                     @Override\r
220                     public void perform(WriteGraph graph) throws DatabaseException {\r
221                         graph.markUndoPoint();\r
222                         performAction(graph);\r
223                     }\r
224                 });\r
225                 ThreadUtils.asyncExec(context.getThreadAccess(), new Runnable() {\r
226                     @Override\r
227                     public void run() {\r
228                         if (context.isDisposed())\r
229                             return;\r
230                         Selection selection = context.getAtMostOneItemOfClass(Selection.class);\r
231                         if (selection != null) {\r
232                             // This prevents workbench selection from being left over.\r
233                             // Also prevents scene graph crap from being left on the screen.\r
234                             selection.clear(0);\r
235                         }\r
236                     }\r
237                 });\r
238             } catch (DatabaseException e) {\r
239                 ExceptionUtils.logError(e);\r
240             }\r
241         }\r
242 \r
243         /**\r
244          * @param graph\r
245          */\r
246         protected abstract void performAction(WriteGraph graph) throws DatabaseException;\r
247 \r
248     }\r
249 \r
250     public static class Split extends Helper {\r
251         private final Point2D      splitCanvasPos;\r
252         private final IElement     edgeElement;\r
253         private final EdgeResource edge;\r
254 \r
255         public Split(Session session, ICanvasContext context, Point2D splitCanvasPos, IElement edgeElement, EdgeResource edge) {\r
256             super("Split Connection", session, context);\r
257             setImageDescriptor(Activator.LINK_BREAK_ICON);\r
258             this.splitCanvasPos = splitCanvasPos;\r
259             this.edgeElement = edgeElement;\r
260             this.edge = edge;\r
261         }\r
262 \r
263         @Override\r
264         protected void performAction(WriteGraph graph) throws DatabaseException {\r
265             new Splitter(graph).split(graph, edgeElement, edge, splitCanvasPos);\r
266         }\r
267     }\r
268 \r
269     public static class SplitRouteGraph extends Helper {\r
270         private final Point2D  splitCanvasPos;\r
271         private final Resource connection;\r
272 \r
273         public SplitRouteGraph(Session session, ICanvasContext context, Point2D splitCanvasPos, Resource connection) {\r
274             super("Split Connection", session, context);\r
275             setImageDescriptor(Activator.LINK_BREAK_ICON);\r
276             this.splitCanvasPos = splitCanvasPos;\r
277             this.connection = connection;\r
278         }\r
279 \r
280         @Override\r
281         protected void performAction(WriteGraph graph) throws DatabaseException {\r
282             Command command = Commands.get(graph, "Simantics/Diagram/splitConnection");\r
283             command.execute(graph, graph.syncRequest(new IndexRoot(connection)),\r
284                     connection, splitCanvasPos.getX(), splitCanvasPos.getY());\r
285         }\r
286     }\r
287 \r
288     public static class Join extends Helper {\r
289         private final Collection<Resource> flags;\r
290 \r
291         public Join(Session session, ICanvasContext context, Collection<Resource> flags) {\r
292             super("Join Flags", session, context);\r
293             setImageDescriptor(Activator.LINK_ICON);\r
294             this.flags = flags;\r
295         }\r
296 \r
297         @Override\r
298         protected void performAction(WriteGraph graph) throws DatabaseException {\r
299             Command command = Commands.get(graph, "Simantics/Diagram/joinFlagsLocal");\r
300             command.execute(graph, graph.syncRequest(new IndexRoot(flags.iterator().next())), flags);\r
301         }\r
302     }\r
303 \r
304 }