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