Dynamic terminals and connections
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / adapters / SymbolCodeStyle.java
1 /*******************************************************************************
2  * Copyright (c) 2017 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     Semantum Oy - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.modeling.adapters;
13
14 import java.awt.geom.AffineTransform;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
20
21 import org.simantics.Simantics;
22 import org.simantics.db.ReadGraph;
23 import org.simantics.db.Resource;
24 import org.simantics.db.exception.DatabaseException;
25 import org.simantics.db.layer0.variable.Variable;
26 import org.simantics.db.layer0.variable.Variables;
27 import org.simantics.diagram.profile.StyleBase;
28 import org.simantics.diagram.stubs.DiagramResource;
29 import org.simantics.scenegraph.INode;
30 import org.simantics.scenegraph.g2d.G2DNodeModification;
31 import org.simantics.scenegraph.g2d.IG2DNode;
32 import org.simantics.scenegraph.g2d.nodes.ConnectionNode;
33 import org.simantics.scenegraph.g2d.nodes.SVGNode;
34 import org.simantics.scenegraph.g2d.nodes.SVGNodeAssignment;
35 import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
36 import org.simantics.scenegraph.g2d.nodes.TargetedSVGNodeAssignment;
37 import org.simantics.scenegraph.g2d.nodes.TransformationAssignment;
38 import org.simantics.scenegraph.g2d.nodes.connection.RouteGraphNode;
39 import org.simantics.scenegraph.profile.EvaluationContext;
40 import org.simantics.scenegraph.utils.NodeUtil;
41 import org.simantics.scl.runtime.function.Function;
42 import org.simantics.structural.stubs.StructuralResource2;
43 import org.simantics.utils.datastructures.Pair;
44
45 /**
46  * @author Antti Villberg
47  * @since 1.29.0
48  */
49 public class SymbolCodeStyle extends StyleBase<Pair<G2DNodeModification, Object>> {
50
51     @Override
52     public Pair<G2DNodeModification, Object> calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration) throws DatabaseException {
53         DiagramResource DIA = DiagramResource.getInstance(graph);
54
55         // Find a component for the element 
56         Variable elementVariable = Variables.getPossibleVariable(graph, element);
57         if (elementVariable == null)
58             return null;
59         Object modi = elementVariable.getPossiblePropertyValue(graph, DIA.symbolCode);
60         if (modi == null) {
61                 if(graph.isInstanceOf(element, DIA.RouteGraphConnection)) {
62                         StructuralResource2 STR = StructuralResource2.getInstance(graph);
63                         Resource connectionType = graph.getPossibleObject(element, STR.HasConnectionType);
64                         if(connectionType != null) {
65                                 Variable connectionTypeVariable = Variables.getPossibleVariable(graph, connectionType);
66                                 Function fn = connectionTypeVariable.getPossiblePropertyValue(graph, DIA.symbolFunction);
67                                 modi = Simantics.applySCLRead(graph, fn, elementVariable);
68                         }
69                 }
70         }
71         
72         if(modi == null) return null;
73
74         // If element is moved, recalculate style
75         Object transform = graph.getPossibleRelatedValue(element, DIA.HasTransform);
76
77         if (modi instanceof G2DNodeModification) {
78             return Pair.make((G2DNodeModification)modi, transform);
79         } else if (modi instanceof List) {
80             @SuppressWarnings("unchecked")
81             List<String> styles = (List<String>)modi;
82             List<SVGNodeAssignment> assignments = new ArrayList<>(styles.size()/3);
83             for (int i = 0; i < styles.size()/3; i++)
84                 assignments.add(new SVGNodeAssignment(styles.get(3*i), styles.get(3*i+1), styles.get(3*i+2)));
85             return Pair.make(new G2DNodeModification(assignments, Collections.emptyList()), transform);
86         } else {
87             throw new DatabaseException("Invalid symbolCode value: " + modi);
88         }
89     }
90
91     private Map<Object,SingleElementNode> buildSingleElementMap(INode node) {
92         Map<Object,SingleElementNode> elements = new HashMap<>();
93         NodeUtil.forChildrenDeep(node, SingleElementNode.class, n -> {
94             elements.put(n.getKey(), n);
95             return null;
96         });
97         return elements;
98     }
99     
100     @Override
101     public void applyStyleForNode(EvaluationContext evaluationContext, INode node, Pair<G2DNodeModification, Object> result) {
102
103         if (result == null || result.first == null)
104             return;
105
106         Map<Object,SingleElementNode> elements = null;
107         
108         G2DNodeModification modification = result.first;
109         
110         if(node instanceof ConnectionNode) {
111                 
112                 if (modification.svgAssignments != null && !modification.svgAssignments.isEmpty()) {
113                         INode child = NodeUtil.getFirstChild(node);
114                         if(child instanceof RouteGraphNode) {
115                                 RouteGraphNode rgn = (RouteGraphNode)child; 
116                                 rgn.setAssignments(modification.svgAssignments);
117                         }
118                 }
119                 
120         } else if(node instanceof SingleElementNode) {
121
122                 Map<SVGNode, List<SVGNodeAssignment>> assignmentMap = new HashMap<>();
123                 
124             if (modification.svgAssignments != null && !modification.svgAssignments.isEmpty()) {
125                 for (SVGNode p : NodeUtil.collectNodes(node, SVGNode.class)) {
126                         List<SVGNodeAssignment> list = assignmentMap.get(p);
127                         if(list == null) {
128                                 list = new ArrayList<>();
129                                 assignmentMap.put(p, list);
130                         }
131                         list.addAll(modification.svgAssignments);
132                 }
133             }
134
135                 if(modification.targetedSVGAssignments != null && !modification.targetedSVGAssignments.isEmpty()) {
136                         elements = buildSingleElementMap(node);
137                         for(TargetedSVGNodeAssignment ass : modification.targetedSVGAssignments) {
138                                 SingleElementNode sen = elements.get(ass.singleElementKey);
139                     for (SVGNode p : NodeUtil.collectNodes(sen, SVGNode.class)) {
140                         List<SVGNodeAssignment> list = assignmentMap.get(p);
141                         if(list == null) {
142                                 list = new ArrayList<>();
143                                 assignmentMap.put(p, list);
144                         }
145                         list.add(ass);
146                     }
147                         }
148                 }
149
150                 for(Map.Entry<SVGNode, List<SVGNodeAssignment>> entry : assignmentMap.entrySet()) {
151                         SVGNode p = entry.getKey();
152                 p.setAssignments(entry.getValue());
153                 p.cleanDiagramCache();
154                 }
155
156             if (modification.transformAssignments != null) {
157                 Map<Object,AffineTransform> trs = new HashMap<>();
158                 for (TransformationAssignment ass : modification.transformAssignments) 
159                     trs.put(ass.key, ass.transform);
160                 NodeUtil.forChildrenDeep(node, SingleElementNode.class, n -> {
161                     Object key = n.getKey();
162                     AffineTransform tr = trs.get(key);
163                     if (tr != null) {
164                         IG2DNode[] children = n.getSortedNodes();
165                         if (children.length > 0)
166                             children[0].setTransform(tr);
167                     }
168                     return null;
169                 });
170             }
171                 
172         }
173         
174     }
175
176 }