]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram/src/org/simantics/diagram/profile/TextGridStyle.java
Fix diagram profiles to work with latest DB changes
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / profile / TextGridStyle.java
index 6657d5a60eca5856f3785cb4f77f07197dfa5c5a..997ace1d63f12a87db7fc6fcaaec5f630fc92be3 100644 (file)
-package org.simantics.diagram.profile;\r
-\r
-import java.awt.Color;\r
-import java.awt.Font;\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Point2D;\r
-import java.awt.geom.Rectangle2D;\r
-\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.datatypes.literal.Vec2d;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.layer0.variable.RVI;\r
-import org.simantics.db.layer0.variable.Variable;\r
-import org.simantics.diagram.elements.ITextListener;\r
-import org.simantics.diagram.elements.TextGridNode;\r
-import org.simantics.diagram.elements.TextNode;\r
-import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
-import org.simantics.g2d.utils.Alignment;\r
-import org.simantics.modeling.ModelingResources;\r
-import org.simantics.scenegraph.INode;\r
-import org.simantics.scenegraph.g2d.nodes.ConnectionNode;\r
-import org.simantics.scenegraph.profile.EvaluationContext;\r
-import org.simantics.scenegraph.profile.common.ProfileVariables;\r
-import org.simantics.scenegraph.utils.GeometryUtils;\r
-import org.simantics.scenegraph.utils.NodeUtil;\r
-import org.simantics.scl.runtime.function.Function1;\r
-import org.simantics.ui.colors.Colors;\r
-\r
-/**\r
- * @author Antti Villberg\r
- * @author Tuukka Lehtonen\r
- */\r
-public abstract class TextGridStyle extends StyleBase<MonitorTextGridResult> {\r
-\r
-       private final Font  FONT             = Font.decode("Arial 12");\r
-       private final Color BACKGROUND_COLOR = new Color(255, 255, 255, 192);\r
-       private static final Rectangle2D EMPTY_BOUNDS = new Rectangle2D.Double(0, 0, 0, 0);\r
-\r
-       // NOTE: this is a hack\r
-       String id;\r
-\r
-       protected double xOffset;\r
-       protected double yOffset;\r
-\r
-       public TextGridStyle() {\r
-               this(0.0, 2.1);\r
-       }\r
-\r
-       public TextGridStyle(double xOffset, double yOffset) {\r
-               this.xOffset = xOffset;\r
-               this.yOffset = yOffset;\r
-       }\r
-\r
-       public Resource getPropertyRelation(ReadGraph g, Resource module) {\r
-       throw new Error("Fix this");\r
-       }\r
-\r
-       /**\r
-        * @return the name of the scene graph node to create to represent the text\r
-        *         element created by this style\r
-        */\r
-       public String getNodeName() {\r
-               return getClass().getSimpleName();\r
-       }\r
-\r
-       /**\r
-        * Override to customize.\r
-        * \r
-        * @param graph\r
-        * @param element\r
-        * @return\r
-        * @throws DatabaseException\r
-        */\r
-       protected Resource getConfigurationComponent(ReadGraph graph, Resource element) throws DatabaseException {\r
-               ModelingResources mr = ModelingResources.getInstance(graph);\r
-               Resource config = graph.getPossibleObject(element, mr.ElementToComponent);\r
-               return config;\r
-       }\r
-\r
-       /**\r
-        * Override to customize.\r
-        * \r
-        * @param graph\r
-        * @param element\r
-        * @return\r
-        * @throws DatabaseException\r
-        */\r
-       protected String getConfigurationComponentNameForElement(ReadGraph graph, Resource element) throws DatabaseException {\r
-               Resource config = getConfigurationComponent(graph, element);\r
-               if (config == null)\r
-                       return null;\r
-               String name = graph.getPossibleRelatedValue(config, getPropertyRelation(graph,element), Bindings.STRING);\r
-               return name;\r
-       }\r
-       \r
-       public AffineTransform getTransform(INode node, AffineTransform parentTransform, Rectangle2D elementBounds, int location, boolean up) {\r
-               return getTransform(parentTransform, elementBounds, location, up);\r
-       }\r
-\r
-       public AffineTransform getTransform(AffineTransform parentTransform, Rectangle2D elementBounds, int location, boolean up) {\r
-\r
-               double scale = GeometryUtils.getScale(parentTransform);\r
-\r
-               AffineTransform at = AffineTransform.getTranslateInstance(\r
-                               parentTransform.getTranslateX() + elementBounds.getCenterX() * scale + xOffset,\r
-                               parentTransform.getTranslateY() + elementBounds.getMinY() * scale + yOffset*(location-1) + (up ? 0.0 : (2.0*yOffset) + elementBounds.getHeight() * scale) \r
-                               );\r
-               at.scale(0.15, 0.15);\r
-\r
-               return at;\r
-               \r
-       }\r
-\r
-       protected String rowId() {\r
-               return "";\r
-       }\r
-\r
-       @Override\r
-       public MonitorTextGridResult calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration)\r
-                       throws DatabaseException {\r
-               String name = getConfigurationComponentNameForElement(graph, element);\r
-               if (name == null)\r
-                       return null;\r
-               AffineTransform transform = DiagramGraphUtil.getAffineTransform(graph, element);\r
-               Vec2d offset = DiagramGraphUtil.getOffset(graph, element);\r
-               boolean enabled = !DiagramGraphUtil.getProfileMonitorsHidden(graph, element);\r
-               boolean up = DiagramGraphUtil.getProfileMonitorsUp(graph, element);\r
-               double spacing = DiagramGraphUtil.getProfileMonitorSpacing(graph, element);\r
-               return new MonitorTextGridResult(rowId(), name, "", "", enabled, up, spacing, null, null, null, transform, offset);\r
-       }\r
-\r
-       // Not to be modified\r
-       protected final static Point2D[] DEFAULT_CELL_OFFSETS = new Point2D[] {\r
-                       new Point2D.Double(-45, 0),\r
-                       new Point2D.Double(22, 0),\r
-                       new Point2D.Double(24, 0)\r
-       };\r
-\r
-       // Not to be modified\r
-       protected final static Point2D[] ZERO_CELL_OFFSETS = new Point2D[] {\r
-                       new Point2D.Double(0, 0),\r
-                       new Point2D.Double(0, 0),\r
-                       new Point2D.Double(0, 0)\r
-       };\r
-\r
-       protected Point2D[] getCellOffsets() {\r
-               return DEFAULT_CELL_OFFSETS;\r
-       }\r
-\r
-       @Override\r
-       public void applyStyleForNode(EvaluationContext observer, INode _node, MonitorTextGridResult result) {\r
-\r
-               String value = result != null ? result.getText1() : null;\r
-               boolean enabled = result != null ? result.getEnabled() : false;\r
-\r
-               if (value != null && enabled) {\r
-\r
-                       //        if (value != null && !value.isEmpty() && !value.trim().isEmpty()) {\r
-\r
-                       String value2 = result != null ? result.getText2() : null;\r
-                       String value3 = result != null ? result.getText3() : null;\r
-                       \r
-                       double spacing = result.getSpacing();\r
-\r
-                       final Function1<String, String> modifier = result != null ? result.getModifier() : null;\r
-                       final Function1<String, String> validator = result != null ? result.getValidator() : null;\r
-                       final Function1<Vec2d, Boolean> translator = result != null ? result.getTranslator() : null;\r
-                       final RVI rvi = result != null ? result.getRVI() : null;\r
-\r
-                       final TextGridNode node = ProfileVariables.claimChild(_node, "", "TextGridStyle", TextGridNode.class, observer);\r
-                       if (node == null)\r
-                               return;\r
-\r
-                       // This assumes that this TextGridStyle instance will be devoted to\r
-                       // this row ID until the end of its life.\r
-                       String id = result.getRowId();\r
-                       //System.out.println(this + " ID: " + id);\r
-                       if (!id.equals(this.id)) {\r
-                               //System.out.println(this + " SET ID: " + this.id + " -> " + id);\r
-                               this.id = id;\r
-                       }\r
-\r
-                       Integer newRow = observer.getTemporaryProperty(_node, "location");\r
-                       if (newRow == null)\r
-                               newRow = 1;\r
-\r
-                       // Remove from existing row to add to another row if necessary.\r
-                       Integer row = observer.getProperty(_node, id);\r
-                       if (row != null && row != newRow) {\r
-                               String actualId = node.getRowId(row);\r
-                               if (id.equals(actualId)) {\r
-                                       node.removeRow(row);\r
-                               }\r
-                       }\r
-                       row = newRow;\r
-\r
-                       node.setRowId(row, id);\r
-                       observer.setProperty(_node, id, row);\r
-                       observer.setTemporaryProperty(_node, "location", row + 1);\r
-\r
-                       node.setText(2, row, value2);\r
-                       node.setUp(result.getUp());\r
-\r
-                       MonitorTextGridResult cache = node.getCache(1, row);\r
-                       if(cache != null && cache.sameStructure(result)) return;\r
-\r
-                       node.setCache(1, row, result);\r
-\r
-                       boolean isConnection = _node instanceof ConnectionNode;\r
-                       \r
-                       Rectangle2D elementBounds = isConnection ? EMPTY_BOUNDS : NodeUtil.getLocalElementBounds(_node);\r
-                       if(elementBounds == null) {\r
-                               new Exception("Cannot get local element bounds for node " + _node.toString()).printStackTrace();\r
-                               // This is here for checking why getLocalElementBounds failed in the debugger.\r
-                               NodeUtil.getLocalElementBounds(_node);\r
-                               return;\r
-                       }\r
-\r
-                       //            System.err.println("elementBounds " + elementBounds);\r
-                       //            System.err.println("parentTransform " + result.getParentTransform());\r
-\r
-                       AffineTransform at = getTransform(_node,result.getParentTransform(), elementBounds, row, result.getUp());\r
-                       Vec2d offset = result.getOffset();\r
-\r
-                       Point2D[] cellOffsets = getCellOffsets();\r
-                       \r
-                       AffineTransform at1 = new AffineTransform(at);\r
-                       at1.translate(cellOffsets[0].getX(),cellOffsets[0].getY());\r
-                       AffineTransform at2 = new AffineTransform(at);\r
-                       at2.translate(cellOffsets[1].getX()+spacing,cellOffsets[1].getY());\r
-                       AffineTransform at3 = new AffineTransform(at);\r
-                       at3.translate(cellOffsets[2].getX()+spacing,cellOffsets[2].getY());\r
-                       \r
-                       at1.translate(offset.x, offset.y);\r
-                       at2.translate(offset.x, offset.y);\r
-                       at3.translate(offset.x, offset.y);\r
-\r
-                       node.setTransform(1, row, at1);\r
-                       node.setTransform(2, row, at2);\r
-                       node.setTransform(3, row, at3);\r
-\r
-                       Alignment[] alignments = result.getAlignments();\r
-                       if(alignments != null) {\r
-                               node.setHorizontalAlignment(1, row, (byte) alignments[0].ordinal());\r
-                               node.setHorizontalAlignment(2, row, (byte) alignments[1].ordinal());\r
-                               node.setHorizontalAlignment(3, row, (byte) alignments[2].ordinal());\r
-                       } else {\r
-                               node.setHorizontalAlignment(1, row, (byte) getAlignment(1).ordinal());\r
-                               node.setHorizontalAlignment(2, row, (byte) getAlignment(2).ordinal());\r
-                               node.setHorizontalAlignment(3, row, (byte) getAlignment(3).ordinal());\r
-                       }\r
-                       \r
-                       Alignment[] verticalAlignments = result.getVerticalAlignments();\r
-                       if(verticalAlignments != null) {\r
-                               node.setVerticalAlignment(1, row, (byte) verticalAlignments[0].ordinal());\r
-                               node.setVerticalAlignment(2, row, (byte) verticalAlignments[1].ordinal());\r
-                               node.setVerticalAlignment(3, row, (byte) verticalAlignments[2].ordinal());\r
-                       } else {\r
-                               node.setVerticalAlignment(1, row, (byte) getVerticalAlignment(1).ordinal());\r
-                               node.setVerticalAlignment(2, row, (byte) getVerticalAlignment(2).ordinal());\r
-                               node.setVerticalAlignment(3, row, (byte) getVerticalAlignment(3).ordinal());\r
-                       }\r
-\r
-                       node.setZIndex(3000);\r
-\r
-                       org.simantics.common.color.Color color = result.getColor();\r
-                       java.awt.Color awtColor = color != null ? Colors.awt(color) : Color.DARK_GRAY;\r
-\r
-                       setTextNodeData(node, 1, row, value, FONT, awtColor, BACKGROUND_COLOR);\r
-                       setTextNodeData(node, 2, row, value2, result.getPending(), FONT, awtColor, BACKGROUND_COLOR);\r
-                       setTextNodeData(node, 3, row, value3, FONT, awtColor, BACKGROUND_COLOR);\r
-\r
-                       node.setEditable(1, row, false);\r
-                       node.setForceEventListening(2, row, true);\r
-                       node.setEditable(2, row, modifier != null);\r
-                       node.setEditable(3, row, false);\r
-\r
-                       final int finalRow = row;\r
-\r
-                       if (modifier != null) {\r
-                               node.setTextListener(2, row, new ITextListener() {\r
-                                       @Override\r
-                                       public void textChanged() {}\r
-\r
-                                       @Override\r
-                                       public void textEditingStarted() {}\r
-\r
-                                       @Override\r
-                                       public void textEditingCancelled() {\r
-                                       }\r
-\r
-                                       @Override\r
-                                       public void textEditingEnded() {\r
-\r
-                                               TextNode t = node.get(2, finalRow);\r
-                                               if (t == null)\r
-                                                       return;\r
-\r
-                                               if(!t.getText().equals(t.getTextBeforeEdit()))\r
-                                                       modifier.apply(t.getText());\r
-\r
-                                       }\r
-                               });\r
-                       } else {\r
-                               node.setTextListener(2,  row,  null);\r
-                       }\r
-\r
-                       node.setInputValidator(2, row, validator);\r
-                       node.setTranslator(translator);\r
-                       \r
-                       node.setRVI(2, row, rvi);\r
-\r
-                       postProcessNode(node, row);\r
-\r
-               } else {\r
-                       cleanupStyleForNode(observer, _node);\r
-               }\r
-       }\r
-\r
-       private void setTextNodeData(TextGridNode node, int x, int y, String text, Font font, Color fgColor, Color bgColor) {\r
-               if (text != null) {\r
-                       node.setText(x, y, text);\r
-                       node.setFont(x, y, font);\r
-                       node.setColor(x, y, fgColor);\r
-                       node.setBackgroundColor(x, y, bgColor);\r
-               } else {\r
-                       // Prevent rendering of the node.\r
-                       node.setFont(x, y, null);\r
-               }\r
-       }\r
-\r
-       private void setTextNodeData(TextGridNode node, int x, int y, String text, boolean pending, Font font, Color fgColor, Color bgColor) {\r
-               setTextNodeData(node, x, y, text, font, fgColor, bgColor);\r
-               node.setPending(x, y, pending);\r
-       }\r
-\r
-       protected Alignment getAlignment(int column) {\r
-               switch(column) {\r
-               case 1: return Alignment.TRAILING;\r
-               case 2: return Alignment.TRAILING;\r
-               case 3: return Alignment.LEADING;\r
-               default: return Alignment.LEADING;\r
-               }\r
-       }\r
-\r
-       protected Alignment getVerticalAlignment(int column) {\r
-               return Alignment.TRAILING;\r
-       }\r
-\r
-       @Override\r
-       protected void cleanupStyleForNode(EvaluationContext observer, INode _node) {\r
-               Integer row = observer.getProperty(_node, id);\r
-               //System.out.println(this + " cleanup(" + id + ", " + row + ")");\r
-               //System.out.println(element);\r
-               if (row == null)\r
-                       return;\r
-               observer.setProperty(_node, id, null);\r
-               TextGridNode node = ProfileVariables.browseChild(_node, "TextGridStyle");\r
-               if (node != null)\r
-                       node.removeRow(row);\r
-       }\r
-\r
-       protected void postProcessNode(TextGridNode node, int row) {\r
-       }\r
-\r
-}\r
+package org.simantics.diagram.profile;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.datatypes.literal.Vec2d;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.variable.RVI;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.diagram.elements.ITextListener;
+import org.simantics.diagram.elements.TextGridNode;
+import org.simantics.diagram.elements.TextNode;
+import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
+import org.simantics.g2d.utils.Alignment;
+import org.simantics.modeling.ModelingResources;
+import org.simantics.scenegraph.INode;
+import org.simantics.scenegraph.g2d.nodes.ConnectionNode;
+import org.simantics.scenegraph.profile.EvaluationContext;
+import org.simantics.scenegraph.profile.common.ProfileVariables;
+import org.simantics.scenegraph.utils.GeometryUtils;
+import org.simantics.scenegraph.utils.NodeUtil;
+import org.simantics.scl.runtime.function.Function1;
+import org.simantics.ui.colors.Colors;
+
+/**
+ * @author Antti Villberg
+ * @author Tuukka Lehtonen
+ */
+public abstract class TextGridStyle extends StyleBase<MonitorTextGridResult> {
+
+       private final Font  FONT             = Font.decode("Arial 12");
+       private final Color BACKGROUND_COLOR = new Color(255, 255, 255, 192);
+       private static final Rectangle2D EMPTY_BOUNDS = new Rectangle2D.Double(0, 0, 0, 0);
+
+       protected double xOffset;
+       protected double yOffset;
+
+       public TextGridStyle(Resource r) {
+               this(r, 0.0, 2.1);
+       }
+
+       public TextGridStyle(Resource r, double xOffset, double yOffset) {
+           super(r);
+               this.xOffset = xOffset;
+               this.yOffset = yOffset;
+       }
+
+       public Resource getPropertyRelation(ReadGraph g, Resource module) {
+       throw new Error("Fix this");
+       }
+
+       /**
+        * @return the name of the scene graph node to create to represent the text
+        *         element created by this style
+        */
+       public String getNodeName() {
+               return getClass().getSimpleName();
+       }
+
+       /**
+        * Override to customize.
+        * 
+        * @param graph
+        * @param element
+        * @return
+        * @throws DatabaseException
+        */
+       protected Resource getConfigurationComponent(ReadGraph graph, Resource element) throws DatabaseException {
+               ModelingResources mr = ModelingResources.getInstance(graph);
+               Resource config = graph.getPossibleObject(element, mr.ElementToComponent);
+               return config;
+       }
+
+       /**
+        * Override to customize.
+        * 
+        * @param graph
+        * @param element
+        * @return
+        * @throws DatabaseException
+        */
+       protected String getConfigurationComponentNameForElement(ReadGraph graph, Resource element) throws DatabaseException {
+               Resource config = getConfigurationComponent(graph, element);
+               if (config == null)
+                       return null;
+               String name = graph.getPossibleRelatedValue(config, getPropertyRelation(graph,element), Bindings.STRING);
+               return name;
+       }
+
+       public AffineTransform getTransform(INode node, AffineTransform parentTransform, Rectangle2D elementBounds, int location, boolean up) {
+               return getTransform(parentTransform, elementBounds, location, up);
+       }
+
+       public AffineTransform getTransform(AffineTransform parentTransform, Rectangle2D elementBounds, int location, boolean up) {
+
+               double scale = GeometryUtils.getScale(parentTransform);
+
+               AffineTransform at = AffineTransform.getTranslateInstance(
+                               parentTransform.getTranslateX() + elementBounds.getCenterX() * scale + xOffset,
+                               parentTransform.getTranslateY() + elementBounds.getMinY() * scale + yOffset*(location-1) + (up ? 0.0 : (2.0*yOffset) + elementBounds.getHeight() * scale) 
+                               );
+               at.scale(0.15, 0.15);
+
+               return at;
+
+       }
+
+       protected String rowId() {
+               return "";
+       }
+
+       @Override
+       public MonitorTextGridResult calculateStyle(ReadGraph graph, Resource runtimeDiagram, Resource entry, Resource element, Variable configuration)
+                       throws DatabaseException {
+               String name = getConfigurationComponentNameForElement(graph, element);
+               if (name == null)
+                       return null;
+               AffineTransform transform = DiagramGraphUtil.getDynamicAffineTransform(graph, runtimeDiagram, element);
+               Vec2d offset = DiagramGraphUtil.getOffset(graph, element);
+               boolean enabled = !DiagramGraphUtil.getProfileMonitorsHidden(graph, element);
+               boolean up = DiagramGraphUtil.getProfileMonitorsUp(graph, element);
+               double spacing = DiagramGraphUtil.getProfileMonitorSpacing(graph, element);
+               return new MonitorTextGridResult(rowId(), name, "", "", enabled, up, spacing, null, null, null, transform, offset);
+       }
+
+       // Not to be modified
+       protected final static Point2D[] DEFAULT_CELL_OFFSETS = new Point2D[] {
+                       new Point2D.Double(-45, 0),
+                       new Point2D.Double(22, 0),
+                       new Point2D.Double(24, 0)
+       };
+
+       // Not to be modified
+       protected final static Point2D[] ZERO_CELL_OFFSETS = new Point2D[] {
+                       new Point2D.Double(0, 0),
+                       new Point2D.Double(0, 0),
+                       new Point2D.Double(0, 0)
+       };
+
+       protected Point2D[] getCellOffsets() {
+               return DEFAULT_CELL_OFFSETS;
+       }
+
+       @Override
+       public void applyStyleForNode(EvaluationContext observer, INode _node, MonitorTextGridResult result) {
+
+               String value = result != null ? result.getText1() : null;
+               boolean enabled = result != null ? result.getEnabled() : false;
+
+               if (value != null && enabled) {
+
+                       //        if (value != null && !value.isEmpty() && !value.trim().isEmpty()) {
+
+                       String value2 = result != null ? result.getText2() : null;
+                       String value3 = result != null ? result.getText3() : null;
+
+                       double spacing = result.getSpacing();
+
+                       final Function1<String, String> modifier = result != null ? result.getModifier() : null;
+                       final Function1<String, String> validator = result != null ? result.getValidator() : null;
+                       final Function1<Vec2d, Boolean> translator = result != null ? result.getTranslator() : null;
+                       final RVI rvi = result != null ? result.getRVI() : null;
+
+                       final TextGridNode node = ProfileVariables.claimChild(_node, "", "TextGridStyle", TextGridNode.class, observer);
+                       if (node == null)
+                               return;
+
+                       // This assumes that this TextGridStyle instance will be devoted to
+                       // this row ID until the end of its life.
+//                     String id = result.getRowId();
+//                     System.out.println(this + " ID: " + id);
+//                     if (!id.equals(this.id)) {
+//                             System.out.println(this + " SET ID: " + this.id + " -> " + id);
+//                             this.id = id;
+//                     }
+
+                       Integer newRow = observer.getTemporaryProperty(_node, "location");
+                       if (newRow == null)
+                               newRow = 1;
+
+                       // Remove from existing row to add to another row if necessary.
+                       Integer row = getCurrentRowNumber(observer, _node);
+                       if (row != null && row != newRow) {
+                               String actualId = node.getRowId(row);
+                               String id = observer.getProperty(_node, rowIdKey());
+                               if (id.equals(actualId)) {
+                                       node.removeRow(row);
+                               }
+                       }
+                       row = newRow;
+
+                       node.setRowId(row, result.getRowId());
+
+                       setCurrentRowNumber(observer, _node, result.getRowId(), row);
+
+                       observer.setTemporaryProperty(_node, "location", row + 1);
+
+                       node.setText(2, row, value2);
+                       node.setUp(result.getUp());
+
+                       MonitorTextGridResult cache = node.getCache(1, row);
+                       if(cache != null && cache.sameStructure(result)) return;
+
+                       node.setCache(1, row, result);
+
+                       boolean isConnection = _node instanceof ConnectionNode;
+
+                       Rectangle2D elementBounds = isConnection ? EMPTY_BOUNDS : NodeUtil.getLocalElementBounds(_node);
+                       if(elementBounds == null) {
+                               new Exception("Cannot get local element bounds for node " + _node.toString()).printStackTrace();
+                               // This is here for checking why getLocalElementBounds failed in the debugger.
+                               NodeUtil.getLocalElementBounds(_node);
+                               return;
+                       }
+
+                       //            System.err.println("elementBounds " + elementBounds);
+                       //            System.err.println("parentTransform " + result.getParentTransform());
+
+                       AffineTransform at = getTransform(_node,result.getParentTransform(), elementBounds, row, result.getUp());
+                       Vec2d offset = result.getOffset();
+
+                       Point2D[] cellOffsets = getCellOffsets();
+
+                       AffineTransform at1 = new AffineTransform(at);
+                       at1.translate(cellOffsets[0].getX(),cellOffsets[0].getY());
+                       AffineTransform at2 = new AffineTransform(at);
+                       at2.translate(cellOffsets[1].getX()+spacing,cellOffsets[1].getY());
+                       AffineTransform at3 = new AffineTransform(at);
+                       at3.translate(cellOffsets[2].getX()+spacing,cellOffsets[2].getY());
+
+                       at1.translate(offset.x, offset.y);
+                       at2.translate(offset.x, offset.y);
+                       at3.translate(offset.x, offset.y);
+
+                       node.setTransform(1, row, at1);
+                       node.setTransform(2, row, at2);
+                       node.setTransform(3, row, at3);
+
+                       Alignment[] alignments = result.getAlignments();
+                       if(alignments != null) {
+                               node.setHorizontalAlignment(1, row, (byte) alignments[0].ordinal());
+                               node.setHorizontalAlignment(2, row, (byte) alignments[1].ordinal());
+                               node.setHorizontalAlignment(3, row, (byte) alignments[2].ordinal());
+                       } else {
+                               node.setHorizontalAlignment(1, row, (byte) getAlignment(1).ordinal());
+                               node.setHorizontalAlignment(2, row, (byte) getAlignment(2).ordinal());
+                               node.setHorizontalAlignment(3, row, (byte) getAlignment(3).ordinal());
+                       }
+
+                       Alignment[] verticalAlignments = result.getVerticalAlignments();
+                       if(verticalAlignments != null) {
+                               node.setVerticalAlignment(1, row, (byte) verticalAlignments[0].ordinal());
+                               node.setVerticalAlignment(2, row, (byte) verticalAlignments[1].ordinal());
+                               node.setVerticalAlignment(3, row, (byte) verticalAlignments[2].ordinal());
+                       } else {
+                               node.setVerticalAlignment(1, row, (byte) getVerticalAlignment(1).ordinal());
+                               node.setVerticalAlignment(2, row, (byte) getVerticalAlignment(2).ordinal());
+                               node.setVerticalAlignment(3, row, (byte) getVerticalAlignment(3).ordinal());
+                       }
+
+                       node.setZIndex(3000);
+
+                       org.simantics.common.color.Color color = result.getColor();
+                       Color awtColor = color != null ? Colors.awt(color) : Color.DARK_GRAY;
+                       Color bgColor = getBackgroundColor();
+                       Font font = getFont();
+
+                       setTextNodeData(node, 1, row, value, font, awtColor, bgColor);
+                       setTextNodeData(node, 2, row, value2, result.getPending(), font, awtColor, bgColor);
+                       setTextNodeData(node, 3, row, value3, font, awtColor, bgColor);
+
+                       node.setEditable(1, row, false);
+                       node.setForceEventListening(2, row, true);
+                       node.setEditable(2, row, modifier != null);
+                       node.setEditable(3, row, false);
+
+                       final int finalRow = row;
+
+                       if (modifier != null) {
+                               node.setTextListener(2, row, new ITextListener() {
+                                       @Override
+                                       public void textChanged() {}
+
+                                       @Override
+                                       public void textEditingStarted() {}
+
+                                       @Override
+                                       public void textEditingCancelled() {
+                                       }
+
+                                       @Override
+                                       public void textEditingEnded() {
+
+                                               TextNode t = node.get(2, finalRow);
+                                               if (t == null)
+                                                       return;
+
+                                               if(!t.getText().equals(t.getTextBeforeEdit()))
+                                                       modifier.apply(t.getText());
+
+                                       }
+                               });
+                       } else {
+                               node.setTextListener(2,  row,  null);
+                       }
+
+                       node.setInputValidator(2, row, validator);
+                       node.setTranslator(translator);
+
+                       node.setRVI(2, row, rvi);
+
+                       postProcessNode(node, row);
+
+               } else {
+                       cleanupStyleForNode(observer, _node);
+               }
+       }
+
+       private void setTextNodeData(TextGridNode node, int x, int y, String text, Font font, Color fgColor, Color bgColor) {
+               if (text != null) {
+                       node.setText(x, y, text);
+                       node.setFont(x, y, font);
+                       node.setColor(x, y, fgColor);
+                       node.setBackgroundColor(x, y, bgColor);
+               } else {
+                       // Prevent rendering of the node.
+                       node.setFont(x, y, null);
+               }
+       }
+
+       private void setTextNodeData(TextGridNode node, int x, int y, String text, boolean pending, Font font, Color fgColor, Color bgColor) {
+               setTextNodeData(node, x, y, text, font, fgColor, bgColor);
+               node.setPending(x, y, pending);
+       }
+
+       protected Font getFont() {
+               return FONT;
+       }
+
+       protected Color getBackgroundColor() {
+               return BACKGROUND_COLOR;
+       }
+
+       protected Alignment getAlignment(int column) {
+               switch(column) {
+               case 1: return Alignment.TRAILING;
+               case 2: return Alignment.TRAILING;
+               case 3: return Alignment.LEADING;
+               default: return Alignment.LEADING;
+               }
+       }
+
+       protected Alignment getVerticalAlignment(int column) {
+               return Alignment.TRAILING;
+       }
+
+       @Override
+       protected void cleanupStyleForNode(EvaluationContext observer, INode _node) {
+               Integer row = getCurrentRowNumber(observer, _node);
+               //System.out.println(this + " cleanup(" + id + ", " + row + ")");
+               //System.out.println(element);
+               if (row == null)
+                       return;
+               clearCurrentRowNumber(observer, _node);
+               TextGridNode node = ProfileVariables.browseChild(_node, "TextGridStyle");
+               if (node != null)
+                       node.removeRow(row);
+       }
+
+       private Integer getCurrentRowNumber(EvaluationContext observer, INode _node) {
+               String rowId = observer.getProperty(_node, rowIdKey());
+               return observer.getProperty(_node, rowId);
+       }
+
+       private void setCurrentRowNumber(EvaluationContext observer, INode _node, String rowId, int row) {
+               // Mapping style identity -> rowId (resourceId)
+               observer.setProperty(_node, rowIdKey(), rowId);
+               // Mapping rowId (resourceId) -> row number
+               observer.setProperty(_node, rowId, row);
+       }
+
+       private void clearCurrentRowNumber(EvaluationContext observer, INode _node) {
+               String rowId = observer.getProperty(_node, rowIdKey());
+               if(rowId != null) {
+                       observer.setProperty(_node, rowIdKey(), null);
+                       Integer row = observer.getProperty(_node, rowId);
+                       if(row != null) {
+                               observer.setProperty(_node, rowId, null);
+                       }
+               }
+       }
+
+       protected void postProcessNode(TextGridNode node, int row) {
+       }
+
+       private String rowIdKey() {
+               return "style" + getIdentity().toString();
+       }
+
+}