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