1   /****************************************************************************
2    * This demo file is part of yFiles for Java 2.14.
3    * Copyright (c) 2000-2017 by yWorks GmbH, Vor dem Kreuzberg 28,
4    * 72070 Tuebingen, Germany. All rights reserved.
5    * 
6    * yFiles demo files exhibit yFiles for Java functionalities. Any redistribution
7    * of demo files in source code or binary form, with or without
8    * modification, is not permitted.
9    * 
10   * Owners of a valid software license for a yFiles for Java version that this
11   * demo is shipped with are allowed to use the demo source code as basis
12   * for their own yFiles for Java powered applications. Use of such programs is
13   * governed by the rights and conditions as set out in the yFiles for Java
14   * license agreement.
15   * 
16   * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED
17   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19   * NO EVENT SHALL yWorks BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21   * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   *
27   ***************************************************************************/
28  package demo.view.realizer;
29  
30  import demo.view.DemoBase;
31  
32  import y.base.Edge;
33  import y.base.EdgeMap;
34  import y.base.Node;
35  import y.geom.YInsets;
36  import y.layout.LayoutOrientation;
37  import y.layout.PortConstraint;
38  import y.layout.PortConstraintKeys;
39  import y.layout.hierarchic.IncrementalHierarchicLayouter;
40  import y.layout.hierarchic.incremental.LayerConstraintFactory;
41  import y.view.AbstractCustomNodePainter;
42  import y.view.EditMode;
43  import y.view.GenericNodeRealizer;
44  import y.view.Graph2D;
45  import y.view.Graph2DLayoutExecutor;
46  import y.view.LineType;
47  import y.view.MultiplexingNodeEditor;
48  import y.view.NodeLabel;
49  import y.view.NodeRealizer;
50  import y.view.ShapeNodePainter;
51  import y.view.YRenderingHints;
52  import y.view.hierarchy.HierarchyManager;
53  import y.view.tabular.TableGroupNodeRealizer;
54  import y.view.tabular.TableGroupNodeRealizer.Column;
55  import y.view.tabular.TableGroupNodeRealizer.Row;
56  import y.view.tabular.TableGroupNodeRealizer.Table;
57  import y.view.tabular.TableNodePainter;
58  import y.view.tabular.TableSelectionEditor;
59  import y.view.tabular.TableStyle;
60  
61  import java.awt.Color;
62  import java.awt.EventQueue;
63  import java.awt.GradientPaint;
64  import java.awt.Graphics2D;
65  import java.awt.Paint;
66  import java.awt.Shape;
67  import java.awt.Stroke;
68  import java.awt.event.ActionEvent;
69  import java.awt.geom.AffineTransform;
70  import java.awt.geom.Rectangle2D;
71  import java.util.HashMap;
72  import java.util.Iterator;
73  import java.util.List;
74  import java.util.Locale;
75  import java.util.Map;
76  import javax.swing.AbstractAction;
77  import javax.swing.JMenu;
78  import javax.swing.JMenuBar;
79  
80  /**
81   * Demonstrates different visual styles for table groups and their content.
82   *
83   * @see <a href="http://docs.yworks.com/yfiles/doc/api/index.html#/dguide/tabular_data_table_structure#tabular_data_rows_columns" target="_blank">Section Table Structure Model</a> in the yFiles for Java Developer's Guide
84   */
85  public class TableStyleDemo extends DemoBase {
86    private static final String CONFIGURATION_POOL_GRADIENT = "POOL_GRADIENT";
87    private static final String CONFIGURATION_POOL_ALTERNATING = "POOL_ALTERNATING";
88    private static final String CONFIGURATION_POOL_GENERIC = "POOL_GENERIC";
89    private static final String CONFIGURATION_POOL_BPMN_STYLE = "POOL_BPMN_STYLE";
90    private static final String CONFIGURATION_GRADIENT_RECT = "GRADIENT_RECT";
91    private static final String CONFIGURATION_GRADIENT_ROUNDRECT = "GRADIENT_ROUNDRECT";
92    private static final String CONFIGURATION_GRADIENT_DIAMOND = "GRADIENT_DIAMOND";
93    private static final String CONFIGURATION_GRADIENT_ELLIPSE = "GRADIENT_ELLIPSE";
94    private static final String CONFIGURATION_SIMPLE_ROUNDRECT = "SIMPLE_ROUNDRECT";
95    private static final String CONFIGURATION_SIMPLE_DIAMOND = "SIMPLE_DIAMOND";
96    private static final String CONFIGURATION_SIMPLE_ELLIPSE = "SIMPLE_ELLIPSE";
97  
98    private static final Color MAGENTA = new Color(253, 0, 127);
99    private static final Color ORANGE = new Color(249, 134, 5);
100   private static final Color DARK_GRAY = new Color(132, 131, 129);
101   private static final Color GREEN = new Color(156, 210, 60);
102   private static final Color PASTEL_YELLOW = new Color(254, 254, 212);
103   private static final Color PASTEL_GREEN = new Color(212, 254, 228);
104   private static final Color PASTEL_BLUE = new Color(212, 228, 254);
105   private static final Color LIGHT_BLUE = new Color(161, 188, 255);
106   private static final Color DARK_GREEN = new Color(98, 167, 22);
107   private static final Color BLOOD_RED = new Color(153, 0, 0);
108 
109   static {
110     initConfigurations();
111   }
112 
113   public TableStyleDemo() {
114     createBpmnStyleSample(view.getGraph2D());
115     view.fitContent();
116   }
117 
118   protected void initialize() {
119     new HierarchyManager(view.getGraph2D());
120   }
121 
122   /**
123    * Overwritten to disable undo/redo because this is not an editable demo.
124    */
125   protected boolean isUndoRedoEnabled() {
126     return false;
127   }
128 
129   /**
130    * Overwritten to disable clipboard because this is not an editable demo.
131    */
132   protected boolean isClipboardEnabled() {
133     return false;
134   }
135 
136   /**
137    * Creates an almost view-only edit mode. Almost view-only because
138    * nodes may be selected and table nodes, columns, and rows may be resized.
139    */
140   protected EditMode createEditMode() {
141     final EditMode editMode = new EditMode();
142     editMode.allowBendCreation(false);
143     editMode.allowEdgeCreation(false);
144     editMode.allowLabelSelection(false);
145     editMode.allowMoveLabels(false);
146     editMode.allowMovePorts(false);
147     editMode.allowMoveSelection(false);
148     editMode.allowMoving(false);
149     editMode.allowMovingWithPopup(false);
150     editMode.allowNodeCreation(false);
151     editMode.allowNodeEditing(false);
152     editMode.allowResizeNodes(false);
153 
154     // activate node specific user interaction
155     // e.g. TableGroupNodeRealizer usually is configured to recognize mouse
156     // gestures for selecting and resizing tables, columns, and rows as well
157     // as reordering columns and rows
158     editMode.getMouseInputMode().setNodeSearchingEnabled(true);
159     return editMode;
160   }
161 
162   /**
163    * Creates a sample graph depicting a table node that uses
164    * {@link demo.view.realizer.TableStyleDemo.GradientRowPainter} as a custom
165    * row painter.
166    * @param graph   the graph to configure.
167    */
168   private void createGradientSample( final Graph2D graph ) {
169     graph.clear();
170 
171 
172     // create the realizer for the table node
173     final TableGroupNodeRealizer tgnr = new TableGroupNodeRealizer();
174     tgnr.setConfiguration(CONFIGURATION_POOL_GRADIENT);
175     tgnr.setLocation(0, 0);
176     tgnr.setAutoResize(true);
177 
178     tgnr.setFillColor(Color.LIGHT_GRAY);
179     // this color is used by GradientRowPainter together with a row specific
180     // color to create a gradient fill for each row
181     tgnr.setFillColor2(Color.WHITE);
182 
183     tgnr.setDefaultColumnInsets(new YInsets(0, 5, 0, 5));
184     tgnr.setDefaultColumnWidth(400);
185     tgnr.setDefaultRowHeight(100);
186     tgnr.setDefaultRowInsets(new YInsets(5, 20, 5, 0));
187 
188 
189     final Table dt = tgnr.getTable();
190 
191     // create one labeled row for each of the four colors
192     final Color[] colors = {GREEN, DARK_GRAY, ORANGE, MAGENTA};
193     final double rowSizeAdjustment = 10;
194     final Map row2color = new HashMap();
195     for (int i = 0; i < colors.length; ++i) {
196       final Row row;
197       if (i == 0) {
198         row = dt.getRow(0);
199       } else {
200         row = dt.addRow();
201       }
202       row2color.put(row, colors[i]);
203 
204       final NodeLabel label = tgnr.createNodeLabel();
205       label.setText("Lane " + (i + 1));
206       final double minHeight = label.getWidth() + rowSizeAdjustment;
207       row.setMinimumHeight(minHeight);
208 
209       // associate the label to the row
210       // the ratio value of 0 means the label will be left-aligned (regarding
211       // the row) and rotated 90 degress counter clockwise
212       tgnr.configureRowLabel(label, row, true, 0);
213 
214       // row labels are normal node labels and have to be explicitly added
215       // to the realizer as usual
216       tgnr.addLabel(label);
217     }
218 
219     // sets the style property that is used by GradientRowPainter to determine
220     // the color that defines the gradient fill for each row
221     tgnr.setStyleProperty(
222             GradientRowPainter.STYLE_ROW_COLOR_MAP,
223             new GradientRowPainter.RowColorMap() {
224       public Color getColor( final Row row ) {
225         return (Color) row2color.get(row);
226       }
227     });
228 
229     // ensure that the table has the correct (sufficiently large) size
230     tgnr.updateTableBounds();
231 
232 
233     final HierarchyManager hm = HierarchyManager.getInstance(graph);
234 
235     // create a group node ..
236     final Node pool = hm.createGroupNode(graph);
237     // .. and assign it the previously created table realizer
238     graph.setRealizer(pool, tgnr);
239 
240     // prototype realizer for rectangular child nodes
241     final GenericNodeRealizer rectangle = new GenericNodeRealizer();
242     rectangle.setConfiguration(CONFIGURATION_GRADIENT_RECT);
243     rectangle.setSize(80, 60);
244     rectangle.setCenter(tgnr.getCenterX(), tgnr.getCenterY());
245     rectangle.setFillColor(Color.WHITE);
246 
247     // prototype realizer for rectangular child nodes with rounded corners
248     final GenericNodeRealizer roundRect = new GenericNodeRealizer();
249     roundRect.setConfiguration(CONFIGURATION_GRADIENT_ROUNDRECT);
250     roundRect.setSize(80, 60);
251     roundRect.setCenter(tgnr.getCenterX(), tgnr.getCenterY());
252     roundRect.setFillColor(Color.WHITE);
253     roundRect.setFillColor2(PASTEL_BLUE);
254 
255     // prototype realizer for circular child nodes
256     final GenericNodeRealizer ellipse = new GenericNodeRealizer();
257     ellipse.setConfiguration(CONFIGURATION_GRADIENT_ELLIPSE);
258     ellipse.setSize(60, 60);
259     ellipse.setCenter(tgnr.getCenterX(), tgnr.getCenterY());
260     ellipse.setFillColor(Color.WHITE);
261     ellipse.setFillColor2(PASTEL_YELLOW);
262 
263     // prototype realizer for diamond-shaped child nodes
264     final GenericNodeRealizer diamond = new GenericNodeRealizer();
265     diamond.setConfiguration(CONFIGURATION_GRADIENT_DIAMOND);
266     diamond.setSize(60, 60);
267     diamond.setCenter(tgnr.getCenterX(), tgnr.getCenterY());
268     diamond.setFillColor(Color.WHITE);
269     diamond.setFillColor2(PASTEL_GREEN);
270 
271 
272     // create some nodes with different realizers
273     final Node[] nodes = new Node[22];
274     for (int i = 0; i < nodes.length; ++i) {
275       if (i < 5 ) {
276         if (i % 2 == 0) {
277           nodes[i] = graph.createNode(ellipse.createCopy());
278         } else {
279           nodes[i] = graph.createNode(rectangle.createCopy());
280         }
281       } else if (i == 6 || i == 11 || i == 12) {
282         nodes[i] = graph.createNode(diamond.createCopy());
283       } else if (i == 5 || i == 19 || (i > 8 && i < 18)) {
284         nodes[i] = graph.createNode(roundRect.createCopy());
285       } else {
286         nodes[i] = graph.createNode(rectangle.createCopy());
287       }
288     }
289 
290 
291     // label the recently created nodes and assign them to the previously
292     // created group node
293     for (int i = 0; i < nodes.length; ++i) {
294       graph.getRealizer(nodes[i]).setLabelText(Integer.toString(i + 1));
295 
296       // important: assign the node to the table group
297       hm.setParentNode(nodes[i], pool);
298 
299       // assign the child nodes to different rows
300       if (i < 5) {
301         dt.moveToRow(nodes[i], dt.getRow(0));
302       } else if (i < 9) {
303         dt.moveToRow(nodes[i], dt.getRow(1));
304       } else if (i < 18) {
305         dt.moveToRow(nodes[i], dt.getRow(2));
306       } else {
307         dt.moveToRow(nodes[i], dt.getRow(3));
308       }
309     }
310 
311 
312     // create some edges
313     final Edge[] edges = {
314       graph.createEdge(nodes[0], nodes[5]),
315       graph.createEdge(nodes[2], nodes[14]),
316       graph.createEdge(nodes[4], nodes[17]),
317 
318       graph.createEdge(nodes[5], nodes[6]),
319       graph.createEdge(nodes[6], nodes[9]),
320       graph.createEdge(nodes[6], nodes[10]),
321       graph.createEdge(nodes[6], nodes[11]),
322 
323       graph.createEdge(nodes[9], nodes[12]),
324       graph.createEdge(nodes[10], nodes[12]),
325       graph.createEdge(nodes[11], nodes[12]),
326       graph.createEdge(nodes[12], nodes[19]),
327       graph.createEdge(nodes[13], nodes[19]),
328 
329       graph.createEdge(nodes[19], nodes[20]),
330       graph.createEdge(nodes[19], nodes[21]),
331     };
332 //    for (int i = 0; i < edges.length; ++i) {
333 //      graph.getRealizer(edges[i]).setLabelText(Integer.toString(i));
334 //    }
335 
336 
337     // setup port constraints for diamond-shaped child nodes
338 
339     // create the corresponding port constraints ...
340     final EdgeMap srcPc = graph.createEdgeMap();
341     srcPc.set(edges[4], PortConstraint.create(PortConstraint.WEST));
342     srcPc.set(edges[5], PortConstraint.create(PortConstraint.EAST));
343     final EdgeMap tgtPc = graph.createEdgeMap();
344     tgtPc.set(edges[7], PortConstraint.create(PortConstraint.WEST));
345     tgtPc.set(edges[8], PortConstraint.create(PortConstraint.EAST));
346 
347     // ... and register the port constraints
348     graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, srcPc);
349     graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, tgtPc);
350 
351 
352     try {
353       layout(graph, true);
354     } finally {
355 
356       graph.removeDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY);
357       graph.removeDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY);
358       graph.disposeEdgeMap(tgtPc);
359       graph.disposeEdgeMap(srcPc);
360     }
361   }
362 
363   /**
364    * Creates a sample graph depicting a table node that uses alternating
365    * colors to paint its columns.
366    * @param graph   the graph to configure.
367    */
368   private void createAlternatingSample( final Graph2D graph ) {
369     graph.clear();
370 
371 
372     // create the realizer for the table node
373     final TableGroupNodeRealizer tgnr = new TableGroupNodeRealizer();
374     tgnr.setConfiguration(CONFIGURATION_POOL_ALTERNATING);
375     tgnr.setLocation(0, 0);
376     tgnr.setAutoResize(true);
377 
378     tgnr.setDefaultColumnInsets(new YInsets(20, 5, 0, 5));
379     tgnr.setDefaultColumnWidth(100);
380     tgnr.setDefaultRowHeight(100);
381     tgnr.setDefaultRowInsets(new YInsets(30, 0, 10, 0));
382 
383 
384     final Table dt = tgnr.getTable();
385     dt.setInsets(new YInsets(30, 5, 5, 5));
386 
387     // create couple of columns in the table model
388     final List cols = dt.getColumns();
389     dt.addColumn();
390     dt.addColumn();
391     dt.addColumn();
392     final Column[] columns = new Column[cols.size()];
393     cols.toArray(columns);
394 
395     final Row row0 = dt.getRow(0);
396 
397 
398     final NodeLabel label = tgnr.getLabel();
399     label.setText("Pool");
400     label.setPosition(NodeLabel.TOP);
401 
402     // configure the columns:
403     //  - assign each column a label
404     //  - set a suitable minimum size (width) to each column
405     final double columnSizeAdjustment = 10;
406     int i = 1;
407     for (Iterator it = cols.iterator(); it.hasNext(); ++i) {
408       final Column column = (Column) it.next();
409       final NodeLabel columnLabel = tgnr.createNodeLabel();
410       columnLabel.setText("Lane " + i);
411       final double minWidth = columnLabel.getWidth() + columnSizeAdjustment;
412       column.setMinimumWidth(minWidth);
413 
414       // associate the label to the column
415       tgnr.configureColumnLabel(columnLabel, column, true, 0);
416 
417       // column labels are normal node labels and have to be explicitly added
418       // to the realizer as usual
419       tgnr.addLabel(columnLabel);
420     }
421 
422     // ensure that the table has the correct (sufficiently large) size
423     tgnr.updateTableBounds();
424 
425 
426     final HierarchyManager hm = HierarchyManager.getInstance(graph);
427 
428 
429     // create a group node ..
430     final Node pool = hm.createGroupNode(graph);
431     // .. and assign it the previously created table realizer
432     graph.setRealizer(pool, tgnr);
433 
434 
435     // prototype realizer for child nodes
436     final GenericNodeRealizer prototype = new GenericNodeRealizer();
437     prototype.setConfiguration(CONFIGURATION_GRADIENT_RECT);
438     prototype.setSize(90, 60);
439     prototype.setCenter(
440             columns[0].calculateBounds().getCenterX(),
441             row0.calculateBounds().getCenterY());
442     prototype.setFillColor(Color.WHITE);
443     prototype.setFillColor2(PASTEL_BLUE);
444 
445     // create a couple of child nodes with different shapes
446     final String[] configurations = {
447             CONFIGURATION_GRADIENT_RECT,
448             CONFIGURATION_GRADIENT_DIAMOND,
449             CONFIGURATION_GRADIENT_DIAMOND,
450             CONFIGURATION_GRADIENT_DIAMOND,
451             CONFIGURATION_GRADIENT_RECT,
452             CONFIGURATION_GRADIENT_RECT,
453             CONFIGURATION_GRADIENT_RECT,
454             CONFIGURATION_GRADIENT_RECT,
455     };
456     final Node[] nodes = new Node[configurations.length];
457     for (int j = 0; j < nodes.length; ++j) {
458       final GenericNodeRealizer nr = new GenericNodeRealizer(prototype);
459       nr.setConfiguration(configurations[j]);
460       nr.setLabelText(Integer.toString(j + 1));
461       nodes[j] = graph.createNode(nr);
462 
463       // important: assign the node to the table group
464       hm.setParentNode(nodes[j], pool);
465 
466       // move the new node into (the first and only row of the) the table
467       dt.moveToRow(nodes[j], row0);
468     }
469 
470     // distribute the child nodes over the table
471     dt.moveToColumn(nodes[0], columns[3]);
472     dt.moveToColumn(nodes[1], columns[2]);
473     dt.moveToColumn(nodes[2], columns[1]);
474     dt.moveToColumn(nodes[4], columns[2]);
475     dt.moveToColumn(nodes[7], columns[3]);
476 
477 
478     // couple of edges
479     final Edge[] edges = {
480       graph.createEdge(nodes[0], nodes[1]),
481       graph.createEdge(nodes[1], nodes[0]),
482       graph.createEdge(nodes[1], nodes[2]),
483       graph.createEdge(nodes[2], nodes[3]),
484       graph.createEdge(nodes[2], nodes[4]),
485       graph.createEdge(nodes[3], nodes[5]),
486       graph.createEdge(nodes[3], nodes[6]),
487       graph.createEdge(nodes[4], nodes[6]),
488       graph.createEdge(nodes[4], nodes[7]),
489       graph.createEdge(nodes[6], nodes[7]),
490     };
491 //    for (int j = 0; j < edges.length; ++j) {
492 //      graph.getRealizer(edges[j]).setLabelText(Integer.toString(j));
493 //    }
494 
495 
496     // setup port constraints for diamond-shaped child nodes
497 
498     // create the corresponding port constraints ...
499     final EdgeMap srcPc = graph.createEdgeMap();
500     srcPc.set(edges[1], PortConstraint.create(PortConstraint.EAST));
501     srcPc.set(edges[3], PortConstraint.create(PortConstraint.WEST));
502     srcPc.set(edges[4], PortConstraint.create(PortConstraint.EAST));
503     srcPc.set(edges[5], PortConstraint.create(PortConstraint.WEST));
504     srcPc.set(edges[6], PortConstraint.create(PortConstraint.EAST));
505     final EdgeMap tgtPc = graph.createEdgeMap();
506     tgtPc.set(edges[0], PortConstraint.create(PortConstraint.NORTH));
507 
508     // ... and register the port constraints
509     graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, srcPc);
510     graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, tgtPc);
511 
512 
513     try {
514       layout(graph, true);
515     } finally {
516 
517       graph.removeDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY);
518       graph.removeDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY);
519       graph.disposeEdgeMap(tgtPc);
520       graph.disposeEdgeMap(srcPc);
521     }
522   }
523 
524   /**
525    * Creates a sample graph depicting a table node that uses the default
526    * painter configuration with custom row styles.
527    * @param graph   the graph to configure.
528    */
529   private void createGenericSample( final Graph2D graph ) {
530     graph.clear();
531 
532 
533     final TableGroupNodeRealizer tgnr = new TableGroupNodeRealizer();
534     tgnr.setConfiguration(CONFIGURATION_POOL_GENERIC);
535     tgnr.setLocation(0, 0);
536     tgnr.setAutoResize(true);
537 
538     // file color 2 is used by RowStyle as the row fill color
539     tgnr.setFillColor2(LIGHT_BLUE);
540     // register RowStyle as the style to be used when painting rows
541     tgnr.setStyleProperty(TableNodePainter.ROW_STYLE_ID, new RowStyle(false));
542     tgnr.setStyleProperty(TableNodePainter.ROW_SELECTION_STYLE_ID, new RowStyle(true));
543 
544     tgnr.setDefaultColumnInsets(new YInsets(0, 5, 0, 10));
545     tgnr.setDefaultColumnWidth(100);
546     tgnr.setDefaultRowInsets(new YInsets(5, 25, 5, 0));
547     tgnr.setDefaultRowHeight(80);
548 
549     final Table table = tgnr.getTable();
550     table.setInsets(new YInsets(10, 25, 10, 10));
551 
552     final Column column0 = table.getColumn(0);
553     final Row row0 = table.getRow(0);
554     final Row row1 = table.addRow();
555     table.addRow();
556 
557     final NodeLabel label = tgnr.getLabel();
558     label.setText("Pool");
559     label.setPosition(NodeLabel.LEFT);
560     label.setRotationAngle(270);
561 
562     // configure the rows:
563     //  - assign each row a label
564     //  - set a suitable minimum size (height) to each row
565     final double rowSizeAdjustment = 10;
566     int i = 1;
567     for (Iterator it = table.getRows().iterator(); it.hasNext(); ++i) {
568       final Row row = (Row) it.next();
569       final NodeLabel rowLabel = tgnr.createNodeLabel();
570       rowLabel.setText("Lane " + i);
571       final double minHeight = rowLabel.getWidth() + rowSizeAdjustment;
572       row.setMinimumHeight(minHeight);
573 
574       // associate the label to the row
575       // the ratio value of 0 means the label will be left-aligned (regarding
576       // the row) and rotated 90 degress counter clockwise
577       tgnr.configureRowLabel(rowLabel, row, true, 0);
578 
579       // row labels are normal node labels and have to be explicitly added
580       // to the realizer as usual
581       tgnr.addLabel(rowLabel);
582     }
583 
584     // ensure that the table has the correct (sufficiently large) size
585     tgnr.updateTableBounds();
586 
587 
588     final HierarchyManager hm = HierarchyManager.getInstance(graph);
589 
590 
591     // create a group node ..
592     final Node pool = hm.createGroupNode(graph);
593     // .. and assign it the previously created table realizer
594     graph.setRealizer(pool, tgnr);
595 
596 
597     // prototype realizer for child nodes
598     final GenericNodeRealizer prototype = new GenericNodeRealizer();
599     prototype.setConfiguration(CONFIGURATION_GRADIENT_RECT);
600     prototype.setSize(90, 60);
601     prototype.setCenter(
602             column0.calculateBounds().getCenterX(),
603             row0.calculateBounds().getCenterY());
604     prototype.setFillColor(Color.WHITE);
605     prototype.setFillColor2(PASTEL_BLUE);
606 
607     // create a couple of child nodes
608     final Node[] nodes = new Node[6];
609     for (int j = 0; j < nodes.length; ++j) {
610       final NodeRealizer nr = prototype.createCopy();
611       nr.setLabelText(Integer.toString(j + 1));
612       nodes[j] = graph.createNode(nr);
613 
614       // important: assign the node to the table group
615       hm.setParentNode(nodes[j], pool);
616 
617       // move the row into the table's second row
618       table.moveToRow(nodes[j], row1);
619     }
620     // move the first and last child to different rows
621     table.moveToRow(nodes[0], row0);
622     table.moveToRow(nodes[5], table.getRow(table.rowCount() - 1));
623 
624 
625     // change the shape of one of the child nodes
626     ((GenericNodeRealizer) graph.getRealizer(nodes[2]))
627             .setConfiguration(CONFIGURATION_GRADIENT_DIAMOND);
628 
629     // create a couple of edges
630     final Edge[] edges = {
631       graph.createEdge(nodes[0], nodes[1]),
632       graph.createEdge(nodes[1], nodes[2]),
633       graph.createEdge(nodes[2], nodes[3]),
634       graph.createEdge(nodes[2], nodes[4]),
635       graph.createEdge(nodes[4], nodes[5]),
636     };
637 //    for (int j = 0; j < edges.length; ++j) {
638 //      graph.getRealizer(edges[j]).setLabelText(Integer.toString(j));
639 //    }
640 
641 
642     // setup port constraints for diamond-shaped child nodes
643 
644     // create the corresponding port constraints ...
645     final EdgeMap srcPc = graph.createEdgeMap();
646     srcPc.set(edges[2], PortConstraint.create(PortConstraint.NORTH));
647     srcPc.set(edges[3], PortConstraint.create(PortConstraint.EAST));
648 
649     // ... and register the port constraints
650     graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, srcPc);
651 
652     try {
653       layout(graph, false);
654     } finally {
655       graph.removeDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY);
656       graph.disposeEdgeMap(srcPc);
657     }
658   }
659 
660   /**
661    * Creates a sample graph depicting a BPMN style diagram.
662    * @param graph   the graph to configure.
663    */
664   private void createBpmnStyleSample( final Graph2D graph ) {
665     graph.clear();
666 
667 
668     final TableGroupNodeRealizer tgnr = new TableGroupNodeRealizer();
669     tgnr.setConfiguration(CONFIGURATION_POOL_BPMN_STYLE);
670     tgnr.setAutoResize(true);
671     tgnr.setDefaultColumnInsets(new YInsets(0, 5, 0, 5));
672     tgnr.setDefaultRowInsets(new YInsets(0, 25, 0, 0));
673 
674     // set up the table
675     final Table table = tgnr.getTable();
676     table.setInsets(new YInsets(20, 25, 5, 10));
677     table.addColumn();
678     table.addColumn();
679     table.addRow();
680     table.addRow();
681 
682     final NodeLabel label = tgnr.getLabel();
683     label.setText("Pool");
684     label.setPosition(NodeLabel.LEFT);
685     label.setRotationAngle(270);
686 
687     final YInsets insets = table.getInsets();
688     final double columnSizeAdjustment = 10 + 2 * insets.right;
689     final double rowSizeAdjustment = 10;
690 
691     // configure the rows:
692     //  - assign each row a label
693     //  - set a suitable minimum size (height) to each row
694     int r = 1;
695     for (Iterator it = table.getRows().iterator(); it.hasNext(); ++r) {
696       final Row row = (Row) it.next();
697       final NodeLabel rowLabel = tgnr.createNodeLabel();
698       rowLabel.setText("Lane " + r);
699       final double minHeight = rowLabel.getWidth() + rowSizeAdjustment;
700       row.setHeight(minHeight);
701       row.setMinimumHeight(minHeight);
702 
703       // associate the label to the row
704       // the ratio value of 0 means the label will be left-aligned (regarding
705       // the row) and rotated 90 degress counter clockwise
706       tgnr.configureRowLabel(rowLabel, row, true, 0);
707 
708       // row labels are normal node labels and have to be explicitly added
709       // to the realizer as usual
710       tgnr.addLabel(rowLabel);
711     }
712 
713     // configure the columns:
714     //  - assign each row a label
715     //  - set a suitable minimum size (width) to each column
716     int c = 1;
717     for (Iterator it = table.getColumns().iterator(); it.hasNext(); ++c) {
718       final Column column = (Column) it.next();
719       final NodeLabel columnLabel = tgnr.createNodeLabel();
720       columnLabel.setText("Milestone " + c);
721       final double minWidth = columnLabel.getWidth() + columnSizeAdjustment;
722       column.setWidth(minWidth);
723       column.setMinimumWidth(minWidth);
724 
725       // associate the label to the column
726       tgnr.configureColumnLabel(columnLabel, column, false, 0);
727 
728       // column labels are normal node labels and have to be explicitly added
729       // to the realizer as usual
730       tgnr.addLabel(columnLabel);
731     }
732 
733     // ensure that the table has the correct (sufficiently large) size
734     tgnr.updateTableBounds();
735 
736 
737     final HierarchyManager hm = HierarchyManager.getInstance(graph);
738 
739 
740     // create a group node ..
741     final Node pool = hm.createGroupNode(graph);
742     // .. and assign it the previously created table realizer
743     graph.setRealizer(pool, tgnr);
744 
745 
746     // prototype realizer for rectangular child nodes
747     final GenericNodeRealizer roundRect = new GenericNodeRealizer();
748     roundRect.setConfiguration(CONFIGURATION_SIMPLE_ROUNDRECT);
749     roundRect.setSize(80, 60);
750     roundRect.setCenter(tgnr.getCenterX(), tgnr.getCenterY());
751     roundRect.setFillColor(Color.WHITE);
752     roundRect.setLineColor(new Color(3, 104, 154));
753 
754     // prototype realizer for circular child nodes
755     final GenericNodeRealizer ellipse = new GenericNodeRealizer();
756     ellipse.setConfiguration(CONFIGURATION_SIMPLE_ELLIPSE);
757     ellipse.setSize(30, 30);
758     ellipse.setCenter(tgnr.getCenterX(), tgnr.getCenterY());
759     ellipse.setFillColor(Color.WHITE);
760     ellipse.setLineColor(new Color(198, 194, 139));
761 
762     // prototype realizer for diamond-shaped child nodes
763     final GenericNodeRealizer diamond = new GenericNodeRealizer();
764     diamond.setConfiguration(CONFIGURATION_SIMPLE_DIAMOND);
765     diamond.setSize(30, 30);
766     diamond.setCenter(tgnr.getCenterX(), tgnr.getCenterY());
767     diamond.setFillColor(Color.WHITE);
768     diamond.setLineColor(new Color(166, 166, 29));
769 
770 
771     // create several child nodes using the previously created prototype
772     // realizers and distribute the nodes over the table
773     final Node[] nodes = new Node[15];
774     for (int i = 0; i < nodes.length; ++i) {
775       if (i < 5) {
776         nodes[i] = graph.createNode(ellipse.createCopy());
777       } else if (i < 11) {
778         nodes[i] = graph.createNode(roundRect.createCopy());
779       } else {
780         nodes[i] = graph.createNode(diamond.createCopy());
781       }
782 
783       // important: assign the node to the table group
784       hm.setParentNode(nodes[i], pool);
785 
786       // "assign" the node to a column
787       if (i == 4 || i == 9 || i == 10 || i == 14) {
788         table.moveToColumn(nodes[i], table.getColumn(2));
789       } else if ((0 < i && i < 4) || i == 7 || i == 8 || i == 13) {
790         table.moveToColumn(nodes[i], table.getColumn(1));
791       } else {
792         table.moveToColumn(nodes[i], table.getColumn(0));
793       }
794 
795       // "assign" the node to a row
796       if (i == 10) {
797         table.moveToRow(nodes[i], table.getRow(2));
798       } else if (i == 4 || i == 8 || i == 14) {
799         table.moveToRow(nodes[i], table.getRow(1));
800       } else {
801         table.moveToRow(nodes[i], table.getRow(0));
802       }
803     }
804 //    for (int i = 0; i < nodes.length; ++i) {
805 //      graph.getRealizer(nodes[i]).setLabelText(Integer.toString(i));
806 //    }
807 
808 
809     // create some edges
810     final Edge[] edges = {
811       graph.createEdge(nodes[0], nodes[5]),
812       graph.createEdge(nodes[2], nodes[7]),
813       graph.createEdge(nodes[3], nodes[8]),
814       graph.createEdge(nodes[5], nodes[6]),
815       graph.createEdge(nodes[6], nodes[11]),
816       graph.createEdge(nodes[7], nodes[13]),
817       graph.createEdge(nodes[8], nodes[14]),
818       graph.createEdge(nodes[9], nodes[4]),
819       graph.createEdge(nodes[10], nodes[4]),
820       graph.createEdge(nodes[11], nodes[1]),
821       graph.createEdge(nodes[11], nodes[12]),
822       graph.createEdge(nodes[12], nodes[2]),
823       graph.createEdge(nodes[12], nodes[3]),
824       graph.createEdge(nodes[13], nodes[1]),
825       graph.createEdge(nodes[13], nodes[12]),
826       graph.createEdge(nodes[14], nodes[9]),
827       graph.createEdge(nodes[14], nodes[10]),
828     };
829 //    for (int i = 0; i < edges.length; ++i) {
830 //      graph.getRealizer(edges[i]).setLabelText(Integer.toString(i));
831 //    }
832 
833     for (int i = 0; i < nodes.length; ++i) {
834       final Node node = nodes[i];
835       if (node.inDegree() == 0) {
836         graph.getRealizer(node).setLineColor(DARK_GREEN);
837       } else if (node.outDegree() == 0) {
838         graph.getRealizer(node).setLineColor(BLOOD_RED);
839       }
840     }
841 
842 
843     // setup port constraints to get BPMN-like edge routing for diamond-shaped
844     // and circular child nodes
845 
846     // create the corresponding port constraints ...
847     final EdgeMap srcPc = graph.createEdgeMap();
848     srcPc.set(edges[9], PortConstraint.create(PortConstraint.NORTH));
849     srcPc.set(edges[10], PortConstraint.create(PortConstraint.SOUTH));
850     srcPc.set(edges[12], PortConstraint.create(PortConstraint.SOUTH));
851     srcPc.set(edges[13], PortConstraint.create(PortConstraint.NORTH));
852     srcPc.set(edges[14], PortConstraint.create(PortConstraint.WEST));
853     srcPc.set(edges[15], PortConstraint.create(PortConstraint.NORTH));
854     srcPc.set(edges[16], PortConstraint.create(PortConstraint.SOUTH));
855     final EdgeMap tgtPc = graph.createEdgeMap();
856     tgtPc.set(edges[4], PortConstraint.create(PortConstraint.WEST));
857     tgtPc.set(edges[5], PortConstraint.create(PortConstraint.EAST));
858     tgtPc.set(edges[7], PortConstraint.create(PortConstraint.NORTH));
859     tgtPc.set(edges[8], PortConstraint.create(PortConstraint.SOUTH));
860     tgtPc.set(edges[13], PortConstraint.create(PortConstraint.SOUTH));
861     tgtPc.set(edges[14], PortConstraint.create(PortConstraint.NORTH));
862 
863     // ... and register the port constraints
864     graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, srcPc);
865     graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, tgtPc);
866 
867 
868     // add a few layering constraint for a more BPMN-like result
869     final LayerConstraintFactory lcf = new IncrementalHierarchicLayouter()
870             .createLayerConstraintFactory(graph);
871     lcf.addPlaceNodeInSameLayerConstraint(nodes[7], nodes[1]);
872     lcf.addPlaceNodeInSameLayerConstraint(nodes[7], nodes[13]);
873 
874     try {
875       layout(graph, false);
876     } finally {
877 
878       lcf.dispose();
879 
880       graph.removeDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY);
881       graph.removeDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY);
882       graph.disposeEdgeMap(tgtPc);
883       graph.disposeEdgeMap(srcPc);
884     }
885   }
886 
887   /**
888    * Performs a layout calculation for the specified graph using
889    * {@link y.layout.hierarchic.IncrementalHierarchicLayouter}.
890    * @param graph   the graph to be laid out.
891    * @param vertical   if <code>true</code> a top-to-bottom layout is calculated
892    * and if <code>false</code> a left-to-right layout is calculated.
893    */
894   void layout( final Graph2D graph, final boolean vertical ) {
895     // setup a suitable layout algorithm for a graph with table nodes
896     final IncrementalHierarchicLayouter ihl = new IncrementalHierarchicLayouter();
897     ihl.setOrthogonallyRouted(true);
898     ihl.setLayoutOrientation(
899             vertical
900             ? LayoutOrientation.TOP_TO_BOTTOM
901             : LayoutOrientation.LEFT_TO_RIGHT);
902 
903     final Graph2DLayoutExecutor layoutExecutor =
904             new Graph2DLayoutExecutor(Graph2DLayoutExecutor.BUFFERED);
905     layoutExecutor.setConfiguringTableNodeRealizers(true);
906     layoutExecutor.doLayout(graph, ihl);
907   }
908 
909   protected void createExamplesMenu(JMenuBar menuBar) {
910     final JMenu menu = new JMenu("Example Graphs");
911     menuBar.add(menu);
912 
913     menu.add(new AbstractAction("Gradient Rows") {
914       public void actionPerformed(ActionEvent e) {
915         createGradientSample(view.getGraph2D());
916         view.fitContent();
917       }
918     });
919 
920     menu.add(new AbstractAction("Alternating Columns") {
921       public void actionPerformed(ActionEvent e) {
922         createAlternatingSample(view.getGraph2D());
923         view.fitContent();
924       }
925     });
926 
927     menu.add(new AbstractAction("Generic") {
928       public void actionPerformed(ActionEvent e) {
929         createGenericSample(view.getGraph2D());
930         view.fitContent();
931       }
932     });
933 
934     menu.add(new AbstractAction("BPMN Style") {
935       public void actionPerformed(ActionEvent e) {
936         createBpmnStyleSample(view.getGraph2D());
937         view.fitContent();
938       }
939     });
940   }
941 
942   /**
943    * Overwritten.
944    * @return the application menu bar.
945    */
946   protected JMenuBar createMenuBar() {
947     final JMenu fileMenu = new JMenu("File");
948     fileMenu.add(new PrintAction());
949     fileMenu.addSeparator();
950     fileMenu.add(new ExitAction());
951 
952     final JMenuBar jmb = new JMenuBar();
953     jmb.add(fileMenu);
954     createExamplesMenu(jmb);
955     return jmb;
956   }
957 
958   /**
959    * Overwritten to prevent deletion of graph elements.
960    * @return <code>false</code>.
961    */
962   protected boolean isDeletionEnabled() {
963     return false;
964   }
965 
966   public static void main( String[] args ) {
967     EventQueue.invokeLater(new Runnable() {
968       public void run() {
969         Locale.setDefault(Locale.ENGLISH);
970         initLnF();
971         (new TableStyleDemo()).start();
972       }
973     });
974   }
975 
976   /**
977    * Creates and registers lots of configurations for the various (generic)
978    * node realizers used throughout the demo.
979    */
980   private static void initConfigurations() {
981     final GenericNodeRealizer.Factory factory = GenericNodeRealizer.getFactory();
982 
983 
984     // configurations used for table nodes
985     {
986       // configuration for the table node in the BPMN style sample
987       final Map bpmn = TableGroupNodeRealizer.createDefaultConfigurationMap();
988       configureSelectionMode(bpmn);
989       configureHotSpots(bpmn);
990       bpmn.put(GenericNodeRealizer.Painter.class, TableNodePainter.newBpmnInstance());
991 
992 
993       // configuration for the table node in the generic sample
994       final Map generic = TableGroupNodeRealizer.createDefaultConfigurationMap();
995       configureSelectionMode(generic);
996       configureHotSpots(generic);
997 
998 
999       // configuration for the table node in the alternating columns sample
1000      final Map alternating = TableGroupNodeRealizer.createDefaultConfigurationMap();
1001      configureSelectionMode(alternating);
1002      configureHotSpots(alternating);
1003      alternating.put(GenericNodeRealizer.Painter.class,
1004                    TableNodePainter.newAlternatingColumnsInstance());
1005
1006
1007      // configure the table painter for the gradient rows sample
1008      final TableNodePainter painter = TableNodePainter.newDefaultInstance();
1009      // disable column background rendering
1010      painter.setSubPainter(TableNodePainter.PAINTER_COLUMN_BACKGROUND, null);
1011      // register a custom row subordinate painter
1012      painter.setSubPainter(TableNodePainter.PAINTER_ROW_BACKGROUND, new GradientRowPainter());
1013
1014      // configuration for the table node in the gradient rows sample
1015      final Map gradient = TableGroupNodeRealizer.createDefaultConfigurationMap();
1016      configureSelectionMode(gradient);
1017      configureHotSpots(gradient);
1018      gradient.put(GenericNodeRealizer.Painter.class, painter);
1019
1020
1021      // register the configurations
1022      factory.addConfiguration(
1023              CONFIGURATION_POOL_GRADIENT, gradient);
1024      factory.addConfiguration(
1025              CONFIGURATION_POOL_ALTERNATING, alternating);
1026      factory.addConfiguration(
1027              CONFIGURATION_POOL_GENERIC, generic);
1028      factory.addConfiguration(
1029              CONFIGURATION_POOL_BPMN_STYLE, bpmn);
1030    }
1031
1032
1033    // configurations used for child nodes in the BPMN style sample
1034    {
1035      final ShapeNodePainter roundRect =
1036              new ShapeNodePainter(ShapeNodePainter.ROUND_RECT);
1037      final Map simpleRoundRect = factory.createDefaultConfigurationMap();
1038      simpleRoundRect.put(GenericNodeRealizer.Painter.class, roundRect);
1039      simpleRoundRect.put(GenericNodeRealizer.ContainsTest.class, roundRect);
1040
1041      final ShapeNodePainter diamond =
1042              new ShapeNodePainter(ShapeNodePainter.DIAMOND);
1043      final Map simpleDiamond = factory.createDefaultConfigurationMap();
1044      simpleDiamond.put(GenericNodeRealizer.Painter.class, diamond);
1045      simpleDiamond.put(GenericNodeRealizer.ContainsTest.class, diamond);
1046
1047      final ShapeNodePainter ellipse =
1048              new ShapeNodePainter(ShapeNodePainter.ELLIPSE);
1049      final Map simpleEllipse = factory.createDefaultConfigurationMap();
1050      simpleEllipse.put(GenericNodeRealizer.Painter.class, ellipse);
1051      simpleEllipse.put(GenericNodeRealizer.ContainsTest.class, ellipse);
1052
1053      factory.addConfiguration(CONFIGURATION_SIMPLE_ROUNDRECT, simpleRoundRect);
1054      factory.addConfiguration(CONFIGURATION_SIMPLE_DIAMOND, simpleDiamond);
1055      factory.addConfiguration(CONFIGURATION_SIMPLE_ELLIPSE, simpleEllipse);
1056    }
1057
1058
1059    // configurations used for all other child nodes
1060    {
1061      final Map gradientRect = factory.createDefaultConfigurationMap();
1062      configureSelectionMode(gradientRect);
1063      gradientRect.put(
1064            GenericNodeRealizer.Painter.class,
1065            new SimpleGradientNodePainter(ShapeNodePainter.RECT));
1066
1067      final Map gradientRoundRect = factory.createDefaultConfigurationMap();
1068      final SimpleGradientNodePainter roundRect =
1069              new SimpleGradientNodePainter(ShapeNodePainter.ROUND_RECT);
1070      gradientRoundRect.put(GenericNodeRealizer.Painter.class, roundRect);
1071      gradientRoundRect.put(GenericNodeRealizer.ContainsTest.class, roundRect);
1072
1073      final Map gradientDiamond = factory.createDefaultConfigurationMap();
1074      final SimpleGradientNodePainter diamond =
1075              new SimpleGradientNodePainter(ShapeNodePainter.DIAMOND);
1076      gradientDiamond.put(GenericNodeRealizer.Painter.class, diamond);
1077      gradientDiamond.put(GenericNodeRealizer.ContainsTest.class, diamond);
1078
1079      final Map gradientEllipse = factory.createDefaultConfigurationMap();
1080      final SimpleGradientNodePainter ellipse =
1081              new SimpleGradientNodePainter(ShapeNodePainter.ELLIPSE);
1082      gradientEllipse.put(GenericNodeRealizer.Painter.class, ellipse);
1083      gradientEllipse.put(GenericNodeRealizer.ContainsTest.class, ellipse);
1084
1085      factory.addConfiguration(CONFIGURATION_GRADIENT_RECT, gradientRect);
1086      factory.addConfiguration(CONFIGURATION_GRADIENT_ROUNDRECT, gradientRoundRect);
1087      factory.addConfiguration(CONFIGURATION_GRADIENT_DIAMOND, gradientDiamond);
1088      factory.addConfiguration(CONFIGURATION_GRADIENT_ELLIPSE, gradientEllipse);
1089    }
1090  }
1091
1092  /**
1093   * Configures the specified configuration map for default hot spot painting
1094   * and hit testing.
1095   * @param map   a configuration map.
1096   */
1097  private static void configureHotSpots( final Map map ) {
1098    // setting a null HotSpotPainter actually configures GenericNodeRealizer
1099    // to use the default hot spot painting
1100    map.put(GenericNodeRealizer.HotSpotPainter.class, null);
1101    // setting a null HotSpotHitTest actually configures GenericNodeRealizer
1102    // to use the default hot spot hit testing
1103    map.put(GenericNodeRealizer.HotSpotHitTest.class, null);
1104  }
1105
1106  /**
1107   * Configures the <code>TableSelectionMode</code> in the specified
1108   * configuration map to couple column/row selection state and realizer
1109   * selection state.
1110   * @param map   a configuration map.
1111   */
1112  private static void configureSelectionMode( final Map map ) {
1113    final Object miep =
1114            map.get(GenericNodeRealizer.GenericMouseInputEditorProvider.class);
1115    if (miep instanceof MultiplexingNodeEditor) {
1116      final MultiplexingNodeEditor editor = (MultiplexingNodeEditor) miep;
1117      for (Iterator it = editor.getNodeEditors().iterator(); it.hasNext();) {
1118        final Object mode = it.next();
1119        if (mode instanceof TableSelectionEditor) {
1120          ((TableSelectionEditor) mode).setSelectionPolicy(
1121                  TableSelectionEditor.RELATE_TO_NODE_SELECTION);
1122        }
1123      }
1124    }
1125  }
1126
1127  static boolean useGradientStyle( final Graphics2D graphics ) {
1128    return YRenderingHints.isGradientPaintingEnabled(graphics);
1129  }
1130
1131
1132  /**
1133   * {@link y.view.ShapeNodePainter} painter that uses a vertical gradient paint
1134   * (defined by the context realizer's fill color and fill color 2) to fill
1135   * node shapes and adds a very simple drop shadow.
1136   */
1137  private static final class SimpleGradientNodePainter extends ShapeNodePainter {
1138    SimpleGradientNodePainter( final byte type ) {
1139      super(type);
1140    }
1141
1142    protected void paintFilledShape(
1143            final NodeRealizer context,
1144            final Graphics2D graphics,
1145            final Shape shape
1146    ) {
1147      if (!context.isTransparent()) {
1148        final boolean useSelectionStyle = useSelectionStyle(context, graphics);
1149        final Paint paint =
1150                useGradientStyle(graphics)
1151                ? getFillPaint(context, useSelectionStyle)
1152                : getFillColor(context, useSelectionStyle);
1153        if (paint != null) {
1154          final AffineTransform oldTransform = graphics.getTransform();
1155          graphics.translate(3, 3);
1156          graphics.setColor(Color.GRAY);
1157          graphics.fill(shape);
1158          graphics.setTransform(oldTransform);
1159
1160          graphics.setPaint(paint);
1161          graphics.fill(shape);
1162        }
1163      }
1164    }
1165
1166    protected Paint getFillPaint( final NodeRealizer context, final boolean selected ) {
1167      Color fill1 = getFillColor(context, selected);
1168      if (fill1 != null) {
1169        Color fill2 = getFillColor2(context, selected);
1170        if (fill2 != null) {
1171          final float x = (float) context.getX();
1172          final double y = context.getY();
1173          return new GradientPaint(
1174                  x, (float) y, fill1,
1175                  x, (float) (y + context.getHeight()), fill2, true);
1176        } else {
1177          return fill1;
1178        }
1179      } else {
1180        return null;
1181      }
1182    }
1183
1184    private static boolean useSelectionStyle(
1185            final NodeRealizer context,
1186            final Graphics2D graphics
1187    ) {
1188      return context.isSelected() &&
1189             YRenderingHints.isSelectionPaintingEnabled(graphics);
1190    }
1191  }
1192
1193  /**
1194   * {@link y.view.GenericNodeRealizer.Painter} meant to be used as a row
1195   * background painter for {@link y.view.tabular.TableNodePainter}.
1196   * The row background is filled using gradient paints defined by custom
1197   * style properties.
1198   */
1199  private static final class GradientRowPainter extends AbstractCustomNodePainter {
1200    /**
1201     * Style property ID used to retrieve style properties of type
1202     * {@link demo.view.realizer.TableStyleDemo.GradientRowPainter.RowColorMap}
1203     * that are used to create appropriate gradient paints.
1204     */
1205    static final String STYLE_ROW_COLOR_MAP = "ROW_COLOR_MAP";
1206
1207
1208    final Rectangle2D.Double shape;
1209
1210    GradientRowPainter() {
1211      shape = new Rectangle2D.Double();
1212    }
1213
1214    /**
1215     * Overwritten to prevent hot spot and label painting.
1216     * @param dummy   the dummy realizer representing the bounds of the row
1217     * that is to be painted.
1218     * @param graphics   the graphics context for painting.
1219     */
1220    public void paint( final NodeRealizer dummy, final Graphics2D graphics ) {
1221      if (!dummy.isVisible()) {
1222        return;
1223      }
1224      backupGraphics(graphics);
1225      try {
1226        paintNode(dummy, graphics, false);
1227      } finally {
1228        restoreGraphics(graphics);
1229      }
1230    }
1231
1232    /**
1233     * Paints the row represented by the specified realizer.
1234     * @param dummy   the dummy realizer representing the bounds of the row
1235     * that is to be painted.
1236     * @param graphics   the graphics context for painting.
1237     * @param sloppy   ignored.
1238     */
1239    protected void paintNode(
1240            final NodeRealizer dummy,
1241            final Graphics2D graphics,
1242            final boolean sloppy
1243    ) {
1244      if (!dummy.isTransparent()) {
1245        final Paint paint =
1246                useGradientStyle(graphics)
1247                ? getFillPaint(dummy, dummy.isSelected())
1248                : getFillColor(dummy, dummy.isSelected());
1249        if (paint != null) {
1250          shape.setFrame(dummy.getX(), dummy.getY(), dummy.getWidth(), dummy.getHeight());
1251          graphics.setPaint(paint);
1252          graphics.fill(shape);
1253        }
1254
1255        final YInsets insets = getRow((dummy)).getInsets();
1256        if (insets != null && insets.left > 0) {
1257          final Color color = getFillColor(dummy, false);
1258          if (color != null) {
1259            shape.setFrame(dummy.getX(), dummy.getY(), insets.left, dummy.getHeight());
1260            graphics.setColor(color);
1261            graphics.fill(shape);
1262          }
1263        }
1264      }
1265    }
1266
1267    /**
1268     * Determines the fill paint for the row represented by the specified
1269     * realizer depending on the registered {@link #STYLE_ROW_COLOR_MAP} style
1270     * property.
1271     * @param dummy   the dummy realizer representing the bounds of the row
1272     * that is to be painted.
1273     * @param selected whether the node is currently selected
1274     * @return the background fill paint for the row represented by the
1275     * specified realizer.
1276     */
1277    protected Paint getFillPaint( final NodeRealizer dummy, final boolean selected ) {
1278      final GenericNodeRealizer gnr = (GenericNodeRealizer) dummy;
1279      final RowColorMap rcm = (RowColorMap) gnr.getStyleProperty(STYLE_ROW_COLOR_MAP);
1280      if (rcm != null) {
1281        final Row row = getRow(gnr);
1282        Color color = rcm.getColor(row);
1283        if (color == null) {
1284          color = new Color(0, 0, 0, 0);
1285        }
1286
1287        final double x = dummy.getX();
1288        final float y = (float) dummy.getY();
1289        return new GradientPaint(
1290                (float) x, y, color,
1291                (float) (x + dummy.getWidth()),  y, getFillColor2(dummy, false));
1292      } else {
1293        return getFillColor(dummy, selected);
1294      }
1295    }
1296
1297    /**
1298     * Returns the row represented by the specified realizer.
1299     * @param dummy   a {@link y.view.GenericNodeRealizer} representing
1300     * a row in a {@link y.view.tabular.TableGroupNodeRealizer}.
1301     * @return the row represented by the specified realizer.
1302     */
1303    private static Row getRow( final NodeRealizer dummy ) {
1304      return TableNodePainter.getRow(dummy);
1305    }
1306
1307
1308    private interface RowColorMap {
1309      public Color getColor( Row row );
1310    }
1311  }
1312
1313
1314  /**
1315   * <code>TableStyle</code> intended for rows that uses a realizer's fill color
1316   * 2 as fill color.
1317   */
1318  private static final class RowStyle implements TableStyle {
1319    private final boolean selected;
1320
1321    RowStyle( final boolean selected ) {
1322      this.selected = selected;
1323    }
1324
1325    public Stroke getBorderLineType( final NodeRealizer context ) {
1326      return null;
1327    }
1328
1329    public Color getBorderLineColor( final NodeRealizer context ) {
1330      return null;
1331    }
1332
1333    public Color getBorderFillColor( final NodeRealizer context ) {
1334      return null;
1335    }
1336
1337    public Stroke getLineType( final NodeRealizer context ) {
1338      if (selected) {
1339        final LineType lt = context.getLineType();
1340        return LineType.createLineType(
1341                (int)Math.ceil(lt.getLineWidth()) + 2,
1342                lt.getLineStyle());
1343      } else {
1344        return context.getLineType();
1345      }
1346    }
1347
1348    public Color getLineColor( final NodeRealizer context ) {
1349      return context.getLineColor();
1350    }
1351
1352    public Color getFillColor( final NodeRealizer context ) {
1353      return context.getFillColor2();
1354    }
1355  }
1356}
1357