]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/style/TypicalInheritanceStyle.java
Moved RouteGraphConnectionClass to org.simantics.g2d.elementclass
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / diagram / style / TypicalInheritanceStyle.java
1 package org.simantics.modeling.ui.diagram.style;
2
3 import java.awt.BasicStroke;
4 import java.awt.Color;
5 import java.awt.Paint;
6 import java.awt.Shape;
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;
13 import java.util.Set;
14
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.RouteTerminal;
22 import org.simantics.diagram.content.ConnectionUtil;
23 import org.simantics.diagram.handler.Paster;
24 import org.simantics.diagram.handler.Paster.RouteLine;
25 import org.simantics.diagram.profile.ProfileKeys;
26 import org.simantics.diagram.profile.StyleBase;
27 import org.simantics.diagram.stubs.DiagramResource;
28 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
29 import org.simantics.g2d.diagram.IDiagram;
30 import org.simantics.g2d.diagram.handler.DataElementMap;
31 import org.simantics.g2d.element.ElementUtils;
32 import org.simantics.g2d.element.IElement;
33 import org.simantics.g2d.element.handler.SelectionOutline;
34 import org.simantics.g2d.element.handler.impl.ConnectionSelectionOutline;
35 import org.simantics.g2d.elementclass.RouteGraphConnectionClass;
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;
49
50 import gnu.trove.set.TLongSet;
51 import gnu.trove.set.hash.TLongHashSet;
52
53 /**
54  * @author Tuukka Lehtonen
55  */
56 public class TypicalInheritanceStyle extends StyleBase<TypicalInheritanceResult> {
57
58     private static final TypicalInheritanceResult NOT_INHERITED = new TypicalInheritanceResult(Boolean.FALSE, null, null, Boolean.FALSE, null);
59
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);
63
64     @Override
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);
69
70         boolean templatized = graph.hasStatement(element, MOD.IsTemplatized);
71         boolean hasElementSource = graph.hasStatement(element, MOD.HasElementSource);
72         if (templatized) {
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;
77
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());
80
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);
85                             identifier.add(at);
86
87                             boolean connectedToTemplatized = graph.hasStatement(connectedTo, MOD.IsTemplatized);
88                             if (!connectedToTemplatized) {
89                                 if (nonTemplatizedConnectors == null)
90                                     nonTemplatizedConnectors = new TLongHashSet();
91                                 nonTemplatizedConnectors.add(connector.getResourceId());
92                             }
93                             break;
94                         }
95                     }
96                 }
97                 if (!routeNodes.isEmpty()) {
98                     for (Resource routeLine : routeNodes) {
99                         RouteLine rl = Paster.readRouteLine(graph, routeLine);
100                         identifier.add(rl);
101                     }
102                 }
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);
107
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;
114                         }
115                     }
116
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);
124                             }
125                         }
126                     }
127                 }
128
129                 return new TypicalInheritanceResult(templatized, null, worldTransform, hasElementSource, null);
130             }
131
132             AffineTransform worldTransform = DiagramGraphUtil.getDynamicWorldTransform(graph, runtimeDiagram, element);
133             return new TypicalInheritanceResult(templatized, null, worldTransform, hasElementSource, null);
134         }
135
136         return NOT_INHERITED;
137     }
138
139     public void applyStyleForItem(EvaluationContext context, DataNodeMap map, Object item, TypicalInheritanceResult result) {
140         final INode _node = map.getNode(item);
141
142         if (result != null && Boolean.TRUE.equals(result.isTemplatized())) {
143             boolean fill = true;
144             Stroke stroke = null;
145             ShapeNode node = null;
146
147             if (_node instanceof ParentNode<?>) {
148                 node = ProfileVariables.claimChild(_node, "", "typical", DecorationShapeNode.class, context); //$NON-NLS-1$ //$NON-NLS-2$
149             } else {
150                 // Ignore, cannot create decoration.
151                 return;
152             }
153
154             if (_node instanceof ConnectionNode) {
155                 fill = false;
156                 stroke = STROKE;
157             }
158
159             Shape shape = null;
160             IDiagram diagram = context.getConstant(ProfileKeys.DIAGRAM);
161             if (diagram != null) {
162                 DataElementMap dem = diagram.getDiagramClass().getAtMostOneItemOfClass(DataElementMap.class);
163                 if (dem != null) {
164                     IElement element = dem.getElement(diagram, item);
165                     if (element != null) {
166                         SelectionOutline so = element.getElementClass().getAtMostOneItemOfClass(SelectionOutline.class);
167                         if (so != null) {
168                             RouteGraph rg = element.getHint(RouteGraphConnectionClass.KEY_ROUTEGRAPH);
169                             if (rg != null) {
170                                 RouteGraph rgc = rg;
171                                 TLongSet nonTemplatizedConnectors = result.getNonTemplatizedConnectors();
172                                 if (nonTemplatizedConnectors != null) {
173                                     rgc = rg.copy();
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
181                                         // terminals.
182                                         // Fall back to highlighting the whole
183                                         // connection.
184                                     } else {
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()))
190                                                     rgc.remove(rt);
191                                             }
192                                         }
193                                     }
194                                 }
195
196                                 Path2D path = rgc.getPath2D();
197                                 Stroke connectionStroke = ConnectionSelectionOutline.INSTANCE.resolveStroke(element, ConnectionSelectionOutline.defaultStroke);
198                                 shape = connectionStroke.createStrokedShape(path);
199                             } else {
200                                 shape = so.getSelectionShape(element);
201                             }
202                         } else {
203                             Rectangle2D rect = ElementUtils.getElementBounds(element);
204                             shape = GeometryUtils.expandRectangle( rect, 0.5 );
205                         }
206                     }
207                 }
208             }
209
210             AffineTransform at = result.getWorldTransform();
211             if (at != null)
212                 node.setTransform(at);
213
214             node.setZIndex(-1000);
215             node.setColor(result.hasElementSource() ? PAINT : PAINT_WITHOUT_SOURCE);
216             node.setFill(fill);
217             node.setScaleStroke(false);
218             node.setScaleShape(false);
219             node.setStroke(stroke);
220             node.setShape(shape);
221         } else {
222             cleanupStyleForNode(context, _node);
223         }
224     }
225
226     @Override
227     protected void cleanupStyleForNode(EvaluationContext context, INode node) {
228         ProfileVariables.denyChild(node, "*", "typical"); //$NON-NLS-1$ //$NON-NLS-2$
229         ProfileVariables.denyChild(node, "", "typical"); //$NON-NLS-1$ //$NON-NLS-2$
230     }
231
232 }
233
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);
237     }
238     public boolean isTemplatized() {
239         return (Boolean) get(0);
240     }
241     public TLongSet getNonTemplatizedConnectors() {
242         return (TLongSet) get(1);
243     }
244     public AffineTransform getWorldTransform() {
245         return (AffineTransform) get(2);
246     }
247     public boolean hasElementSource() {
248         return (Boolean) get(3);
249     }
250 }