1 package org.simantics.modeling.ui.diagram.style;
3 import java.awt.BasicStroke;
7 import java.awt.Stroke;
8 import java.awt.geom.AffineTransform;
9 import java.awt.geom.Path2D;
10 import java.awt.geom.Rectangle2D;
11 import java.util.Collection;
12 import java.util.HashSet;
15 import org.simantics.db.ReadGraph;
16 import org.simantics.db.Resource;
17 import org.simantics.db.common.request.PossibleTypedParent;
18 import org.simantics.db.exception.DatabaseException;
19 import org.simantics.db.layer0.variable.Variable;
20 import org.simantics.diagram.connection.RouteGraph;
21 import org.simantics.diagram.connection.RouteGraphConnectionClass;
22 import org.simantics.diagram.connection.RouteTerminal;
23 import org.simantics.diagram.content.ConnectionUtil;
24 import org.simantics.diagram.handler.Paster;
25 import org.simantics.diagram.handler.Paster.RouteLine;
26 import org.simantics.diagram.profile.ProfileKeys;
27 import org.simantics.diagram.profile.StyleBase;
28 import org.simantics.diagram.stubs.DiagramResource;
29 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
30 import org.simantics.g2d.diagram.IDiagram;
31 import org.simantics.g2d.diagram.handler.DataElementMap;
32 import org.simantics.g2d.element.ElementUtils;
33 import org.simantics.g2d.element.IElement;
34 import org.simantics.g2d.element.handler.SelectionOutline;
35 import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;
36 import org.simantics.modeling.ModelingResources;
37 import org.simantics.scenegraph.INode;
38 import org.simantics.scenegraph.ParentNode;
39 import org.simantics.scenegraph.g2d.IdentityAffineTransform;
40 import org.simantics.scenegraph.g2d.nodes.ConnectionNode;
41 import org.simantics.scenegraph.g2d.nodes.DecorationShapeNode;
42 import org.simantics.scenegraph.g2d.nodes.ShapeNode;
43 import org.simantics.scenegraph.profile.DataNodeMap;
44 import org.simantics.scenegraph.profile.EvaluationContext;
45 import org.simantics.scenegraph.profile.common.ProfileVariables;
46 import org.simantics.scenegraph.utils.GeometryUtils;
47 import org.simantics.scl.runtime.tuple.Tuple5;
48 import org.simantics.structural.stubs.StructuralResource2;
50 import gnu.trove.set.TLongSet;
51 import gnu.trove.set.hash.TLongHashSet;
54 * @author Tuukka Lehtonen
56 public class TypicalInheritanceStyle extends StyleBase<TypicalInheritanceResult> {
58 private static final TypicalInheritanceResult NOT_INHERITED = new TypicalInheritanceResult(Boolean.FALSE, null, null, Boolean.FALSE, null);
60 private static final Paint PAINT = new Color(128, 128, 128, 64);
61 private static final Paint PAINT_WITHOUT_SOURCE = new Color(255, 128, 128, 64);
62 private static final Stroke STROKE = new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
65 public TypicalInheritanceResult calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration) throws DatabaseException {
66 DiagramResource DIA = DiagramResource.getInstance(graph);
67 ModelingResources MOD = ModelingResources.getInstance(graph);
68 StructuralResource2 STR = StructuralResource2.getInstance(graph);
70 boolean templatized = graph.hasStatement(element, MOD.IsTemplatized);
71 boolean hasElementSource = graph.hasStatement(element, MOD.HasElementSource);
73 if (graph.isInstanceOf(element, DIA.RouteGraphConnection)) {
74 Collection<Resource> connectors = graph.getObjects(element, DIA.HasConnector);
75 Collection<Resource> routeNodes = graph.getObjects(element, DIA.HasInteriorRouteNode);
76 TLongHashSet nonTemplatizedConnectors = null;
78 // This is needed to make this query result change every time the underlying element changes visually.
79 Set<Object> identifier = new HashSet<Object>(connectors.size() + routeNodes.size());
81 for (Resource connector : connectors) {
82 for (Resource connectedTo : graph.getObjects(connector, STR.Connects)) {
83 if (!connectedTo.equals(element)) {
84 AffineTransform at = DiagramGraphUtil.getDynamicAffineTransform(graph, runtimeDiagram, connectedTo, DIA.HasDynamicTransform, false);
87 boolean connectedToTemplatized = graph.hasStatement(connectedTo, MOD.IsTemplatized);
88 if (!connectedToTemplatized) {
89 if (nonTemplatizedConnectors == null)
90 nonTemplatizedConnectors = new TLongHashSet();
91 nonTemplatizedConnectors.add(connector.getResourceId());
97 if (!routeNodes.isEmpty()) {
98 for (Resource routeLine : routeNodes) {
99 RouteLine rl = Paster.readRouteLine(graph, routeLine);
103 return new TypicalInheritanceResult(templatized, nonTemplatizedConnectors, IdentityAffineTransform.INSTANCE, hasElementSource, identifier);
104 } else if (graph.isInstanceOf(element, DIA.Monitor)) {
105 AffineTransform worldTransform = DiagramGraphUtil.getWorldTransform(graph, element);
106 Resource monitoredComponent = graph.getPossibleObject(element, DIA.HasMonitorComponent);
108 if (monitoredComponent != null) {
109 Resource monitoredElement = graph.getPossibleObject(monitoredComponent, MOD.ComponentToElement);
110 if (graph.isInstanceOf(monitoredElement, DIA.Connection)) {
111 Resource tailNode = ConnectionUtil.getConnectionTailNode(graph, monitoredElement);
112 if (tailNode != null) {
113 monitoredElement = tailNode;
117 if (monitoredElement != null) {
118 Resource diagram = graph.syncRequest(new PossibleTypedParent(element, DIA.Diagram));
119 if (diagram != null) {
120 Resource monitoredDiagram = graph.syncRequest(new PossibleTypedParent(monitoredElement, DIA.Diagram));
121 if (diagram.equals(monitoredDiagram)) {
122 AffineTransform monitoredElementWorldTransform = DiagramGraphUtil.getWorldTransform(graph, monitoredElement);
123 worldTransform.preConcatenate(monitoredElementWorldTransform);
129 return new TypicalInheritanceResult(templatized, null, worldTransform, hasElementSource, null);
132 AffineTransform worldTransform = DiagramGraphUtil.getDynamicWorldTransform(graph, runtimeDiagram, element);
133 return new TypicalInheritanceResult(templatized, null, worldTransform, hasElementSource, null);
136 return NOT_INHERITED;
139 public void applyStyleForItem(EvaluationContext context, DataNodeMap map, Object item, TypicalInheritanceResult result) {
140 final INode _node = map.getNode(item);
142 if (result != null && Boolean.TRUE.equals(result.isTemplatized())) {
144 Stroke stroke = null;
145 ShapeNode node = null;
147 if (_node instanceof ParentNode<?>) {
148 node = ProfileVariables.claimChild(_node, "", "typical", DecorationShapeNode.class, context);
150 // Ignore, cannot create decoration.
154 if (_node instanceof ConnectionNode) {
160 IDiagram diagram = context.getConstant(ProfileKeys.DIAGRAM);
161 if (diagram != null) {
162 DataElementMap dem = diagram.getDiagramClass().getAtMostOneItemOfClass(DataElementMap.class);
164 IElement element = dem.getElement(diagram, item);
165 if (element != null) {
166 SelectionOutline so = element.getElementClass().getAtMostOneItemOfClass(SelectionOutline.class);
168 RouteGraph rg = element.getHint(RouteGraphConnectionClass.KEY_ROUTEGRAPH);
171 TLongSet nonTemplatizedConnectors = result.getNonTemplatizedConnectors();
172 if (nonTemplatizedConnectors != null) {
174 // Must copy the RouteTerminal to an array before
175 // invoking rgc.remove(RouteTerminal), otherwise
176 // ConcurrentModificationExceptions will arise.
177 Collection<RouteTerminal> rtc = rgc.getTerminals();
178 if (nonTemplatizedConnectors.size() > (rtc.size() - 2)) {
179 // Cannot make a RouteGraph any simpler
180 // than a simple connection between two
182 // Fall back to highlighting the whole
185 RouteTerminal[] rts = rtc.toArray(new RouteTerminal[rtc.size()]);
186 for (RouteTerminal rt : rts) {
187 Object data = rt.getData();
188 if (data instanceof Long) {
189 if (nonTemplatizedConnectors.contains(((Long) data).longValue()))
196 Path2D path = rgc.getPath2D();
197 Stroke connectionStroke = ConnectionSelectionOutline.INSTANCE.resolveStroke(element, ConnectionSelectionOutline.defaultStroke);
198 shape = connectionStroke.createStrokedShape(path);
200 shape = so.getSelectionShape(element);
203 Rectangle2D rect = ElementUtils.getElementBounds(element);
204 shape = GeometryUtils.expandRectangle( rect, 0.5 );
210 AffineTransform at = result.getWorldTransform();
212 node.setTransform(at);
214 node.setZIndex(-1000);
215 node.setColor(result.hasElementSource() ? PAINT : PAINT_WITHOUT_SOURCE);
217 node.setScaleStroke(false);
218 node.setScaleShape(false);
219 node.setStroke(stroke);
220 node.setShape(shape);
222 cleanupStyleForNode(context, _node);
227 protected void cleanupStyleForNode(EvaluationContext context, INode node) {
228 ProfileVariables.denyChild(node, "*", "typical");
229 ProfileVariables.denyChild(node, "", "typical");
234 class TypicalInheritanceResult extends Tuple5 {
235 public TypicalInheritanceResult(Boolean templatized, TLongSet nonTemplatizedConnectors, AffineTransform worldTransform, Boolean hasElementSource, Set<Object> queryIdentifier) {
236 super(templatized, nonTemplatizedConnectors, worldTransform, hasElementSource, queryIdentifier);
238 public boolean isTemplatized() {
239 return (Boolean) get(0);
241 public TLongSet getNonTemplatizedConnectors() {
242 return (TLongSet) get(1);
244 public AffineTransform getWorldTransform() {
245 return (AffineTransform) get(2);
247 public boolean hasElementSource() {
248 return (Boolean) get(3);