]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/profile/TextGridStyle.java
Preliminary implementation to update only changed profile entries
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / profile / TextGridStyle.java
1 package org.simantics.diagram.profile;
2
3 import java.awt.Color;
4 import java.awt.Font;
5 import java.awt.geom.AffineTransform;
6 import java.awt.geom.Point2D;
7 import java.awt.geom.Rectangle2D;
8 import java.util.Comparator;
9 import java.util.HashMap;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.stream.Collectors;
13
14 import org.simantics.databoard.Bindings;
15 import org.simantics.datatypes.literal.Vec2d;
16 import org.simantics.db.ReadGraph;
17 import org.simantics.db.Resource;
18 import org.simantics.db.exception.DatabaseException;
19 import org.simantics.db.layer0.variable.RVI;
20 import org.simantics.db.layer0.variable.Variable;
21 import org.simantics.diagram.elements.ITextListener;
22 import org.simantics.diagram.elements.TextGridNode;
23 import org.simantics.diagram.elements.TextNode;
24 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
25 import org.simantics.g2d.utils.Alignment;
26 import org.simantics.modeling.ModelingResources;
27 import org.simantics.scenegraph.INode;
28 import org.simantics.scenegraph.g2d.nodes.ConnectionNode;
29 import org.simantics.scenegraph.profile.EvaluationContext;
30 import org.simantics.scenegraph.profile.common.ProfileVariables;
31 import org.simantics.scenegraph.utils.GeometryUtils;
32 import org.simantics.scenegraph.utils.NodeUtil;
33 import org.simantics.scl.runtime.function.Function1;
34 import org.simantics.ui.colors.Colors;
35 import org.simantics.utils.datastructures.Pair;
36
37 /**
38  * @author Antti Villberg
39  * @author Tuukka Lehtonen
40  */
41 public abstract class TextGridStyle extends StyleBase<MonitorTextGridResult> {
42
43         private final Font  FONT             = Font.decode("Arial 12");
44         private final Color BACKGROUND_COLOR = new Color(255, 255, 255, 192);
45         private static final Rectangle2D EMPTY_BOUNDS = new Rectangle2D.Double(0, 0, 0, 0);
46
47         protected double xOffset;
48         protected double yOffset;
49
50         public TextGridStyle(Resource r) {
51                 this(r, 0.0, 2.1);
52         }
53
54         public TextGridStyle(Resource r, double xOffset, double yOffset) {
55             super(r);
56                 this.xOffset = xOffset;
57                 this.yOffset = yOffset;
58         }
59
60         public Resource getPropertyRelation(ReadGraph g, Resource module) {
61         throw new Error("Fix this");
62         }
63
64         /**
65          * @return the name of the scene graph node to create to represent the text
66          *         element created by this style
67          */
68         public String getNodeName() {
69                 return getClass().getSimpleName();
70         }
71
72         /**
73          * Override to customize.
74          * 
75          * @param graph
76          * @param element
77          * @return
78          * @throws DatabaseException
79          */
80         protected Resource getConfigurationComponent(ReadGraph graph, Resource element) throws DatabaseException {
81                 ModelingResources mr = ModelingResources.getInstance(graph);
82                 Resource config = graph.getPossibleObject(element, mr.ElementToComponent);
83                 return config;
84         }
85
86         /**
87          * Override to customize.
88          * 
89          * @param graph
90          * @param element
91          * @return
92          * @throws DatabaseException
93          */
94         protected String getConfigurationComponentNameForElement(ReadGraph graph, Resource element) throws DatabaseException {
95                 Resource config = getConfigurationComponent(graph, element);
96                 if (config == null)
97                         return null;
98                 String name = graph.getPossibleRelatedValue(config, getPropertyRelation(graph,element), Bindings.STRING);
99                 return name;
100         }
101
102         public AffineTransform getTransform(INode node, AffineTransform parentTransform, Rectangle2D elementBounds, int location, boolean up) {
103                 return getTransform(parentTransform, elementBounds, location, up);
104         }
105
106         public AffineTransform getTransform(AffineTransform parentTransform, Rectangle2D elementBounds, int location, boolean up) {
107
108                 double scale = GeometryUtils.getScale(parentTransform);
109
110                 AffineTransform at = AffineTransform.getTranslateInstance(
111                                 parentTransform.getTranslateX() + elementBounds.getCenterX() * scale + xOffset,
112                                 parentTransform.getTranslateY() + elementBounds.getMinY() * scale + yOffset*(location-1) + (up ? 0.0 : (2.0*yOffset) + elementBounds.getHeight() * scale) 
113                                 );
114                 at.scale(0.15, 0.15);
115
116                 return at;
117
118         }
119
120         protected String rowId() {
121                 return "";
122         }
123
124         @Override
125         public MonitorTextGridResult calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration)
126                         throws DatabaseException {
127                 String name = getConfigurationComponentNameForElement(graph, element);
128                 if (name == null)
129                         return null;
130                 AffineTransform transform = DiagramGraphUtil.getDynamicAffineTransform(graph, runtimeDiagram, element);
131                 Vec2d offset = DiagramGraphUtil.getOffset(graph, element);
132                 boolean enabled = !DiagramGraphUtil.getProfileMonitorsHidden(graph, element);
133                 boolean up = DiagramGraphUtil.getProfileMonitorsUp(graph, element);
134                 double spacing = DiagramGraphUtil.getProfileMonitorSpacing(graph, element);
135                 return new MonitorTextGridResult(rowId(), name, "", "", enabled, up, spacing, null, null, null, transform, offset);
136         }
137
138         // Not to be modified
139         protected final static Point2D[] DEFAULT_CELL_OFFSETS = new Point2D[] {
140                         new Point2D.Double(-45, 0),
141                         new Point2D.Double(22, 0),
142                         new Point2D.Double(24, 0)
143         };
144
145         // Not to be modified
146         protected final static Point2D[] ZERO_CELL_OFFSETS = new Point2D[] {
147                         new Point2D.Double(0, 0),
148                         new Point2D.Double(0, 0),
149                         new Point2D.Double(0, 0)
150         };
151
152         protected Point2D[] getCellOffsets() {
153                 return DEFAULT_CELL_OFFSETS;
154         }
155
156         @Override
157         public void applyStyleForNode(EvaluationContext observer, INode _node, MonitorTextGridResult result) {
158                 String value = result != null ? result.getText1() : null;
159                 boolean enabled = result != null ? result.getEnabled() : false;
160
161                 if (value != null && enabled) {
162
163                         Map<String, Pair<TextGridStyle, MonitorTextGridResult>> rows = observer.getProperty(_node, "rows");
164                         if (rows == null) {
165                             rows = new HashMap<String, Pair<TextGridStyle, MonitorTextGridResult>>();
166                             observer.setProperty(_node, "rows", rows);
167                         }
168                         Pair<TextGridStyle, MonitorTextGridResult> oldResultPair = rows.get(result.getRowId());
169                         if (oldResultPair != null && oldResultPair.first == this && oldResultPair.second.sameStructure(result)) {
170                             return;
171                         }
172                         
173                         rows.put(rowIdKey(), new Pair<TextGridStyle, MonitorTextGridResult>(this, result));
174                         
175                         // FIXME: Improve performance by calling refreshAll only once after all text grid style changes have been applied
176                         refreshAll(observer, _node);
177                 } else {
178                         cleanupStyleForNode(observer, _node);
179                 }
180         }
181
182         private static void refreshAll(EvaluationContext observer, INode _node) {
183             final TextGridNode node = ProfileVariables.claimChild(_node, "", "TextGridStyle", TextGridNode.class, observer);
184         if (node == null)
185             return;
186             
187             
188         int row = 0;
189         Map<String, Pair<TextGridStyle, MonitorTextGridResult>> rows = observer.getProperty(_node, "rows");
190             if (rows != null) {
191             List<Pair<TextGridStyle, MonitorTextGridResult>> sortedRows = rows.values().stream().sorted(new Comparator<Pair<TextGridStyle, MonitorTextGridResult>>() {
192                 @Override
193                 public int compare(Pair<TextGridStyle, MonitorTextGridResult> o1, Pair<TextGridStyle, MonitorTextGridResult> o2) {
194                     return Double.compare(o1.first.getPriority(), o2.first.getPriority());
195                 }
196             }).collect(Collectors.toList());
197             
198                 
199                 for (Pair<TextGridStyle, MonitorTextGridResult> resultPair : sortedRows) {
200                 row++;
201                 TextGridStyle style = resultPair.first;
202                 MonitorTextGridResult result = resultPair.second;
203                 
204                 String value = result != null ? result.getText1() : null;
205     
206                 String value2 = result != null ? result.getText2() : null;
207                 String value3 = result != null ? result.getText3() : null;
208     
209                 double spacing = result.getSpacing();
210     
211                 final Function1<String, String> modifier = result != null ? result.getModifier() : null;
212                 final Function1<String, String> validator = result != null ? result.getValidator() : null;
213                 final Function1<Vec2d, Boolean> translator = result != null ? result.getTranslator() : null;
214                 final RVI rvi = result != null ? result.getRVI() : null;
215     
216                 
217                 node.setRowId(row, result.getRowId());
218         
219                 //setCurrentRowNumber(observer, _node, result.getRowId(), row);
220         
221                 //observer.setTemporaryProperty(_node, "location", row + 1);
222         
223                 node.setText(2, row, value2);
224                 node.setUp(result.getUp());
225         
226                 //MonitorTextGridResult cache = node.getCache(1, row);
227                 //if(cache != null && cache.sameStructure(result)) return;
228         
229                 node.setCache(1, row, result);
230         
231                 boolean isConnection = _node instanceof ConnectionNode;
232         
233                 Rectangle2D elementBounds = isConnection ? EMPTY_BOUNDS : NodeUtil.getLocalElementBounds(_node);
234                 if(elementBounds == null) {
235                     new Exception("Cannot get local element bounds for node " + _node.toString()).printStackTrace();
236                     // This is here for checking why getLocalElementBounds failed in the debugger.
237                     NodeUtil.getLocalElementBounds(_node);
238                     return;
239                 }
240         
241                 //            System.err.println("elementBounds " + elementBounds);
242                 //            System.err.println("parentTransform " + result.getParentTransform());
243         
244                 AffineTransform at = style.getTransform(_node,result.getParentTransform(), elementBounds, row, result.getUp());
245                 Vec2d offset = result.getOffset();
246         
247                 Point2D[] cellOffsets = style.getCellOffsets();
248         
249                 AffineTransform at1 = new AffineTransform(at);
250                 at1.translate(cellOffsets[0].getX(),cellOffsets[0].getY());
251                 AffineTransform at2 = new AffineTransform(at);
252                 at2.translate(cellOffsets[1].getX()+spacing,cellOffsets[1].getY());
253                 AffineTransform at3 = new AffineTransform(at);
254                 at3.translate(cellOffsets[2].getX()+spacing,cellOffsets[2].getY());
255         
256                 at1.translate(offset.x, offset.y);
257                 at2.translate(offset.x, offset.y);
258                 at3.translate(offset.x, offset.y);
259         
260                 node.setTransform(1, row, at1);
261                 node.setTransform(2, row, at2);
262                 node.setTransform(3, row, at3);
263         
264                 Alignment[] alignments = result.getAlignments();
265                 if(alignments != null) {
266                     node.setHorizontalAlignment(1, row, (byte) alignments[0].ordinal());
267                     node.setHorizontalAlignment(2, row, (byte) alignments[1].ordinal());
268                     node.setHorizontalAlignment(3, row, (byte) alignments[2].ordinal());
269                 } else {
270                     node.setHorizontalAlignment(1, row, (byte) style.getAlignment(1).ordinal());
271                     node.setHorizontalAlignment(2, row, (byte) style.getAlignment(2).ordinal());
272                     node.setHorizontalAlignment(3, row, (byte) style.getAlignment(3).ordinal());
273                 }
274         
275                 Alignment[] verticalAlignments = result.getVerticalAlignments();
276                 if(verticalAlignments != null) {
277                     node.setVerticalAlignment(1, row, (byte) verticalAlignments[0].ordinal());
278                     node.setVerticalAlignment(2, row, (byte) verticalAlignments[1].ordinal());
279                     node.setVerticalAlignment(3, row, (byte) verticalAlignments[2].ordinal());
280                 } else {
281                     node.setVerticalAlignment(1, row, (byte) style.getVerticalAlignment(1).ordinal());
282                     node.setVerticalAlignment(2, row, (byte) style.getVerticalAlignment(2).ordinal());
283                     node.setVerticalAlignment(3, row, (byte) style.getVerticalAlignment(3).ordinal());
284                 }
285         
286                 node.setZIndex(3000);
287         
288                 org.simantics.common.color.Color color = result.getColor();
289                 Color awtColor = color != null ? Colors.awt(color) : Color.DARK_GRAY;
290                 Color bgColor = style.getBackgroundColor();
291                 Font font = style.getFont();
292         
293                 style.setTextNodeData(node, 1, row, value, font, awtColor, bgColor);
294                 style.setTextNodeData(node, 2, row, value2, result.getPending(), font, awtColor, bgColor);
295                 style.setTextNodeData(node, 3, row, value3, font, awtColor, bgColor);
296         
297                 node.setEditable(1, row, false);
298                 node.setForceEventListening(2, row, true);
299                 node.setEditable(2, row, modifier != null);
300                 node.setEditable(3, row, false);
301         
302                 final int finalRow = row;
303         
304                 if (modifier != null) {
305                     node.setTextListener(2, row, new ITextListener() {
306                         @Override
307                         public void textChanged() {}
308         
309                         @Override
310                         public void textEditingStarted() {}
311         
312                         @Override
313                         public void textEditingCancelled() {
314                         }
315         
316                         @Override
317                         public void textEditingEnded() {
318         
319                             TextNode t = node.get(2, finalRow);
320                             if (t == null)
321                                 return;
322         
323                             if(!t.getText().equals(t.getTextBeforeEdit()))
324                                 modifier.apply(t.getText());
325         
326                         }
327                     });
328                 } else {
329                     node.setTextListener(2,  row,  null);
330                 }
331         
332                 node.setInputValidator(2, row, validator);
333                 node.setTranslator(translator);
334         
335                 node.setRVI(2, row, rvi);
336         
337                 style.postProcessNode(node, row);
338             }
339             }
340             // remove excess rows
341         int rowCount = node.computeRows();
342         while (row < rowCount) {
343             row++;
344             node.removeRow(row);
345         }
346             
347         }
348         
349         private void setTextNodeData(TextGridNode node, int x, int y, String text, Font font, Color fgColor, Color bgColor) {
350                 if (text != null) {
351                         node.setText(x, y, text);
352                         node.setFont(x, y, font);
353                         node.setColor(x, y, fgColor);
354                         node.setBackgroundColor(x, y, bgColor);
355                 } else {
356                         // Prevent rendering of the node.
357                         node.setFont(x, y, null);
358                 }
359         }
360
361         private void setTextNodeData(TextGridNode node, int x, int y, String text, boolean pending, Font font, Color fgColor, Color bgColor) {
362                 setTextNodeData(node, x, y, text, font, fgColor, bgColor);
363                 node.setPending(x, y, pending);
364         }
365
366         protected Font getFont() {
367                 return FONT;
368         }
369
370         protected Color getBackgroundColor() {
371                 return BACKGROUND_COLOR;
372         }
373
374         protected Alignment getAlignment(int column) {
375                 switch(column) {
376                 case 1: return Alignment.TRAILING;
377                 case 2: return Alignment.TRAILING;
378                 case 3: return Alignment.LEADING;
379                 default: return Alignment.LEADING;
380                 }
381         }
382
383         protected Alignment getVerticalAlignment(int column) {
384                 return Alignment.TRAILING;
385         }
386
387         @Override
388         protected void cleanupStyleForNode(EvaluationContext observer, INode _node) {
389             Map<String, Pair<Double, MonitorTextGridResult>> rows = observer.getProperty(_node, "rows");
390             if (rows != null) {
391                 rows.remove(rowIdKey());
392                 if (rows.isEmpty()) {
393                     observer.setProperty(_node, "rows", null);
394                 }
395             }
396         refreshAll(observer, _node);
397         }
398
399         protected void postProcessNode(TextGridNode node, int row) {
400         }
401
402         private String rowIdKey() {
403                 return "style" + getIdentity().toString();
404         }
405
406 }