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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.diagram.handler;
\r
14 import java.awt.geom.AffineTransform;
\r
15 import java.awt.geom.Line2D;
\r
16 import java.awt.geom.Point2D;
\r
17 import java.util.ArrayList;
\r
18 import java.util.Collection;
\r
19 import java.util.Collections;
\r
20 import java.util.HashSet;
\r
21 import java.util.Set;
\r
23 import org.simantics.Simantics;
\r
24 import org.simantics.db.Resource;
\r
25 import org.simantics.db.WriteGraph;
\r
26 import org.simantics.db.common.request.WriteRequest;
\r
27 import org.simantics.db.exception.DatabaseException;
\r
28 import org.simantics.diagram.content.ConnectionUtil;
\r
29 import org.simantics.diagram.content.EdgeResource;
\r
30 import org.simantics.diagram.stubs.DiagramResource;
\r
31 import org.simantics.diagram.synchronization.graph.RemoveBranchpoint;
\r
32 import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;
\r
33 import org.simantics.g2d.connection.ConnectionEntity;
\r
34 import org.simantics.g2d.diagram.DiagramHints;
\r
35 import org.simantics.g2d.diagram.IDiagram;
\r
36 import org.simantics.g2d.diagram.handler.PickRequest;
\r
37 import org.simantics.g2d.diagram.handler.Topology;
\r
38 import org.simantics.g2d.diagram.handler.Topology.Connection;
\r
39 import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant;
\r
40 import org.simantics.g2d.diagram.participant.Selection;
\r
41 import org.simantics.g2d.element.ElementHints;
\r
42 import org.simantics.g2d.element.ElementUtils;
\r
43 import org.simantics.g2d.element.IElement;
\r
44 import org.simantics.g2d.elementclass.BranchPoint;
\r
45 import org.simantics.g2d.elementclass.BranchPoint.Direction;
\r
46 import org.simantics.g2d.participant.MouseUtil;
\r
47 import org.simantics.g2d.participant.MouseUtil.MouseInfo;
\r
48 import org.simantics.g2d.participant.WorkbenchStatusLine;
\r
49 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
\r
50 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
\r
51 import org.simantics.scenegraph.g2d.events.command.Commands;
\r
52 import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;
\r
53 import org.simantics.ui.SimanticsUI;
\r
54 import org.simantics.utils.ui.ErrorLogger;
\r
57 * A participant for handling:
\r
59 * <li>DELETE for deleting route points from DIA.Connection type connections
\r
60 * <li>SPLIT_CONNECTION for creating new route points into DIA.Connection type connections
\r
61 * <li>ROTATE_ELEMENT_CW & ROTATE_ELEMENT_CCW for rotating route points orientations.
\r
64 * NOTE: this participant does nothing useful with DIA.RouteGraphConnection type connections.
\r
66 * @author Tuukka Lehtonen
\r
68 * TODO: start using {@link WorkbenchStatusLine}
\r
70 public class ConnectionCommandHandler extends AbstractDiagramParticipant {
\r
72 @Dependency MouseUtil mouseUtil;
\r
73 @Dependency Selection selection;
\r
74 //@Dependency WorkbenchStatusLine statusLine;
\r
76 @EventHandler(priority = 100)
\r
77 public boolean handleCommand(CommandEvent event) {
\r
78 if (Commands.DELETE.equals(event.command)) {
\r
80 return joinConnection();
\r
81 } catch (DatabaseException e) {
\r
82 ErrorLogger.defaultLogError(e);
\r
85 } else if (Commands.SPLIT_CONNECTION.equals(event.command) && routePointsEnabled()) {
\r
86 return splitConnection();
\r
87 } else if (Commands.ROTATE_ELEMENT_CW.equals(event.command) || Commands.ROTATE_ELEMENT_CCW.equals(event.command)) {
\r
88 boolean clockWise = Commands.ROTATE_ELEMENT_CW.equals(event.command);
\r
90 return rotateBranchPoint(clockWise);
\r
91 } catch (DatabaseException e) {
\r
92 ErrorLogger.defaultLogError(e);
\r
99 private boolean routePointsEnabled() {
\r
100 return Boolean.TRUE.equals(diagram.getHint(DiagramHints.KEY_ALLOW_ROUTE_POINTS));
\r
103 boolean joinConnection() throws DatabaseException {
\r
105 final Set<IElement> routePoints = getBranchPoints(2);
\r
107 if (routePoints.isEmpty())
\r
110 selection.clear(0);
\r
112 Simantics.getSession().sync(new WriteRequest() {
\r
114 public void perform(WriteGraph graph) throws DatabaseException {
\r
115 for (IElement routePoint : routePoints) {
\r
116 Object node = ElementUtils.getObject(routePoint);
\r
117 if (node instanceof Resource) {
\r
118 new RemoveBranchpoint(routePoint).perform(graph);
\r
125 //statusLine.message("Deleted " + routePoints.size() + " route points");
\r
129 private boolean rotateBranchPoint(final boolean clockWise) throws DatabaseException {
\r
130 final Set<IElement> routePoints = getBranchPoints(Integer.MAX_VALUE);
\r
131 if (routePoints.isEmpty())
\r
134 // Rotate branch points.
\r
136 SimanticsUI.getSession().syncRequest(new WriteRequest() {
\r
138 public void perform(WriteGraph graph) throws DatabaseException {
\r
139 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
140 for (IElement bp : routePoints) {
\r
141 Resource bpr = (Resource) ElementUtils.getObject(bp);
\r
142 boolean vertical = graph.hasStatement(bpr, DIA.Vertical, bpr);
\r
143 boolean horizontal = graph.hasStatement(bpr, DIA.Horizontal, bpr);
\r
144 Direction dir = Direction.toDirection(horizontal, vertical);
\r
145 Direction newDir = clockWise ? dir.cycleNext() : dir.cyclePrevious();
\r
148 graph.deny(bpr, DIA.Vertical);
\r
149 graph.deny(bpr, DIA.Horizontal);
\r
152 graph.deny(bpr, DIA.Vertical);
\r
153 graph.claim(bpr, DIA.Horizontal, bpr);
\r
156 graph.deny(bpr, DIA.Horizontal);
\r
157 graph.claim(bpr, DIA.Vertical, bpr);
\r
163 } catch (DatabaseException e) {
\r
164 ErrorLogger.defaultLogError(e);
\r
170 boolean splitConnection() {
\r
172 final IElement edge = getSingleConnectionSegment();
\r
175 final IDiagram diagram = ElementUtils.peekDiagram(edge);
\r
176 if (diagram == null)
\r
179 MouseInfo mi = mouseUtil.getMouseInfo(0);
\r
180 final Point2D mousePos = mi.canvasPosition;
\r
182 ISnapAdvisor snap = getHint(DiagramHints.SNAP_ADVISOR);
\r
184 snap.snap(mousePos);
\r
186 final AffineTransform splitPos = AffineTransform.getTranslateInstance(mousePos.getX(), mousePos.getY());
\r
189 SimanticsUI.getSession().syncRequest(new WriteRequest() {
\r
191 public void perform(WriteGraph graph) throws DatabaseException {
\r
192 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
194 // Split the edge with a new branch point
\r
195 ConnectionUtil cu = new ConnectionUtil(graph);
\r
196 EdgeResource segment = (EdgeResource) ElementUtils.getObject(edge);
\r
197 Resource bp = cu.split(segment, splitPos);
\r
199 Line2D nearestLine = ConnectionUtil.resolveNearestEdgeLineSegment(mousePos, edge);
\r
200 if (nearestLine != null) {
\r
201 double angle = Math.atan2(
\r
202 Math.abs(nearestLine.getY2() - nearestLine.getY1()),
\r
203 Math.abs(nearestLine.getX2() - nearestLine.getX1())
\r
206 if (angle >= 0 && angle < Math.PI / 4) {
\r
207 graph.claim(bp, DIA.Horizontal, bp);
\r
208 } else if (angle > Math.PI / 4 && angle <= Math.PI / 2) {
\r
209 graph.claim(bp, DIA.Vertical, bp);
\r
215 selection.clear(0);
\r
217 } catch (DatabaseException e) {
\r
218 ErrorLogger.defaultLogError(e);
\r
225 * @return all route points in the current diagram selection if and only if
\r
226 * only route points are selected. If anything else is selected,
\r
227 * even branch points, an empty set will be returned.
\r
229 Set<IElement> getBranchPoints(int maxDegree) throws DatabaseException {
\r
231 Set<IElement> ss = selection.getSelection(0);
\r
233 return Collections.emptySet();
\r
235 final Set<IElement> result = new HashSet<IElement>();
\r
236 Collection<Connection> connections = new ArrayList<Connection>();
\r
237 for (IElement e : ss) {
\r
238 if (!e.getElementClass().containsClass(BranchPoint.class))
\r
239 return Collections.emptySet();
\r
240 ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);
\r
242 return Collections.emptySet();
\r
243 IDiagram diagram = ElementUtils.peekDiagram(e);
\r
244 if (diagram == null)
\r
245 return Collections.emptySet();
\r
246 for (Topology topology : diagram.getDiagramClass().getItemsByClass(Topology.class)) {
\r
247 connections.clear();
\r
248 topology.getConnections(e, ElementUtils.getSingleTerminal(e), connections);
\r
249 int degree = connections.size();
\r
250 if (degree < 2 || degree > maxDegree)
\r
251 return Collections.emptySet();
\r
260 * @return if a single edge is selected, return the edge. If a single
\r
261 * connection is selected and it contains only one edge segment,
\r
262 * return it. Otherwise return <code>null</code>.
\r
264 IElement getSingleConnectionSegment() {
\r
265 Set<IElement> s = selection.getSelection(0);
\r
268 IElement e = s.iterator().next();
\r
269 if (PickRequest.PickFilter.FILTER_CONNECTIONS.accept(e)) {
\r
270 // Does this connection have only one edge and no branch points?
\r
271 ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);
\r
272 Collection<IElement> coll = ce.getBranchPoints(null);
\r
273 if (!coll.isEmpty())
\r
275 ce.getSegments(coll);
\r
276 if (coll.size() != 1)
\r
278 // Return the only edge element of the whole connection.
\r
279 return coll.iterator().next();
\r
281 if (PickRequest.PickFilter.FILTER_CONNECTION_EDGES.accept(e))
\r
287 * @return the selected top-level connection element if and only if the
\r
288 * selection contains the connection or parts of the same connection
\r
289 * (edge segments or branch/route points) and nothing else.
\r
291 IElement getSingleConnection() {
\r
293 Set<IElement> ss = selection.getSelection(0);
\r
297 IElement result = null;
\r
299 for (IElement e : ss) {
\r
300 ConnectionEntity ce = e.getHint(ElementHints.KEY_CONNECTION_ENTITY);
\r
303 if (result != null && !ce.getConnection().equals(result))
\r
305 result = ce.getConnection();
\r