1 /*******************************************************************************
\r
2 * Copyright (c) 2011 Association for Decentralized Information Management in
\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.profile;
\r
14 import java.awt.BasicStroke;
\r
15 import java.awt.Color;
\r
16 import java.awt.Stroke;
\r
17 import java.awt.geom.AffineTransform;
\r
18 import java.awt.geom.Line2D;
\r
19 import java.awt.geom.Point2D;
\r
20 import java.awt.geom.Rectangle2D;
\r
21 import java.util.ArrayList;
\r
22 import java.util.Collection;
\r
23 import java.util.Collections;
\r
24 import java.util.List;
\r
26 import org.simantics.db.ReadGraph;
\r
27 import org.simantics.db.Resource;
\r
28 import org.simantics.db.Statement;
\r
29 import org.simantics.db.common.utils.OrderedSetUtils;
\r
30 import org.simantics.db.exception.DatabaseException;
\r
31 import org.simantics.db.layer0.variable.Variable;
\r
32 import org.simantics.diagram.content.ConnectionUtil;
\r
33 import org.simantics.diagram.flag.FlagUtil;
\r
34 import org.simantics.diagram.stubs.DiagramResource;
\r
35 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
\r
36 import org.simantics.g2d.connection.handler.ConnectionHandler;
\r
37 import org.simantics.g2d.diagram.IDiagram;
\r
38 import org.simantics.g2d.diagram.handler.DataElementMap;
\r
39 import org.simantics.g2d.diagram.handler.PickRequest.PickFilter;
\r
40 import org.simantics.g2d.diagram.handler.Relationship;
\r
41 import org.simantics.g2d.diagram.handler.Topology.Connection;
\r
42 import org.simantics.g2d.element.ElementUtils;
\r
43 import org.simantics.g2d.element.IElement;
\r
44 import org.simantics.g2d.element.handler.BendsHandler;
\r
45 import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
\r
46 import org.simantics.g2d.utils.GeometryUtils;
\r
47 import org.simantics.modeling.ModelingResources;
\r
48 import org.simantics.scenegraph.INode;
\r
49 import org.simantics.scenegraph.g2d.G2DParentNode;
\r
50 import org.simantics.scenegraph.g2d.nodes.RelationshipNode2;
\r
51 import org.simantics.scenegraph.profile.DataNodeMap;
\r
52 import org.simantics.scenegraph.profile.EvaluationContext;
\r
53 import org.simantics.scenegraph.profile.common.ProfileVariables;
\r
54 import org.simantics.utils.datastructures.Pair;
\r
55 import org.simantics.utils.datastructures.map.Tuple;
\r
58 * @author Tuukka Lehtonen
\r
60 public class ShowRelatedElements extends StyleBase<Object> {
\r
62 protected Collection<Connection> terminalConnections = new ArrayList<Connection>(4);
\r
64 protected final BasicStroke highlightStroke = new BasicStroke(1.0f,
\r
65 BasicStroke.CAP_SQUARE, BasicStroke.CAP_SQUARE, 10.0f,
\r
66 new float[] { 4.0f, 6.0f }, 0);
\r
68 protected Line2D line = new Line2D.Double();
\r
71 * One possible result of
\r
72 * {@link ShowRelatedElements#calculateStyle(ReadGraph, Resource, Resource, Variable)}
\r
74 public static class Relation extends Tuple {
\r
75 public Relation(String relation,
\r
76 Resource element, AffineTransform elementTransform,
\r
77 Resource otherElement, AffineTransform otherElementTransform)
\r
79 super(relation, element, elementTransform, otherElement, otherElementTransform);
\r
81 public String getRelation() {
\r
82 return (String) getField(0);
\r
84 public Resource getElement() {
\r
85 return (Resource) getField(1);
\r
87 public AffineTransform getTransform() {
\r
88 return (AffineTransform) getField(2);
\r
90 public Resource getOtherElement() {
\r
91 return (Resource) getField(3);
\r
93 public AffineTransform getOtherTransform() {
\r
94 return (AffineTransform) getField(4);
\r
99 * Calculates a result that is contains the styled element, its parent and
\r
100 * transforms of both. This takes care of the style updating the visuals
\r
101 * properly every time the related components move.
\r
104 public Object calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration)
\r
105 throws DatabaseException {
\r
106 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
108 if (graph.isInstanceOf(element, DIA.Monitor)) {
\r
109 return calculateFromComponent(graph, runtimeDiagram, element, DIA.HasMonitorComponent);
\r
111 if (graph.isInstanceOf(element, DIA.Flag)) {
\r
112 return calculateFromFlag(graph, element);
\r
115 // Just return anything, no relationship to visualize.
\r
116 return Boolean.TRUE;
\r
119 protected Object calculateFromFlag(ReadGraph graph, Resource flag) throws DatabaseException {
\r
120 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
122 Resource relatedFlag = FlagUtil.getPossibleCounterpart(graph, flag);
\r
123 if (relatedFlag != null) {
\r
124 Collection<Resource> flagDiagrams = OrderedSetUtils.getOwnerLists(graph, flag, DIA.Diagram);
\r
125 Collection<Resource> relatedDiagrams = OrderedSetUtils.getOwnerLists(graph, relatedFlag, DIA.Diagram);
\r
126 if (!Collections.disjoint(flagDiagrams, relatedDiagrams)) {
\r
127 AffineTransform flagAt = DiagramGraphUtil.getTransform(graph, flag);
\r
128 AffineTransform relatedFlagAt = DiagramGraphUtil.getTransform(graph, relatedFlag);
\r
129 return new Relation(Relationship.RELATED_TO.toString(), flag, flagAt, relatedFlag, relatedFlagAt);
\r
133 return Boolean.TRUE;
\r
136 protected Object calculateFromComponent(ReadGraph graph, Resource runtime, Resource element, Resource elementToComponentRelation) throws DatabaseException {
\r
137 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
138 ModelingResources MOD = ModelingResources.getInstance(graph);
\r
140 Resource parentComponent = graph.getPossibleObject(element, elementToComponentRelation);
\r
141 if (parentComponent != null) {
\r
142 Resource parentElement = graph.getPossibleObject(parentComponent, MOD.ComponentToElement);
\r
143 if (parentElement != null) {
\r
144 AffineTransform at = DiagramGraphUtil.getTransform(graph, element);
\r
145 if (graph.isInstanceOf(parentElement, DIA.Connection))
\r
146 return new Relation(Relationship.CHILD_OF.toString(), element, at, parentElement,
\r
147 getConnectionOutputNodePosition(graph, runtime, parentElement));
\r
149 AffineTransform parentAt = DiagramGraphUtil.getTransform(graph, parentElement);
\r
150 return new Relation(Relationship.CHILD_OF.toString(), element, at, parentElement, parentAt);
\r
154 return Boolean.TRUE;
\r
157 protected AffineTransform getConnectionOutputNodePosition(ReadGraph graph, Resource runtime, Resource connection) throws DatabaseException {
\r
158 Statement toTail = ConnectionUtil.getConnectionTailNodeStatement(graph, connection);
\r
159 if (toTail == null)
\r
162 AffineTransform at = DiagramGraphUtil.getDynamicAffineTransform(graph, runtime, toTail.getObject());
\r
164 Resource connectionPoint = graph.getPossibleInverse(toTail.getPredicate());
\r
165 if (connectionPoint == null)
\r
168 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
169 for (Resource terminal : graph.getObjects(connectionPoint, DIA.HasConnectionPoint_Inverse)) {
\r
170 AffineTransform terminalAt = DiagramGraphUtil.getDynamicAffineTransform(graph, runtime, terminal);
\r
171 at.concatenate(terminalAt);
\r
178 public void applyStyleForItem(EvaluationContext evaluationContext, DataNodeMap map, Object item, Object value) {
\r
179 INode node = map.getNode(item);
\r
180 if (node == null) {
\r
181 evaluationContext.update();
\r
185 if (!(value instanceof Relation)) {
\r
186 cleanupStyleForNode(node);
\r
190 IDiagram diagram = evaluationContext.getConstant(ProfileKeys.DIAGRAM);
\r
192 Relation r = (Relation) value;
\r
194 DataElementMap emap = diagram.getDiagramClass().getSingleItem(DataElementMap.class);
\r
195 IElement element = emap.getElement(diagram, item);
\r
196 IElement otherElement = emap.getElement(diagram, r.getOtherElement());
\r
197 if (otherElement != null)
\r
198 addCorrespondence(r, (G2DParentNode) node, r.getRelation(), element, otherElement, highlightStroke, Color.GRAY);
\r
201 protected void addCorrespondence(Relation r, G2DParentNode p, String id, IElement from, IElement to, Stroke lineStroke, Color lineColor) {
\r
202 Pair<Point2D, Rectangle2D> p1 = toPoint(r.getTransform(), from);
\r
203 Pair<Point2D, Rectangle2D> p2 = toPoint(r.getOtherTransform(), to);
\r
204 if (p1 != null && p2 != null)
\r
205 addCorrespondence(p, id, p1, p2, lineStroke, lineColor);
\r
209 * @param parentTransform
\r
211 * @return origin and bounding box of the specified element in canvas
\r
212 * coordinate system
\r
214 protected Pair<Point2D, Rectangle2D> toPoint(AffineTransform parentTransform, IElement e) {
\r
215 if (PickFilter.FILTER_CONNECTIONS.accept(e)) {
\r
216 // For connections select the first found output terminal position as
\r
217 // the correspondence visualization point.
\r
219 ConnectionHandler ch = e.getElementClass().getSingleItem(ConnectionHandler.class);
\r
220 terminalConnections.clear();
\r
221 ch.getTerminalConnections(e, terminalConnections);
\r
222 for (Connection c : terminalConnections) {
\r
223 if (c.end != EdgeEnd.Begin)
\r
226 BendsHandler bh = c.edge.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);
\r
228 List<Point2D> points = new ArrayList<Point2D>();
\r
229 GeometryUtils.getPoints(bh.getPath(c.edge), points);
\r
230 if (!points.isEmpty())
\r
231 return Pair.make(points.get(0), null);
\r
233 AffineTransform at = parentTransform;
\r
234 Point2D point = new Point2D.Double(at.getTranslateX(), at.getTranslateY());
\r
235 return Pair.make(point, null);
\r
239 // Fall back to the default logic if connection output terminal
\r
243 // Using element origin prevents the relationship rubber band target
\r
244 // from changing every time the content of the node changes, e.g. the
\r
245 // text of a monitor.
\r
246 AffineTransform at = ElementUtils.getTransform(e);
\r
247 Point2D p = new Point2D.Double(at.getTranslateX(), at.getTranslateY());
\r
248 Rectangle2D r = ElementUtils.getElementBoundsOnDiagram(e).getBounds2D();
\r
249 return Pair.make(p, r);
\r
252 protected void addCorrespondence(G2DParentNode p, String id,
\r
253 Pair<Point2D, Rectangle2D> p1,
\r
254 Pair<Point2D, Rectangle2D> p2,
\r
255 Stroke lineStroke, Color lineColor)
\r
257 RelationshipNode2 node = p.getOrCreateNode(id, RelationshipNode2.class);
\r
258 node.setZIndex(-10);
\r
260 if (p1.second != null) {
\r
261 line.setLine(p1.first, p2.first);
\r
262 if (LineUtilities.clipLine(line, p1.second))
\r
263 p1.first.setLocation(line.getX2(), line.getY2());
\r
265 if (p2.second != null) {
\r
266 line.setLine(p1.first, p2.first);
\r
267 if (LineUtilities.clipLine(line, p2.second))
\r
268 p2.first.setLocation(line.getX1(), line.getY1());
\r
271 node.init(lineStroke, lineColor, p1.first, p2.first);
\r
275 protected void cleanupStyleForNode(INode node) {
\r
276 ProfileVariables.denyChild(node, "", Relationship.CHILD_OF.toString());
\r
277 //ProfileVariables.denyChild(node, "", Relationship.PARENT_OF.toString());
\r
278 ProfileVariables.denyChild(node, "", Relationship.RELATED_TO.toString());
\r
282 public String toString() {
\r
283 return "show related elements";
\r