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