+package org.simantics.modeling.ui.diagram.style;\r
+\r
+import java.awt.BasicStroke;\r
+import java.awt.Color;\r
+import java.awt.Paint;\r
+import java.awt.Shape;\r
+import java.awt.Stroke;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Path2D;\r
+import java.awt.geom.Rectangle2D;\r
+import java.util.Collection;\r
+import java.util.HashSet;\r
+import java.util.Set;\r
+\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.common.request.PossibleTypedParent;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.variable.Variable;\r
+import org.simantics.diagram.connection.RouteGraph;\r
+import org.simantics.diagram.connection.RouteGraphConnectionClass;\r
+import org.simantics.diagram.connection.RouteTerminal;\r
+import org.simantics.diagram.content.ConnectionUtil;\r
+import org.simantics.diagram.handler.Paster;\r
+import org.simantics.diagram.handler.Paster.RouteLine;\r
+import org.simantics.diagram.profile.ProfileKeys;\r
+import org.simantics.diagram.profile.StyleBase;\r
+import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
+import org.simantics.g2d.diagram.IDiagram;\r
+import org.simantics.g2d.diagram.handler.DataElementMap;\r
+import org.simantics.g2d.element.ElementUtils;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.g2d.element.handler.SelectionOutline;\r
+import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;\r
+import org.simantics.modeling.ModelingResources;\r
+import org.simantics.scenegraph.INode;\r
+import org.simantics.scenegraph.ParentNode;\r
+import org.simantics.scenegraph.g2d.IdentityAffineTransform;\r
+import org.simantics.scenegraph.g2d.nodes.ConnectionNode;\r
+import org.simantics.scenegraph.g2d.nodes.DecorationShapeNode;\r
+import org.simantics.scenegraph.g2d.nodes.ShapeNode;\r
+import org.simantics.scenegraph.profile.DataNodeMap;\r
+import org.simantics.scenegraph.profile.EvaluationContext;\r
+import org.simantics.scenegraph.profile.common.ProfileVariables;\r
+import org.simantics.scenegraph.utils.GeometryUtils;\r
+import org.simantics.scl.runtime.tuple.Tuple5;\r
+import org.simantics.structural.stubs.StructuralResource2;\r
+\r
+import gnu.trove.set.TLongSet;\r
+import gnu.trove.set.hash.TLongHashSet;\r
+\r
+/**\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class TypicalInheritanceStyle extends StyleBase<TypicalInheritanceResult> {\r
+\r
+ private static final TypicalInheritanceResult NOT_INHERITED = new TypicalInheritanceResult(Boolean.FALSE, null, null, Boolean.FALSE, null);\r
+\r
+ private static final Paint PAINT = new Color(128, 128, 128, 64);\r
+ private static final Paint PAINT_WITHOUT_SOURCE = new Color(255, 128, 128, 64);\r
+ private static final Stroke STROKE = new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);\r
+\r
+ @Override\r
+ public TypicalInheritanceResult calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration) throws DatabaseException {\r
+ DiagramResource DIA = DiagramResource.getInstance(graph); \r
+ ModelingResources MOD = ModelingResources.getInstance(graph);\r
+ StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
+\r
+ boolean templatized = graph.hasStatement(element, MOD.IsTemplatized);\r
+ boolean hasElementSource = graph.hasStatement(element, MOD.HasElementSource);\r
+ if (templatized) {\r
+ if (graph.isInstanceOf(element, DIA.RouteGraphConnection)) {\r
+ Collection<Resource> connectors = graph.getObjects(element, DIA.HasConnector);\r
+ Collection<Resource> routeNodes = graph.getObjects(element, DIA.HasInteriorRouteNode);\r
+ TLongHashSet nonTemplatizedConnectors = null;\r
+\r
+ // This is needed to make this query result change every time the underlying element changes visually.\r
+ Set<Object> identifier = new HashSet<Object>(connectors.size() + routeNodes.size());\r
+\r
+ for (Resource connector : connectors) {\r
+ for (Resource connectedTo : graph.getObjects(connector, STR.Connects)) {\r
+ if (!connectedTo.equals(element)) {\r
+ AffineTransform at = DiagramGraphUtil.getDynamicAffineTransform(graph, runtimeDiagram, connectedTo, DIA.HasDynamicTransform, false);\r
+ identifier.add(at);\r
+\r
+ boolean connectedToTemplatized = graph.hasStatement(connectedTo, MOD.IsTemplatized);\r
+ if (!connectedToTemplatized) {\r
+ if (nonTemplatizedConnectors == null)\r
+ nonTemplatizedConnectors = new TLongHashSet();\r
+ nonTemplatizedConnectors.add(connector.getResourceId());\r
+ }\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ if (!routeNodes.isEmpty()) {\r
+ for (Resource routeLine : routeNodes) {\r
+ RouteLine rl = Paster.readRouteLine(graph, routeLine);\r
+ identifier.add(rl);\r
+ }\r
+ }\r
+ return new TypicalInheritanceResult(templatized, nonTemplatizedConnectors, IdentityAffineTransform.INSTANCE, hasElementSource, identifier);\r
+ } else if (graph.isInstanceOf(element, DIA.Monitor)) {\r
+ AffineTransform worldTransform = DiagramGraphUtil.getWorldTransform(graph, element);\r
+ Resource monitoredComponent = graph.getPossibleObject(element, DIA.HasMonitorComponent);\r
+\r
+ if (monitoredComponent != null) {\r
+ Resource monitoredElement = graph.getPossibleObject(monitoredComponent, MOD.ComponentToElement);\r
+ if (graph.isInstanceOf(monitoredElement, DIA.Connection)) {\r
+ Resource tailNode = ConnectionUtil.getConnectionTailNode(graph, monitoredElement);\r
+ if (tailNode != null) {\r
+ monitoredElement = tailNode;\r
+ }\r
+ }\r
+\r
+ if (monitoredElement != null) {\r
+ Resource diagram = graph.syncRequest(new PossibleTypedParent(element, DIA.Diagram));\r
+ if (diagram != null) {\r
+ Resource monitoredDiagram = graph.syncRequest(new PossibleTypedParent(monitoredElement, DIA.Diagram));\r
+ if (diagram.equals(monitoredDiagram)) {\r
+ AffineTransform monitoredElementWorldTransform = DiagramGraphUtil.getWorldTransform(graph, monitoredElement);\r
+ worldTransform.preConcatenate(monitoredElementWorldTransform);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ return new TypicalInheritanceResult(templatized, null, worldTransform, hasElementSource, null);\r
+ }\r
+\r
+ AffineTransform worldTransform = DiagramGraphUtil.getWorldTransform(graph, element);\r
+ return new TypicalInheritanceResult(templatized, null, worldTransform, hasElementSource, null);\r
+ }\r
+\r
+ return NOT_INHERITED;\r
+ }\r
+\r
+ public void applyStyleForItem(EvaluationContext context, DataNodeMap map, Object item, TypicalInheritanceResult result) {\r
+ final INode _node = map.getNode(item);\r
+\r
+ if (result != null && Boolean.TRUE.equals(result.isTemplatized())) {\r
+ boolean fill = true;\r
+ Stroke stroke = null;\r
+ ShapeNode node = null;\r
+\r
+ if (_node instanceof ParentNode<?>) {\r
+ node = ProfileVariables.claimChild(_node, "", "typical", DecorationShapeNode.class, context);\r
+ } else {\r
+ // Ignore, cannot create decoration.\r
+ return;\r
+ }\r
+\r
+ if (_node instanceof ConnectionNode) {\r
+ fill = false;\r
+ stroke = STROKE;\r
+ }\r
+\r
+ Shape shape = null;\r
+ IDiagram diagram = context.getConstant(ProfileKeys.DIAGRAM);\r
+ if (diagram != null) {\r
+ DataElementMap dem = diagram.getDiagramClass().getAtMostOneItemOfClass(DataElementMap.class);\r
+ if (dem != null) {\r
+ IElement element = dem.getElement(diagram, item);\r
+ if (element != null) {\r
+ SelectionOutline so = element.getElementClass().getAtMostOneItemOfClass(SelectionOutline.class);\r
+ if (so != null) {\r
+ RouteGraph rg = element.getHint(RouteGraphConnectionClass.KEY_ROUTEGRAPH);\r
+ if (rg != null) {\r
+ RouteGraph rgc = rg;\r
+ TLongSet nonTemplatizedConnectors = result.getNonTemplatizedConnectors();\r
+ if (nonTemplatizedConnectors != null) {\r
+ rgc = rg.copy();\r
+ // Must copy the RouteTerminal to an array before\r
+ // invoking rgc.remove(RouteTerminal), otherwise\r
+ // ConcurrentModificationExceptions will arise.\r
+ Collection<RouteTerminal> rtc = rgc.getTerminals();\r
+ if (nonTemplatizedConnectors.size() > (rtc.size() - 2)) {\r
+ // Cannot make a RouteGraph any simpler\r
+ // than a simple connection between two\r
+ // terminals.\r
+ // Fall back to highlighting the whole\r
+ // connection.\r
+ } else {\r
+ RouteTerminal[] rts = rtc.toArray(new RouteTerminal[rtc.size()]);\r
+ for (RouteTerminal rt : rts) {\r
+ Object data = rt.getData();\r
+ if (data instanceof Long) {\r
+ if (nonTemplatizedConnectors.contains(((Long) data).longValue()))\r
+ rgc.remove(rt);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ Path2D path = rgc.getPath2D();\r
+ Stroke connectionStroke = ConnectionSelectionOutline.INSTANCE.resolveStroke(element, ConnectionSelectionOutline.defaultStroke);\r
+ shape = connectionStroke.createStrokedShape(path);\r
+ } else {\r
+ shape = so.getSelectionShape(element);\r
+ }\r
+ } else {\r
+ Rectangle2D rect = ElementUtils.getElementBounds(element);\r
+ shape = GeometryUtils.expandRectangle( rect, 0.5 );\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ AffineTransform at = result.getWorldTransform();\r
+ if (at != null)\r
+ node.setTransform(at);\r
+\r
+ node.setZIndex(-1000);\r
+ node.setColor(result.hasElementSource() ? PAINT : PAINT_WITHOUT_SOURCE);\r
+ node.setFill(fill);\r
+ node.setScaleStroke(false);\r
+ node.setScaleShape(false);\r
+ node.setStroke(stroke);\r
+ node.setShape(shape);\r
+ } else {\r
+ cleanupStyleForNode(context, _node);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ protected void cleanupStyleForNode(EvaluationContext context, INode node) {\r
+ ProfileVariables.denyChild(node, "*", "typical");\r
+ ProfileVariables.denyChild(node, "", "typical");\r
+ }\r
+\r
+}\r
+\r
+class TypicalInheritanceResult extends Tuple5 {\r
+ public TypicalInheritanceResult(Boolean templatized, TLongSet nonTemplatizedConnectors, AffineTransform worldTransform, Boolean hasElementSource, Set<Object> queryIdentifier) {\r
+ super(templatized, nonTemplatizedConnectors, worldTransform, hasElementSource, queryIdentifier);\r
+ }\r
+ public boolean isTemplatized() {\r
+ return (Boolean) get(0);\r
+ }\r
+ public TLongSet getNonTemplatizedConnectors() {\r
+ return (TLongSet) get(1);\r
+ }\r
+ public AffineTransform getWorldTransform() {\r
+ return (AffineTransform) get(2);\r
+ }\r
+ public boolean hasElementSource() {\r
+ return (Boolean) get(3);\r
+ }\r
+}\r