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