]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/handler/ConnectionCommandHandler.java
(refs #7595) Started SCL/Reflection module
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / handler / ConnectionCommandHandler.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.AffineTransform;
15 import java.awt.geom.Line2D;
16 import java.awt.geom.Point2D;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.HashSet;
21 import java.util.Set;
22
23 import org.simantics.Simantics;
24 import org.simantics.db.Resource;
25 import org.simantics.db.WriteGraph;
26 import org.simantics.db.common.request.WriteRequest;
27 import org.simantics.db.exception.DatabaseException;
28 import org.simantics.diagram.content.ConnectionUtil;
29 import org.simantics.diagram.content.EdgeResource;
30 import org.simantics.diagram.stubs.DiagramResource;
31 import org.simantics.diagram.synchronization.graph.RemoveBranchpoint;
32 import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;
33 import org.simantics.g2d.connection.ConnectionEntity;
34 import org.simantics.g2d.diagram.DiagramHints;
35 import org.simantics.g2d.diagram.IDiagram;
36 import org.simantics.g2d.diagram.handler.PickRequest;
37 import org.simantics.g2d.diagram.handler.Topology;
38 import org.simantics.g2d.diagram.handler.Topology.Connection;
39 import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant;
40 import org.simantics.g2d.diagram.participant.Selection;
41 import org.simantics.g2d.element.ElementHints;
42 import org.simantics.g2d.element.ElementUtils;
43 import org.simantics.g2d.element.IElement;
44 import org.simantics.g2d.elementclass.BranchPoint;
45 import org.simantics.g2d.elementclass.BranchPoint.Direction;
46 import org.simantics.g2d.participant.MouseUtil;
47 import org.simantics.g2d.participant.MouseUtil.MouseInfo;
48 import org.simantics.g2d.participant.WorkbenchStatusLine;
49 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
50 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
51 import org.simantics.scenegraph.g2d.events.command.Commands;
52 import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;
53 import org.simantics.ui.SimanticsUI;
54 import org.simantics.utils.ui.ErrorLogger;
55
56 /**
57  * A participant for handling:
58  * <ul>
59  * <li>DELETE for deleting route points from DIA.Connection type connections
60  * <li>SPLIT_CONNECTION for creating new route points into DIA.Connection type connections
61  * <li>ROTATE_ELEMENT_CW & ROTATE_ELEMENT_CCW for rotating route points orientations.
62  * </ul>
63  * 
64  *  NOTE: this participant does nothing useful with DIA.RouteGraphConnection type connections.
65  * 
66  * @author Tuukka Lehtonen
67  * 
68  * TODO: start using {@link WorkbenchStatusLine}
69  */
70 public class ConnectionCommandHandler extends AbstractDiagramParticipant {
71
72     @Dependency MouseUtil mouseUtil;
73     @Dependency Selection selection;
74     //@Dependency WorkbenchStatusLine statusLine;
75
76     @EventHandler(priority = 100)
77     public boolean handleCommand(CommandEvent event) {
78         if (Commands.DELETE.equals(event.command)) {
79             try {
80                 return joinConnection();
81             } catch (DatabaseException e) {
82                 ErrorLogger.defaultLogError(e);
83                 return false;
84             }
85         } else if (Commands.SPLIT_CONNECTION.equals(event.command) && routePointsEnabled()) {
86             return splitConnection();
87         } else if (Commands.ROTATE_ELEMENT_CW.equals(event.command) || Commands.ROTATE_ELEMENT_CCW.equals(event.command)) {
88             boolean clockWise = Commands.ROTATE_ELEMENT_CW.equals(event.command);
89             try {
90                 return rotateBranchPoint(clockWise);
91             } catch (DatabaseException e) {
92                 ErrorLogger.defaultLogError(e);
93                 return false;
94             }
95         }
96         return false;
97     }
98
99     private boolean routePointsEnabled() {
100         return Boolean.TRUE.equals(diagram.getHint(DiagramHints.KEY_ALLOW_ROUTE_POINTS));
101     }
102
103     boolean joinConnection() throws DatabaseException {
104
105         final Set<IElement> routePoints = getBranchPoints(2);
106
107         if (routePoints.isEmpty())
108             return false;
109
110         selection.clear(0);
111
112         Simantics.getSession().sync(new WriteRequest() {
113             @Override
114             public void perform(WriteGraph graph) throws DatabaseException {
115                 for (IElement routePoint : routePoints) {
116                     Object node = ElementUtils.getObject(routePoint);
117                     if (node instanceof Resource) {
118                         new RemoveBranchpoint(routePoint).perform(graph);
119                     }
120                 }
121             }
122         });
123         setDirty();
124
125         //statusLine.message("Deleted " + routePoints.size() + " route points");
126         return true;
127     }
128
129     private boolean rotateBranchPoint(final boolean clockWise) throws DatabaseException {
130         final Set<IElement> routePoints = getBranchPoints(Integer.MAX_VALUE);
131         if (routePoints.isEmpty())
132             return false;
133
134         // Rotate branch points.
135         try {
136             SimanticsUI.getSession().syncRequest(new WriteRequest() {
137                 @Override
138                 public void perform(WriteGraph graph) throws DatabaseException {
139                     DiagramResource DIA = DiagramResource.getInstance(graph);
140                     for (IElement bp : routePoints) {
141                         Resource bpr = (Resource) ElementUtils.getObject(bp);
142                         boolean vertical = graph.hasStatement(bpr, DIA.Vertical, bpr);
143                         boolean horizontal = graph.hasStatement(bpr, DIA.Horizontal, bpr);
144                         Direction dir = Direction.toDirection(horizontal, vertical);
145                         Direction newDir = clockWise ? dir.cycleNext() : dir.cyclePrevious();
146                         switch (newDir) {
147                             case Any:
148                                 graph.deny(bpr, DIA.Vertical);
149                                 graph.deny(bpr, DIA.Horizontal);
150                                 break;
151                             case Horizontal:
152                                 graph.deny(bpr, DIA.Vertical);
153                                 graph.claim(bpr, DIA.Horizontal, bpr);
154                                 break;
155                             case Vertical:
156                                 graph.deny(bpr, DIA.Horizontal);
157                                 graph.claim(bpr, DIA.Vertical, bpr);
158                                 break;
159                         }
160                     }
161                 }
162             });
163         } catch (DatabaseException e) {
164             ErrorLogger.defaultLogError(e);
165         }
166
167         return true;
168     }
169
170     boolean splitConnection() {
171
172         final IElement edge = getSingleConnectionSegment();
173         if (edge == null)
174             return false;
175         final IDiagram diagram = ElementUtils.peekDiagram(edge);
176         if (diagram == null)
177             return false;
178
179         MouseInfo mi = mouseUtil.getMouseInfo(0);
180         final Point2D mousePos = mi.canvasPosition;
181
182         ISnapAdvisor snap = getHint(DiagramHints.SNAP_ADVISOR);
183         if (snap != null)
184             snap.snap(mousePos);
185
186         final AffineTransform splitPos = AffineTransform.getTranslateInstance(mousePos.getX(), mousePos.getY());
187
188         try {
189             SimanticsUI.getSession().syncRequest(new WriteRequest() {
190                 @Override
191                 public void perform(WriteGraph graph) throws DatabaseException {
192                     DiagramResource DIA = DiagramResource.getInstance(graph);
193
194                     // Split the edge with a new branch point
195                     ConnectionUtil cu = new ConnectionUtil(graph);
196                     EdgeResource segment = (EdgeResource) ElementUtils.getObject(edge);
197                     Resource bp = cu.split(segment, splitPos);
198
199                     Line2D nearestLine = ConnectionUtil.resolveNearestEdgeLineSegment(mousePos, edge);
200                     if (nearestLine != null) {
201                         double angle = Math.atan2(
202                                 Math.abs(nearestLine.getY2() - nearestLine.getY1()),
203                                 Math.abs(nearestLine.getX2() - nearestLine.getX1())
204                         );
205
206                         if (angle >= 0 && angle < Math.PI / 4) {
207                             graph.claim(bp, DIA.Horizontal, bp);
208                         } else if (angle > Math.PI / 4 && angle <= Math.PI / 2) {
209                             graph.claim(bp, DIA.Vertical, bp);
210                         }
211                     }
212                 }
213             });
214
215             selection.clear(0);
216
217         } catch (DatabaseException e) {
218             ErrorLogger.defaultLogError(e);
219         }
220
221         return false;
222     }
223
224     /**
225      * @return all route points in the current diagram selection if and only if
226      *         only route points are selected. If anything else is selected,
227      *         even branch points, an empty set will be returned.
228      */
229     Set<IElement> getBranchPoints(int maxDegree) throws DatabaseException {
230
231         Set<IElement> ss = selection.getSelection(0);
232         if (ss.isEmpty())
233             return Collections.emptySet();
234
235         final Set<IElement> result = new HashSet<IElement>();
236         Collection<Connection> connections = new ArrayList<Connection>();
237         for (IElement e : ss) {
238             if (!e.getElementClass().containsClass(BranchPoint.class))
239                 return Collections.emptySet();
240             ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);
241             if (ce == null)
242                 return Collections.emptySet();
243             IDiagram diagram = ElementUtils.peekDiagram(e);
244             if (diagram == null)
245                 return Collections.emptySet();
246             for (Topology topology : diagram.getDiagramClass().getItemsByClass(Topology.class)) {
247                 connections.clear();
248                 topology.getConnections(e, ElementUtils.getSingleTerminal(e), connections);
249                 int degree = connections.size();
250                 if (degree < 2 || degree > maxDegree)
251                     return Collections.emptySet();
252                 result.add(e);
253             }
254         }
255
256         return result;
257     }
258
259     /**
260      * @return if a single edge is selected, return the edge. If a single
261      *         connection is selected and it contains only one edge segment,
262      *         return it. Otherwise return <code>null</code>.
263      */
264     IElement getSingleConnectionSegment() {
265         Set<IElement> s = selection.getSelection(0);
266         if (s.size() != 1)
267             return null;
268         IElement e = s.iterator().next();
269         if (PickRequest.PickFilter.FILTER_CONNECTIONS.accept(e)) {
270             // Does this connection have only one edge and no branch points?
271             ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);
272             Collection<IElement> coll = ce.getBranchPoints(null);
273             if (!coll.isEmpty())
274                 return null;
275             ce.getSegments(coll);
276             if (coll.size() != 1)
277                 return null;
278             // Return the only edge element of the whole connection.
279             return coll.iterator().next();
280         }
281         if (PickRequest.PickFilter.FILTER_CONNECTION_EDGES.accept(e))
282             return e;
283         return null;
284     }
285
286     /**
287      * @return the selected top-level connection element if and only if the
288      *         selection contains the connection or parts of the same connection
289      *         (edge segments or branch/route points) and nothing else.
290      */
291     IElement getSingleConnection() {
292
293         Set<IElement> ss = selection.getSelection(0);
294         if (ss.isEmpty())
295             return null;
296
297         IElement result = null;
298
299         for (IElement e : ss) {
300             ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);
301             if (ce == null)
302                 continue;
303             if (result != null && !ce.getConnection().equals(result))
304                 return null;
305             result = ce.getConnection();
306         }
307
308         return result;
309     }
310
311 }