1 /*******************************************************************************
\r
2 * Copyright (c) 2010, 2012 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.sysdyn.ui.elements.connections;
\r
14 import java.awt.BasicStroke;
\r
15 import java.awt.Color;
\r
16 import java.awt.Font;
\r
17 import java.awt.Shape;
\r
18 import java.awt.Stroke;
\r
19 import java.awt.geom.Path2D;
\r
20 import java.awt.geom.Rectangle2D;
\r
21 import java.beans.PropertyChangeEvent;
\r
22 import java.beans.PropertyChangeListener;
\r
23 import java.util.HashMap;
\r
24 import java.util.Map;
\r
26 import org.eclipse.core.runtime.IStatus;
\r
27 import org.eclipse.core.runtime.Status;
\r
28 import org.simantics.Simantics;
\r
29 import org.simantics.db.Resource;
\r
30 import org.simantics.db.WriteGraph;
\r
31 import org.simantics.db.common.request.WriteRequest;
\r
32 import org.simantics.db.exception.DatabaseException;
\r
33 import org.simantics.g2d.diagram.DiagramHints;
\r
34 import org.simantics.g2d.diagram.IDiagram;
\r
35 import org.simantics.g2d.diagram.handler.PickRequest.PickPolicy;
\r
36 import org.simantics.g2d.diagram.handler.Topology;
\r
37 import org.simantics.g2d.diagram.handler.Topology.Connection;
\r
38 import org.simantics.g2d.element.ElementClass;
\r
39 import org.simantics.g2d.element.ElementHints;
\r
40 import org.simantics.g2d.element.ElementUtils;
\r
41 import org.simantics.g2d.element.IElement;
\r
42 import org.simantics.g2d.element.SceneGraphNodeKey;
\r
43 import org.simantics.g2d.element.handler.EdgeVisuals;
\r
44 import org.simantics.g2d.element.handler.EdgeVisuals.ArrowType;
\r
45 import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
\r
46 import org.simantics.g2d.element.handler.Pick;
\r
47 import org.simantics.g2d.element.handler.SceneGraph;
\r
48 import org.simantics.g2d.element.handler.TerminalLayout;
\r
49 import org.simantics.g2d.element.handler.Transform;
\r
50 import org.simantics.g2d.element.handler.impl.ConfigurableEdgeVisuals;
\r
51 import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;
\r
52 import org.simantics.g2d.element.handler.impl.FillColorImpl;
\r
53 import org.simantics.g2d.element.handler.impl.ParentImpl;
\r
54 import org.simantics.g2d.element.handler.impl.SimpleElementLayers;
\r
55 import org.simantics.g2d.element.handler.impl.TextColorImpl;
\r
56 import org.simantics.g2d.element.handler.impl.TextFontImpl;
\r
57 import org.simantics.g2d.element.handler.impl.TextImpl;
\r
58 import org.simantics.g2d.elementclass.connection.EdgeClass.EdgeHandler;
\r
59 import org.simantics.g2d.elementclass.connection.EdgeClass.FixedTransform;
\r
60 import org.simantics.g2d.utils.Alignment;
\r
61 import org.simantics.scenegraph.g2d.G2DParentNode;
\r
62 import org.simantics.scenegraph.utils.NodeUtil;
\r
63 import org.simantics.sysdyn.ui.Activator;
\r
64 import org.simantics.sysdyn.ui.editor.routing.DependencyRouter;
\r
65 import org.simantics.utils.datastructures.Pair;
\r
66 import org.simantics.utils.datastructures.hints.IHintContext.Key;
\r
68 public class DependencyEdgeClass {
\r
70 public static final float DEFAULT_STROKE_WIDTH = 0.3f;
\r
72 private static class NodePick implements Pick {
\r
74 private static final long serialVersionUID = 1L;
\r
76 public static NodePick INSTANCE = new NodePick();
\r
79 public boolean pickTest(IElement e, Shape s, PickPolicy policy) {
\r
80 Rectangle2D pickRect = null;
\r
81 if (s instanceof Rectangle2D)
\r
82 pickRect = (Rectangle2D) s;
\r
84 // FIXME: suboptimal, but works.
\r
85 pickRect = s.getBounds2D();
\r
87 DependencyNode node = e.getHint(SysdynEdgeSceneGraph.KEY_SG_NODE);
\r
91 return Arcs.hitTest(node.getBeginBounds(), node.getEndBounds(), node.getAngle(), pickRect.getCenterX(), pickRect.getCenterY(), 1.7);
\r
97 public static final ElementClass CLASS =
\r
98 ElementClass.compile(
\r
99 SysdynEdgeSceneGraph.INSTANCE,
\r
100 EdgeHandler.INSTANCE,
\r
101 new ConfigurableEdgeVisuals(
\r
102 ArrowType.None, ArrowType.Fill,
\r
103 new BasicStroke(DEFAULT_STROKE_WIDTH,
\r
104 BasicStroke.CAP_BUTT,
\r
105 BasicStroke.JOIN_ROUND,
\r
108 FillColorImpl.BLACK,
\r
109 FixedTransform.INSTANCE,
\r
111 TextColorImpl.BLACK,
\r
112 TextFontImpl.DEFAULT,
\r
114 ConnectionSelectionOutline.INSTANCE,
\r
115 SimpleElementLayers.INSTANCE,
\r
116 ParentImpl.INSTANCE
\r
117 ).setId("EdgeClass.STRAIGHT");
\r
119 public static class SysdynEdgeSceneGraph implements SceneGraph {
\r
121 private static final long serialVersionUID = 2914383071126238996L;
\r
123 public static final SysdynEdgeSceneGraph INSTANCE = new SysdynEdgeSceneGraph();
\r
125 public static final Stroke ARROW_STROKE = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
\r
127 public static final Key KEY_SG_NODE = new SceneGraphNodeKey(DependencyNode.class, "EDGE_NODE");
\r
130 public void init(IElement e, G2DParentNode parent) {
\r
131 DependencyNode node = ElementUtils.getOrCreateNode(e, parent, KEY_SG_NODE, "edge_" + e.hashCode(), DependencyNode.class);
\r
133 Font font = ElementUtils.getTextFont(e);
\r
134 Color color = ElementUtils.getTextColor(e);
\r
136 HashMap<String, Object> properties = e.getHint(DiagramHints.PROPERTIES);
\r
137 Pair<?, ?> polarityPair = (Pair<?, ?>)properties.get("polarity");
\r
138 Pair<?, ?> polarityLocationPair = (Pair<?, ?>)properties.get("polarityLocation");
\r
139 boolean delayMark = properties.containsKey("delayMark");
\r
140 boolean arrowHead = !properties.containsKey("hideArrow");
\r
143 if(polarityLocationPair == null)
\r
144 location = DependencyNode.INSIDE;
\r
146 location = (String) polarityLocationPair.second;
\r
148 String text = polarityPair != null ? (String) polarityPair.second : "";
\r
149 node.init(text, location, delayMark, arrowHead, font, color, 0, 0, 0.235);
\r
155 public void cleanup(IElement e) {
\r
156 ElementUtils.removePossibleNode(e, KEY_SG_NODE);
\r
159 public void update(final IElement e) {
\r
161 DependencyNode node = e.getHint(KEY_SG_NODE);
\r
162 if(node == null) return;
\r
163 final IDiagram diagram = ElementUtils.peekDiagram(e);
\r
165 IElement edgeParent = ElementUtils.getParent(e);
\r
166 Object edgeParentObject = edgeParent != null ? edgeParent.getHint(ElementHints.KEY_OBJECT) : null;
\r
167 final Resource parentElement = edgeParentObject instanceof Resource ? (Resource) edgeParentObject : null;
\r
169 node.setFieldListener(new PropertyChangeListener() {
\r
172 public void propertyChange(final PropertyChangeEvent event) {
\r
173 if (parentElement == null)
\r
176 String field = event.getPropertyName();
\r
177 Map<String, Pair<Resource, Object>> properties = e.getHint(DiagramHints.PROPERTIES);
\r
178 if(properties == null) return;
\r
179 final Pair<Resource, Object> property = properties.get(field);
\r
180 if(property == null) return;
\r
183 Simantics.getSession().syncRequest(new WriteRequest() {
\r
185 public void perform(WriteGraph graph) throws DatabaseException {
\r
186 graph.claimLiteral((Resource) parentElement, property.first, event.getNewValue());
\r
189 } catch (DatabaseException ex) {
\r
190 Activator.getDefault().getLog().log(
\r
191 new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Failed to synchronize property " + field + ".", ex)
\r
200 Shape beginTerminalShape = null;
\r
201 Shape endTerminalShape = null;
\r
202 if (diagram != null) {
\r
203 Topology topology = diagram.getDiagramClass().getAtMostOneItemOfClass(Topology.class);
\r
204 if (topology != null) {
\r
205 Connection beginConnection = topology.getConnection(e, EdgeEnd.Begin);
\r
206 Connection endConnection = topology.getConnection(e, EdgeEnd.End);
\r
207 beginTerminalShape = getCanvasTerminalShape(beginConnection);
\r
208 endTerminalShape = getCanvasTerminalShape(endConnection);
\r
212 if(beginTerminalShape == null || endTerminalShape == null) return;
\r
214 EdgeVisuals vh = e.getElementClass().getSingleItem(EdgeVisuals.class);
\r
215 Map<String, Pair<Resource, Object>> properties = e.getHint(DiagramHints.PROPERTIES);
\r
216 Pair<Resource, Object> strokeWidthPair = properties.get("strokeWidth");
\r
219 if(strokeWidthPair == null)
\r
220 strokeWidth = DEFAULT_STROKE_WIDTH;
\r
222 strokeWidth = (Float)strokeWidthPair.second;
\r
224 vh.setStroke(e, new BasicStroke(strokeWidth,
\r
225 BasicStroke.CAP_BUTT,
\r
226 BasicStroke.JOIN_MITER,
\r
227 10.0f, null, 0.0f));
\r
228 Stroke stroke = vh.getStroke(e);
\r
229 Font font = ElementUtils.getTextFont(e);
\r
230 Color color = ElementUtils.getTextColor(e);
\r
231 // Color fillColor = ElementUtils.getFillColor(e);
\r
232 Color borderColor = ElementUtils.getBorderColor(e, Color.BLACK);
\r
233 // String text = ElementUtils.getText(e);
\r
234 Alignment hAlign = ElementUtils.getHintOrDefault(e, ElementHints.KEY_HORIZONTAL_ALIGN, Alignment.CENTER);
\r
235 node.setBackgroundColor(null);
\r
236 node.setBorderColor(borderColor);
\r
237 node.setHorizontalAlignment((byte) hAlign.ordinal());
\r
238 node.setPadding(0, 0);
\r
239 node.setBorderWidth((float) 0);
\r
240 node.setEditable(false);
\r
241 node.setFont(font);
\r
243 node.setBeginBounds(beginTerminalShape);
\r
244 node.setEndBounds(endTerminalShape);
\r
245 node.setStroke(stroke);
\r
246 node.setColor(color);
\r
247 node.setShapes(DependencyRouter.createArrowShape(node.getShapes(), node.getBeginBounds(), node.getEndBounds(), node.getAngle(), node.getStroke()));
\r
249 if(properties != null) {
\r
250 for(Map.Entry<String, Pair<Resource, Object>> entry : properties.entrySet()) {
\r
251 NodeUtil.setPropertyIfSupported(entry.getKey(), entry.getValue().second, node);
\r
252 // node.setProperty(entry.getKey(), entry.getValue().second);
\r
253 // System.out.println("setProperty " + entry.getKey() + " => " + entry.getValue().second);
\r
256 EdgeHandler eh = e.getElementClass().getAtMostOneItemOfClass(EdgeHandler.class);
\r
257 Path2D path = eh.getPath(e);
\r
259 path = new Path2D.Double();
\r
262 path.append(node.getShapes().first, false);
\r
263 eh.setPath(e, path);
\r
267 private static Shape getCanvasTerminalShape(Connection connection) {
\r
268 if (connection != null && connection.node != null && connection.terminal != null) {
\r
269 TerminalLayout layout = connection.node.getElementClass().getAtMostOneItemOfClass(TerminalLayout.class);
\r
270 if (layout != null) {
\r
271 //return layout.getTerminalShape(connection.node, connection.terminal);
\r
272 Shape shp = layout.getTerminalShape(connection.node, connection.terminal);
\r
273 Transform tr = connection.node.getElementClass().getAtMostOneItemOfClass(Transform.class);
\r
277 return tr.getTransform(connection.node).createTransformedShape(shp);
\r