1 package org.simantics.diagram.profile;
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;
12 import java.util.stream.Collectors;
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;
38 * @author Antti Villberg
39 * @author Tuukka Lehtonen
41 public abstract class TextGridStyle extends StyleBase<MonitorTextGridResult> {
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);
47 protected double xOffset;
48 protected double yOffset;
50 public TextGridStyle(Resource r) {
54 public TextGridStyle(Resource r, double xOffset, double yOffset) {
56 this.xOffset = xOffset;
57 this.yOffset = yOffset;
60 public Resource getPropertyRelation(ReadGraph g, Resource module) {
61 throw new Error("Fix this");
65 * @return the name of the scene graph node to create to represent the text
66 * element created by this style
68 public String getNodeName() {
69 return getClass().getSimpleName();
73 * Override to customize.
78 * @throws DatabaseException
80 protected Resource getConfigurationComponent(ReadGraph graph, Resource element) throws DatabaseException {
81 ModelingResources mr = ModelingResources.getInstance(graph);
82 Resource config = graph.getPossibleObject(element, mr.ElementToComponent);
87 * Override to customize.
92 * @throws DatabaseException
94 protected String getConfigurationComponentNameForElement(ReadGraph graph, Resource element) throws DatabaseException {
95 Resource config = getConfigurationComponent(graph, element);
98 String name = graph.getPossibleRelatedValue(config, getPropertyRelation(graph,element), Bindings.STRING);
102 public AffineTransform getTransform(INode node, AffineTransform parentTransform, Rectangle2D elementBounds, int location, boolean up) {
103 return getTransform(parentTransform, elementBounds, location, up);
106 public AffineTransform getTransform(AffineTransform parentTransform, Rectangle2D elementBounds, int location, boolean up) {
108 double scale = GeometryUtils.getScale(parentTransform);
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)
114 at.scale(0.15, 0.15);
120 protected String rowId() {
125 public MonitorTextGridResult calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration)
126 throws DatabaseException {
127 String name = getConfigurationComponentNameForElement(graph, element);
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);
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)
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)
152 protected Point2D[] getCellOffsets() {
153 return DEFAULT_CELL_OFFSETS;
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;
161 if (value != null && enabled) {
163 Map<String, Pair<TextGridStyle, MonitorTextGridResult>> rows = observer.getProperty(_node, "rows");
165 rows = new HashMap<String, Pair<TextGridStyle, MonitorTextGridResult>>();
166 observer.setProperty(_node, "rows", rows);
168 Pair<TextGridStyle, MonitorTextGridResult> oldResultPair = rows.get(result.getRowId());
169 if (oldResultPair != null && oldResultPair.first == this && oldResultPair.second.sameStructure(result)) {
173 rows.put(rowIdKey(), new Pair<TextGridStyle, MonitorTextGridResult>(this, result));
175 // FIXME: Improve performance by calling refreshAll only once after all text grid style changes have been applied
176 refreshAll(observer, _node);
178 cleanupStyleForNode(observer, _node);
182 private static void refreshAll(EvaluationContext observer, INode _node) {
183 final TextGridNode node = ProfileVariables.claimChild(_node, "", "TextGridStyle", TextGridNode.class, observer);
189 Map<String, Pair<TextGridStyle, MonitorTextGridResult>> rows = observer.getProperty(_node, "rows");
191 List<Pair<TextGridStyle, MonitorTextGridResult>> sortedRows = rows.values().stream().sorted(new Comparator<Pair<TextGridStyle, MonitorTextGridResult>>() {
193 public int compare(Pair<TextGridStyle, MonitorTextGridResult> o1, Pair<TextGridStyle, MonitorTextGridResult> o2) {
194 return Double.compare(o1.first.getPriority(), o2.first.getPriority());
196 }).collect(Collectors.toList());
199 for (Pair<TextGridStyle, MonitorTextGridResult> resultPair : sortedRows) {
201 TextGridStyle style = resultPair.first;
202 MonitorTextGridResult result = resultPair.second;
204 String value = result != null ? result.getText1() : null;
206 String value2 = result != null ? result.getText2() : null;
207 String value3 = result != null ? result.getText3() : null;
209 double spacing = result.getSpacing();
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;
217 node.setRowId(row, result.getRowId());
219 //setCurrentRowNumber(observer, _node, result.getRowId(), row);
221 //observer.setTemporaryProperty(_node, "location", row + 1);
223 node.setText(2, row, value2);
224 node.setUp(result.getUp());
226 //MonitorTextGridResult cache = node.getCache(1, row);
227 //if(cache != null && cache.sameStructure(result)) return;
229 node.setCache(1, row, result);
231 boolean isConnection = _node instanceof ConnectionNode;
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);
241 // System.err.println("elementBounds " + elementBounds);
242 // System.err.println("parentTransform " + result.getParentTransform());
244 AffineTransform at = style.getTransform(_node,result.getParentTransform(), elementBounds, row, result.getUp());
245 Vec2d offset = result.getOffset();
247 Point2D[] cellOffsets = style.getCellOffsets();
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());
256 at1.translate(offset.x, offset.y);
257 at2.translate(offset.x, offset.y);
258 at3.translate(offset.x, offset.y);
260 node.setTransform(1, row, at1);
261 node.setTransform(2, row, at2);
262 node.setTransform(3, row, at3);
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());
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());
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());
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());
286 node.setZIndex(3000);
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();
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);
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);
302 final int finalRow = row;
304 if (modifier != null) {
305 node.setTextListener(2, row, new ITextListener() {
307 public void textChanged() {}
310 public void textEditingStarted() {}
313 public void textEditingCancelled() {
317 public void textEditingEnded() {
319 TextNode t = node.get(2, finalRow);
323 if(!t.getText().equals(t.getTextBeforeEdit()))
324 modifier.apply(t.getText());
329 node.setTextListener(2, row, null);
332 node.setInputValidator(2, row, validator);
333 node.setTranslator(translator);
335 node.setRVI(2, row, rvi);
337 style.postProcessNode(node, row);
340 // remove excess rows
341 int rowCount = node.computeRows();
342 while (row < rowCount) {
349 private void setTextNodeData(TextGridNode node, int x, int y, String text, Font font, Color fgColor, Color bgColor) {
351 node.setText(x, y, text);
352 node.setFont(x, y, font);
353 node.setColor(x, y, fgColor);
354 node.setBackgroundColor(x, y, bgColor);
356 // Prevent rendering of the node.
357 node.setFont(x, y, null);
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);
366 protected Font getFont() {
370 protected Color getBackgroundColor() {
371 return BACKGROUND_COLOR;
374 protected Alignment getAlignment(int column) {
376 case 1: return Alignment.TRAILING;
377 case 2: return Alignment.TRAILING;
378 case 3: return Alignment.LEADING;
379 default: return Alignment.LEADING;
383 protected Alignment getVerticalAlignment(int column) {
384 return Alignment.TRAILING;
388 protected void cleanupStyleForNode(EvaluationContext observer, INode _node) {
389 Map<String, Pair<Double, MonitorTextGridResult>> rows = observer.getProperty(_node, "rows");
391 rows.remove(rowIdKey());
392 if (rows.isEmpty()) {
393 observer.setProperty(_node, "rows", null);
396 refreshAll(observer, _node);
399 protected void postProcessNode(TextGridNode node, int row) {
402 private String rowIdKey() {
403 return "style" + getIdentity().toString();